From e7eb194b6ca69e8673f429da6a7fb91b6f61f287 Mon Sep 17 00:00:00 2001 From: Andrew Segavac Date: Fri, 10 Oct 2025 20:49:11 -0600 Subject: [PATCH 1/2] added type info to getaddr --- packages/boringlang/src/types/context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/boringlang/src/types/context.ts b/packages/boringlang/src/types/context.ts index 0fb2717..49817fa 100644 --- a/packages/boringlang/src/types/context.ts +++ b/packages/boringlang/src/types/context.ts @@ -24,7 +24,7 @@ export interface Context { environment: Record; } -export function getAttr(ctx: Context, name: string, field: string) { +export function getAttr(ctx: Context, name: string, field: string): TypeUsage { const struct = ctx.environment[name]; if (!struct || struct.namedEntity !== "NamedType") { throw Error(`Unknown type ${name}`); -- 2.52.0 From 8f6200f3938f71b537002a64d015526b92aee407 Mon Sep 17 00:00:00 2001 From: Andrew Segavac Date: Tue, 20 Jan 2026 17:47:36 -0700 Subject: [PATCH 2/2] added generics ast --- examples/generics.bl | 6 +- packages/boringlang/src/parse/ast.ts | 30 ++++++ packages/boringlang/src/parse/grammar.ts | 24 +++-- packages/boringlang/src/parse/semantics.ts | 107 ++++++++++++++++++--- 4 files changed, 142 insertions(+), 25 deletions(-) diff --git a/examples/generics.bl b/examples/generics.bl index 5402455..3d50d02 100644 --- a/examples/generics.bl +++ b/examples/generics.bl @@ -2,11 +2,11 @@ type MyTrait trait {} type Pair[K, V: MyTrait] struct { k: K, - v: V, + v: V } type Value struct { - value: i64, + value: i64 } impl MyTrait for Value {} @@ -21,7 +21,7 @@ impl [K, V: MyTrait] Pair[K, V] { fn main(): i64 { let a = Pair[i64, Value]{ k: 4, - v: Value{value: 6}, + v: Value{value: 6} }; return a.get_value[i64](999).value; } diff --git a/packages/boringlang/src/parse/ast.ts b/packages/boringlang/src/parse/ast.ts index ab9f7a9..c27a588 100644 --- a/packages/boringlang/src/parse/ast.ts +++ b/packages/boringlang/src/parse/ast.ts @@ -41,6 +41,7 @@ export interface LiteralStruct { name: Identifier; fields: StructField[]; type: TypeUsage; + typeParameters: GenericUsage; } export interface FunctionCall { @@ -55,10 +56,12 @@ export interface StructGetter { source: Expression; attribute: Identifier; type: TypeUsage; + typeParameters: GenericUsage; } export interface Path { expressionType: "Path"; + typeParameters: GenericUsage; value: | { type: "Identifier"; name: Identifier } | { type: "Nested"; parent: Path; name: Identifier }; @@ -130,6 +133,7 @@ export interface FunctionArgument { export interface FunctionDeclaration { name: Identifier; + generic: GenericDeclaration; arguments: FunctionArgument[]; returnType: TypeUsage; } @@ -157,6 +161,7 @@ export interface StructTypeDeclaration { moduleItem: "StructTypeDeclaration"; typeDeclaration: "StructTypeDeclaration"; name: Identifier; + generic: GenericDeclaration; fields: StructTypeField[]; } @@ -164,6 +169,7 @@ export interface TraitTypeDeclaration { moduleItem: "TraitTypeDeclaration"; typeDeclaration: "TraitTypeDeclaration"; name: Identifier; + generic: GenericDeclaration; functions: FunctionDeclaration[]; } @@ -171,6 +177,7 @@ export type TypeDeclaration = StructTypeDeclaration | TraitTypeDeclaration; export interface Impl { moduleItem: "Impl"; + generic: GenericDeclaration; struct: NamedTypeUsage; trait: NamedTypeUsage | null; functions: Function[]; @@ -182,8 +189,29 @@ export interface Module { items: ModuleItem[]; } +export interface GenericParameter { + name: Identifier; + bounds: Identifier[]; +} + +export interface GenericDeclaration { + parameters: GenericParameter[]; +} + +export interface KnownGenericUsage { + genericUsage: "Known"; + parameters: TypeUsage[]; +} + +export interface UnknownGenericUsage { + genericUsage: "Unknown"; +} + +export type GenericUsage = UnknownGenericUsage | KnownGenericUsage; + export interface NamedTypeUsage { typeUsage: "NamedTypeUsage"; + typeParameters: GenericUsage; name: Identifier; } @@ -203,6 +231,7 @@ export type TypeUsage = NamedTypeUsage | FunctionTypeUsage | UnknownTypeUsage; export const newVoid: () => TypeUsage = () => { return { typeUsage: "NamedTypeUsage", + typeParameters: { genericUsage: "Known", parameters: [] }, name: { text: "Void", spanStart: 0, spanEnd: 0 }, }; }; @@ -210,6 +239,7 @@ export const newVoid: () => TypeUsage = () => { export const newNever: () => TypeUsage = () => { return { typeUsage: "NamedTypeUsage", + typeParameters: { genericUsage: "Known", parameters: [] }, name: { text: "Never", spanStart: 0, spanEnd: 0 }, }; }; diff --git a/packages/boringlang/src/parse/grammar.ts b/packages/boringlang/src/parse/grammar.ts index 233c73b..3918b93 100644 --- a/packages/boringlang/src/parse/grammar.ts +++ b/packages/boringlang/src/parse/grammar.ts @@ -2,6 +2,10 @@ import * as ohm from "ohm-js"; export const boringGrammar = ohm.grammar(String.raw` Boringlang { + GenericUsage = "[" ListOf "]" + GenericParameter = identifier ":" ListOf -- conditions + | identifier + GenericDeclaration = "[" ListOf "]" ReturnStatement = "return" Expression ";" LetStatement = "let" identifier (":" TypeUsage)? "=" Expression ";" AssignmentStatement = identifier "=" Expression ";" -- identifier @@ -17,12 +21,12 @@ export const boringGrammar = ohm.grammar(String.raw` LiteralString = "\"" (~"\"" any)* "\"" | "'" (~"'" any)* "'" LiteralStructField = identifier ":" Expression - LiteralStruct = identifier "{" ListOf "}" + LiteralStruct = identifier (GenericUsage)? "{" ListOf "}" identifier = (letter | "_")+(letter | digit | "_")* StructGetter = Expression "." identifier IfExpression = "if" "(" Expression ")" Block ("else" Block)? - Path = Path "::" identifier -- nested - | identifier -- base + Path = Path "::" identifier (GenericUsage)? -- nested + | identifier (GenericUsage)? -- base PrimaryExpression = LiteralInt | LiteralFloat | LiteralBool @@ -33,9 +37,9 @@ export const boringGrammar = ohm.grammar(String.raw` | Block | IfExpression | PrimaryExpression - MemberExpression = MemberExpression "." identifier -- structGetter + MemberExpression = MemberExpression "." identifier (GenericUsage)? -- structGetter | StructExpression - CallExpression = CallExpression "." identifier -- structGetter + CallExpression = CallExpression "." identifier (GenericUsage)? -- structGetter | CallExpression "(" ListOf ")" -- functionCall | MemberExpression "(" ListOf ")" -- memberFunctionCall | MemberExpression @@ -47,19 +51,19 @@ export const boringGrammar = ohm.grammar(String.raw` | MultExpression Expression = AddExpression Block = "{" Statement* Expression? "}" - NamedTypeUsage = identifier + NamedTypeUsage = identifier (GenericUsage)? TypeUsage = NamedTypeUsage | "fn" "(" ListOf ")" ":" TypeUsage -- function_tu FunctionArgument = identifier ":" TypeUsage - FunctionDeclaration = "fn" identifier "(" ListOf ")" ":" TypeUsage + FunctionDeclaration = "fn" identifier (GenericDeclaration)? "(" ListOf ")" ":" TypeUsage Function = FunctionDeclaration Block StructTypeField = identifier ":" TypeUsage - StructTypeDeclaration = "type" identifier "struct" "{" ListOf "}" + StructTypeDeclaration = "type" identifier (GenericDeclaration)? "struct" "{" ListOf "}" TraitMethod = FunctionDeclaration ";" - TraitTypeDeclaration = "type" identifier "trait" "{" TraitMethod* "}" + TraitTypeDeclaration = "type" identifier (GenericDeclaration)? "trait" "{" TraitMethod* "}" TypeDeclaration = StructTypeDeclaration | TraitTypeDeclaration - Impl = "impl" (NamedTypeUsage "for")? NamedTypeUsage "{" Function* "}" + Impl = "impl" (GenericDeclaration)? (NamedTypeUsage "for")? NamedTypeUsage "{" Function* "}" ModuleItem = Function | TypeDeclaration | Impl diff --git a/packages/boringlang/src/parse/semantics.ts b/packages/boringlang/src/parse/semantics.ts index 1392565..b94db6e 100644 --- a/packages/boringlang/src/parse/semantics.ts +++ b/packages/boringlang/src/parse/semantics.ts @@ -8,6 +8,9 @@ import { FunctionCall, FunctionDeclaration, FunctionTypeUsage, + GenericDeclaration, + GenericParameter, + GenericUsage, Identifier, IfExpression, Impl, @@ -44,6 +47,29 @@ function nextUnknown() { export const semantics = boringGrammar.createSemantics(); semantics.addOperation("toAST", { + GenericUsage(_1, typeUsage, _3): GenericUsage { + return { + genericUsage: "Known", + parameters: typeUsage.asIteration().children.map((c) => c.toAST()), + }; + }, + GenericParameter_conditions(identifier, _2, typeUsage): GenericParameter { + return { + name: identifier.toAST(), + bounds: typeUsage.asIteration().children.map((c) => c.toAST()), + }; + }, + GenericParameter(identifier): GenericParameter { + return { + name: identifier.toAST(), + bounds: [], + }; + }, + GenericDeclaration(_1, parameters, _2): GenericDeclaration { + return { + parameters: parameters.asIteration().children.map((c) => c.toAST()), + }; + }, LiteralInt(a): Expression { return { statementType: "Expression", @@ -52,6 +78,7 @@ semantics.addOperation("toAST", { value: this.sourceString, type: { typeUsage: "NamedTypeUsage", + typeParameters: { genericUsage: "Known", parameters: [] }, name: { text: "i64", spanStart: 0, spanEnd: 0 }, }, }, @@ -66,6 +93,7 @@ semantics.addOperation("toAST", { value: this.sourceString, type: { typeUsage: "NamedTypeUsage", + typeParameters: { genericUsage: "Known", parameters: [] }, name: { text: "f64", spanStart: 0, spanEnd: 0 }, }, }, @@ -80,6 +108,7 @@ semantics.addOperation("toAST", { value: this.sourceString, type: { typeUsage: "NamedTypeUsage", + typeParameters: { genericUsage: "Known", parameters: [] }, name: { text: "bool", spanStart: 0, spanEnd: 0 }, }, }, @@ -94,6 +123,7 @@ semantics.addOperation("toAST", { value: text.sourceString, type: { typeUsage: "NamedTypeUsage", + typeParameters: { genericUsage: "Known", parameters: [] }, name: { text: "String", spanStart: 0, spanEnd: 0 }, }, }, @@ -106,16 +136,25 @@ semantics.addOperation("toAST", { expression: expression.toAST(), }; }, - LiteralStruct(identifier, _2, fields, _4): Expression { + LiteralStruct(identifier, genericUsage, _2, fields, _4): Expression { + const gu = genericUsage.toAST(); return { statementType: "Expression", subExpression: { expressionType: "LiteralStruct", name: identifier.toAST(), + typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" }, fields: fields.asIteration().children.map((c) => c.toAST()), - type: { typeUsage: "NamedTypeUsage", name: identifier.toAST() }, + type: { + typeUsage: "NamedTypeUsage", + typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" }, + name: identifier.toAST(), + }, + }, + type: { + typeUsage: "UnknownTypeUsage", + name: nextUnknown(), }, - type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, }; }, identifier(_1, _2): Identifier { @@ -144,13 +183,15 @@ semantics.addOperation("toAST", { MemberExpression(expression): Expression { return expression.toAST(); }, - MemberExpression_structGetter(expression, _2, identifier): Expression { + MemberExpression_structGetter(expression, _2, identifier, genericUsage): Expression { + const gu = genericUsage.toAST(); return { statementType: "Expression", subExpression: { expressionType: "StructGetter", source: expression.toAST(), attribute: identifier.toAST(), + typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, @@ -159,13 +200,15 @@ semantics.addOperation("toAST", { CallExpression(expression): Expression { return expression.toAST(); }, - CallExpression_structGetter(expression, _2, identifier): Expression { + CallExpression_structGetter(expression, _2, identifier, genericUsage): Expression { + const gu = genericUsage.toAST(); return { statementType: "Expression", subExpression: { expressionType: "StructGetter", source: expression.toAST(), attribute: identifier.toAST(), + typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, @@ -210,6 +253,7 @@ semantics.addOperation("toAST", { expressionType: "StructGetter", source: expression.toAST(), attribute: identifier.toAST(), + typeParameters: { genericUsage: "Unknown" }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, @@ -229,15 +273,19 @@ semantics.addOperation("toAST", { type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, }; }, - Path_base(identifier): Path { + Path_base(identifier, genericUsage): Path { + const gu = genericUsage.toAST(); return { expressionType: "Path", + typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" }, value: { type: "Identifier", name: identifier.toAST() }, }; }, - Path_nested(basePath, _2, attrIdent): Path { + Path_nested(basePath, _2, attrIdent, genericUsage): Path { + const gu = genericUsage.toAST(); return { expressionType: "Path", + typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" }, value: { type: "Nested", parent: basePath.toAST(), name: attrIdent.toAST() }, }; }, @@ -357,9 +405,11 @@ semantics.addOperation("toAST", { } return block; }, - NamedTypeUsage(name): NamedTypeUsage { + NamedTypeUsage(name, genericUsage): NamedTypeUsage { + const gu = genericUsage.toAST(); return { typeUsage: "NamedTypeUsage", + typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" }, name: name.toAST(), }; }, @@ -379,9 +429,20 @@ semantics.addOperation("toAST", { type: typeUsage.toAST(), }; }, - FunctionDeclaration(_1, identifier, _3, args, _4, _5, returnType): FunctionDeclaration { + FunctionDeclaration( + _1, + identifier, + genericDeclaration, + _4, + args, + _5, + _6, + returnType, + ): FunctionDeclaration { + const gd = genericDeclaration.toAST(); return { name: identifier.toAST(), + generic: gd.length ? gd[0] : { parameters: [] }, arguments: args.asIteration().children.map((c) => c.toAST()), returnType: returnType.toAST(), }; @@ -399,32 +460,54 @@ semantics.addOperation("toAST", { type: typeUsage.toAST(), }; }, - StructTypeDeclaration(_1, identifier, _3, _4, fields, _6): StructTypeDeclaration { + StructTypeDeclaration( + _1, + identifier, + genericDeclaration, + _4, + _5, + fields, + _7, + ): StructTypeDeclaration { + const gd = genericDeclaration.toAST(); return { moduleItem: "StructTypeDeclaration", typeDeclaration: "StructTypeDeclaration", name: identifier.toAST(), + generic: gd.length ? gd[0] : { parameters: [] }, fields: fields.asIteration().children.map((c) => c.toAST()), }; }, TraitMethod(declaration, _2): FunctionDeclaration { return declaration.toAST(); }, - TraitTypeDeclaration(_1, identifier, _3, _4, methods, _5): TraitTypeDeclaration { + TraitTypeDeclaration( + _1, + identifier, + genericDeclaration, + _4, + _5, + methods, + _7, + ): TraitTypeDeclaration { + const gd = genericDeclaration.toAST(); return { moduleItem: "TraitTypeDeclaration", typeDeclaration: "TraitTypeDeclaration", name: identifier.toAST(), + generic: gd.length ? gd[0] : { parameters: [] }, functions: methods.asIteration().children.map((c) => c.toAST()), }; }, TypeDeclaration(declaration): TypeDeclaration { return declaration.toAST(); }, - Impl(_1, trait, _3, struct, _4, methods, _5): Impl { + Impl(_1, genericDeclaration, trait, _4, struct, _6, methods, _8): Impl { const tr = trait.toAST(); + const gd = genericDeclaration.toAST(); return { moduleItem: "Impl", + generic: gd.length ? gd[0] : { parameters: [] }, struct: struct.toAST(), trait: tr.length > 0 ? tr[0] : null, functions: methods.asIteration().children.map((c) => c.toAST()), -- 2.52.0