Files
boring-lang/packages/boringlang/src/parse/semantics.ts
2026-01-20 17:47:36 -07:00

528 lines
15 KiB
TypeScript

import {
AssignmentStatement,
Block,
containsReturn,
Expression,
Function,
FunctionArgument,
FunctionCall,
FunctionDeclaration,
FunctionTypeUsage,
GenericDeclaration,
GenericParameter,
GenericUsage,
Identifier,
IfExpression,
Impl,
LetStatement,
LiteralBool,
LiteralFloat,
LiteralInt,
LiteralString,
LiteralStruct,
Module,
ModuleItem,
NamedTypeUsage,
newNever,
Operation,
Path,
ReturnStatement,
Statement,
StructField,
StructGetter,
StructTypeDeclaration,
StructTypeField,
TraitTypeDeclaration,
TypeDeclaration,
TypeUsage,
} from "./ast";
import { boringGrammar } from "./grammar";
let unknownTypeCounter = 0;
function nextUnknown() {
let name = "S" + unknownTypeCounter.toString();
unknownTypeCounter += 1;
return name;
}
export const semantics = boringGrammar.createSemantics();
semantics.addOperation<any>("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",
subExpression: {
expressionType: "LiteralInt",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
typeParameters: { genericUsage: "Known", parameters: [] },
name: { text: "i64", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralFloat(_1, _2, _3): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralFloat",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
typeParameters: { genericUsage: "Known", parameters: [] },
name: { text: "f64", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralBool(_): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralBool",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
typeParameters: { genericUsage: "Known", parameters: [] },
name: { text: "bool", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralString(_1, text, _3): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralString",
value: text.sourceString,
type: {
typeUsage: "NamedTypeUsage",
typeParameters: { genericUsage: "Known", parameters: [] },
name: { text: "String", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralStructField(identifier, _2, expression): StructField {
return {
name: identifier.toAST(),
expression: expression.toAST(),
};
},
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",
typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" },
name: identifier.toAST(),
},
},
type: {
typeUsage: "UnknownTypeUsage",
name: nextUnknown(),
},
};
},
identifier(_1, _2): Identifier {
return {
text: this.sourceString,
spanStart: this.source.startIdx,
spanEnd: this.source.endIdx,
};
},
PrimaryExpression(literal): Expression {
return literal.toAST();
},
PrimaryExpression_path(path): Expression {
return {
statementType: "Expression",
subExpression: path.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
PrimaryExpression_parens(_1, term, _3): Expression {
return term.toAST();
},
StructExpression(expression): Expression {
return expression.toAST();
},
MemberExpression(expression): Expression {
return expression.toAST();
},
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() },
};
},
CallExpression(expression): Expression {
return expression.toAST();
},
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() },
};
},
CallExpression_functionCall(expression, _2, args, _4): Expression {
const resolvedArgs = args.asIteration().children.map((c) => c.toAST());
return {
statementType: "Expression",
subExpression: {
expressionType: "FunctionCall",
source: expression.toAST(),
arguments: resolvedArgs,
type: {
typeUsage: "UnknownTypeUsage",
name: nextUnknown(),
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
CallExpression_memberFunctionCall(expression, _2, args, _4): Expression {
const resolvedArgs = args.asIteration().children.map((c) => c.toAST());
return {
statementType: "Expression",
subExpression: {
expressionType: "FunctionCall",
source: expression.toAST(),
arguments: resolvedArgs,
type: {
typeUsage: "UnknownTypeUsage",
name: nextUnknown(),
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
StructGetter(expression, _2, identifier): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "StructGetter",
source: expression.toAST(),
attribute: identifier.toAST(),
typeParameters: { genericUsage: "Unknown" },
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
IfExpression(_1, _2, expression, _4, block, _6, elseBlock): Expression {
const eb = elseBlock.toAST();
return {
statementType: "Expression",
subExpression: {
expressionType: "IfExpression",
condition: expression.toAST(),
block: block.toAST(),
else: eb.length > 0 ? eb[0] : null,
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
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, 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() },
};
},
MultExpression(expression): Expression {
return expression.toAST();
},
MultExpression_mult(factor, _2, term): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: factor.toAST(),
op: "*",
right: term.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
MultExpression_div(factor, _2, term): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: factor.toAST(),
op: "/",
right: term.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
AddExpression(expression): Expression {
return expression.toAST();
},
AddExpression_plus(expression, _2, factor): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: expression.toAST(),
op: "+",
right: factor.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
AddExpression_minus(expression, _2, factor): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: expression.toAST(),
op: "-",
right: factor.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Expression(expression): Expression {
return expression.toAST();
},
Statement(statement): Statement {
return statement.toAST();
},
ReturnStatement(_1, expression, _3): ReturnStatement {
return {
statementType: "ReturnStatement",
source: expression.toAST(),
};
},
LetStatement(_1, ident, _3, typeUsage, _5, expression, _7): LetStatement {
const tu = typeUsage.toAST();
return {
statementType: "LetStatement",
variableName: ident.toAST(),
expression: expression.toAST(),
type: tu.length > 0 ? tu[0] : { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
AssignmentStatement_identifier(variable, _2, expression, _4): AssignmentStatement {
return {
statementType: "AssignmentStatement",
source: { type: "Identifier", name: variable.toAST() },
expression: expression.toAST(),
};
},
AssignmentStatement_getter(variable, _2, expression, _4): AssignmentStatement {
return {
statementType: "AssignmentStatement",
source: { type: "StructGetter", source: variable.toAST() },
expression: expression.toAST(),
};
},
ExpressionStatement(expression, _2): Expression {
return {
statementType: "Expression",
subExpression: expression.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Block(_1, statements, expression, _4): Block {
const lines = statements.asIteration().children.map((c) => c.toAST());
const finalExpression = expression.toAST();
if (finalExpression.length > 0) {
lines.push(finalExpression[0]);
}
const block: Block = {
expressionType: "Block",
statements: lines,
type: newNever(),
};
if (!containsReturn(block)) {
block.type = { typeUsage: "UnknownTypeUsage", name: nextUnknown() };
}
return block;
},
NamedTypeUsage(name, genericUsage): NamedTypeUsage {
const gu = genericUsage.toAST();
return {
typeUsage: "NamedTypeUsage",
typeParameters: gu.length ? gu[0] : { genericUsage: "Unknown" },
name: name.toAST(),
};
},
TypeUsage_function_tu(_1, _2, args, _4, _5, returnType): FunctionTypeUsage {
return {
typeUsage: "FunctionTypeUsage",
arguments: args.asIteration().children.map((c) => c.toAST()),
returnType: returnType.toAST(),
};
},
TypeUsage(typeUsage): TypeUsage {
return typeUsage.toAST();
},
FunctionArgument(identifier, _2, typeUsage): FunctionArgument {
return {
name: identifier.toAST(),
type: typeUsage.toAST(),
};
},
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(),
};
},
Function(declaration, block): Function {
return {
moduleItem: "Function",
declaration: declaration.toAST(),
block: block.toAST(),
};
},
StructTypeField(identifier, _2, typeUsage): StructTypeField {
return {
name: identifier.toAST(),
type: typeUsage.toAST(),
};
},
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,
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, 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()),
};
},
ModuleItem(item): ModuleItem {
return item.toAST();
},
Module(items): Module {
return {
items: items.asIteration().children.map((c) => c.toAST()),
};
},
_iter(...children) {
return children.map((c) => c.toAST());
},
});