fix parsing

This commit is contained in:
2025-10-09 17:40:54 -06:00
parent 4e981a69a8
commit 7cad512010
14 changed files with 520 additions and 214 deletions

View File

@@ -1,6 +1,7 @@
fn main(): i64 { fn main(): i64 {
let user = User{id: 4}; let user = User{id: 4};
return user.instance_method(); let result = user.instance_method() + User::instance_method(user);
return result;
} }
type User struct { type User struct {

View File

@@ -0,0 +1,38 @@
import net.http as http;
import logging as logging;
import json as json;
type ExampleResponse struct {
pub id: i32;
pub name: str;
pub email: str;
}
type Router struct {
logger: logging::Logger;
pub fn new(logger: logging::Logger): Router {
return Self{logger: logger};
}
pub fn get_user_data(self: Self, req: http.Request): IO[Result[http::Response, http::Error]] {
let response_data = ExampleResponse{
id: 4,
name: "Andrew",
email: "andrew@boringlang.com",
};
self.logger.info("getting user data")?;
return ok(http::Response::ok(json::dumps(response_data)?));
}
}
pub fn main(args: List[String], os: OS): IO[i32] {
let logger = logging::ConsoleLogger::new(os.console.stdout());
let router = Router::new(logger);
let app = http::Router::new("").add_route("/myroute", router.get_user_data);
let http_server = http::Server::new(os.net(), "localhost", 8080, app);
let err = http_server.serve_forever()?;
logger.info("error serving: ", err)?;
return 1;
}

View File

@@ -0,0 +1,33 @@
import uuid as uuid;
import orm as orm;
#[derive(DeepCopy, Debug, PartialEq, DeriveEntityModel)]
#[orm::model(table_name = "user")]
pub type User struct {
#[orm::model(primary_key)]
pub id: uuid::UUID;
#[orm::model(unique, column_type = "VARCHAR(256)")]
pub email: String;
#[orm::model(nullable)]
pub password_hash: Option[String];
}
#[derive(DeepCopy, Debug, PartialEq, DeriveEntityModel)]
#[orm::model(table_name = "todo")]
pub type Todo struct {
#[orm::model(primary_key)]
pub id: uuid::UUID;
#[orm::model(foreign_key = "user", on_delete="CASCADE")]
pub user_id: uuid::UUID;
#[orm::model(column_type = "VARCHAR(256)")]
pub title: String;
#[orm::model(column_type = "TEXT")]
pub description: String;
pub created_at: DateTime;
pub updated_at: DateTime;
}

View File

@@ -0,0 +1,45 @@
import logging as logging;
import orm as orm;
import uuid as uuid;
import webapp.models as models;
#[derive(DeepCopy, Debug, PartialEq)]
type TodoGetParams struct {
#[validate(format="short")]
pub id: uuid.UUID;
}
#[derive(DeepCopy, Debug, PartialEq, Serialize)]
type TodoResponse struct {
#[validate(format="short")]
pub id: uuid.UUID;
#[validate(format="short")]
pub user_id: uuid.UUID;
#[validate(min_length=1, max_length=256)]
pub title: String;
#[validate(min_length=1, max_length=1000000)]
pub description: String;
pub created_at: DateTime;
pub updated_at: DateTime;
}
impl Into<TodoResponse> for models.Todo {
fn into(self: Self): TodoResponse {
return TodoResponse{..self};
}
}
type Router struct {
logger: logging::Logger;
db: orm::DB;
pub fn new(logger: logging::Logger, db: orm::DB): Router {
return Self{logger: logger, db: db};
}
pub fn get_todo(self: Self, req: http.Request[TodoGetParams, http::NoQuery, http::NoBody]): IO[Result[http::JsonResponse[TodoResponse], http::Error]] {
let id = req.params.id;
let instance = models::Todo::select().filter(models::Todo::Id::equals(id)).first(self.db)??;
return http::JsonResponse<TodoResponse>::ok(instance.into());
}
}

View File

@@ -26,14 +26,24 @@ export const run = defineCommand({
if (match.succeeded()) { if (match.succeeded()) {
const adapter = semantics(match); const adapter = semantics(match);
const ast = adapter.toAST(); const ast = adapter.toAST();
// console.log(JSON.stringify(ast, null, 2));
new TraitChecker().withModule(ast); new TraitChecker().withModule(ast);
const aliasResolvedAst = new TypeAliasResolver().withModule(ast); const aliasResolvedAst = new TypeAliasResolver().withModule(ast);
const typeSystem = new TypeSystem(); const typeSystem = new TypeSystem();
const typeChecker = new TypeChecker(); const typeChecker = new TypeChecker();
const typeResolver = new TypeResolver(); const typeResolver = new TypeResolver();
typeChecker.withModule(aliasResolvedAst, typeSystem); typeChecker.withModule(aliasResolvedAst, typeSystem);
try {
typeSystem.solve(); typeSystem.solve();
} catch (e) {
console.log(e);
console.log(JSON.stringify(typeSystem.result, null, 2));
return;
}
const typeResolvedAst = typeResolver.withModule(aliasResolvedAst, typeSystem); const typeResolvedAst = typeResolver.withModule(aliasResolvedAst, typeSystem);
// console.log(JSON.stringify(typeResolvedAst, null, 2));
const interpreter = new TreeWalkInterpreter(); const interpreter = new TreeWalkInterpreter();
const result = interpreter.withModule(typeResolvedAst); const result = interpreter.withModule(typeResolvedAst);
console.log(JSON.stringify(result, null, 2)); console.log(JSON.stringify(result, null, 2));

View File

@@ -7,13 +7,14 @@ import {
LetStatement, LetStatement,
Module, Module,
Operation, Operation,
Path,
ReturnStatement, ReturnStatement,
StructGetter, StructGetter,
StructTypeDeclaration, StructTypeDeclaration,
TypeUsage, TypeUsage,
} from "../parse/ast"; } from "../parse/ast";
import { contextFromModule } from "./builtins"; import { contextFromModule } from "./builtins";
import { Context, Value } from "./context"; import { Context, NamedType, Value } from "./context";
interface ExpressionResultValue { interface ExpressionResultValue {
resultType: "Value"; resultType: "Value";
@@ -74,21 +75,21 @@ export class TreeWalkInterpreter {
if (result.resultType === "Return") { if (result.resultType === "Return") {
return result; return result;
} }
if (statement.source.expressionType == "VariableUsage") { if (statement.source.type == "Identifier") {
ctx.environment[statement.source.name.text] = { ctx.environment[statement.source.name.text] = {
namedEntity: "Variable", namedEntity: "Variable",
value: result.value, value: result.value,
}; };
} }
if (statement.source.expressionType == "StructGetter") { if (statement.source.type == "StructGetter") {
let source = this.withStructGetter(ctx, statement.source); let source = this.withStructGetter(ctx, statement.source.source);
if (source.resultType === "Return") { if (source.resultType === "Return") {
return source; return source;
} }
if (source.value.value !== "StructValue") { if (source.value.value !== "StructValue") {
throw Error("set attr on nonstruct, should never happen due to type system"); throw Error("set attr on nonstruct, should never happen due to type system");
} }
source.value.fields[statement.source.attribute.text] = result.value; source.value.fields[statement.source.source.attribute.text] = result.value;
} }
return { resultType: "Value", value: { value: "UnitValue" } }; return { resultType: "Value", value: { value: "UnitValue" } };
}; };
@@ -163,12 +164,13 @@ export class TreeWalkInterpreter {
if (expression.subExpression.expressionType === "FunctionCall") { if (expression.subExpression.expressionType === "FunctionCall") {
return this.withFunctionCall(ctx, expression.subExpression); return this.withFunctionCall(ctx, expression.subExpression);
} }
if (expression.subExpression.expressionType === "VariableUsage") { if (expression.subExpression.expressionType === "Path") {
const variableValue = ctx.environment[expression.subExpression.name.text]; // const variableValue = ctx.environment[expression.subExpression.name.text];
if (!variableValue || variableValue.namedEntity !== "Variable") { // if (!variableValue || variableValue.namedEntity !== "Variable") {
throw Error(`not found: ${expression.subExpression.name.text}`); // throw Error(`not found: ${expression.subExpression.name.text}`);
} // }
return { resultType: "Value", value: variableValue.value }; const value = this.withPath(ctx, expression.subExpression);
return { resultType: "Value", value: value };
} }
if (expression.subExpression.expressionType === "IfExpression") { if (expression.subExpression.expressionType === "IfExpression") {
const condition = this.withExpression(ctx, expression.subExpression.condition); const condition = this.withExpression(ctx, expression.subExpression.condition);
@@ -302,4 +304,42 @@ export class TreeWalkInterpreter {
value: { value: "NumericValue", number: left.value.number / right.value.number }, value: { value: "NumericValue", number: left.value.number / right.value.number },
}; };
}; };
withPath = (ctx: Context, path: Path): Value => {
if (path.value.type == "Identifier") {
const variableValue = ctx.environment[path.value.name.text];
if (!variableValue || variableValue.namedEntity !== "Variable") {
throw Error(`not found: ${path.value.name.text}`);
}
return variableValue.value;
}
if (path.value.type == "Nested") {
return this.withPathItem(ctx, path) as Value;
}
throw Error(`Impossible path`);
};
withPathItem = (ctx: Context, path: Path): NamedType | Value => {
if (path.value.type == "Identifier") {
const envValue = ctx.environment[path.value.name.text];
if (!envValue || envValue.namedEntity !== "NamedType") {
throw Error(`not found: ${path.value.name.text}`);
}
return envValue;
}
if (path.value.type == "Nested") {
const envValue = this.withPathItem(ctx, path.value.parent) as NamedType;
if (envValue.isA == "Trait") {
throw Error(`Cannot get function impl from raw trait`);
}
for (const impl of envValue.impls) {
for (const [name, method] of Object.entries(impl.functions)) {
if (name == path.value.name.text) {
return { value: "FunctionValue", partial: [], ref: method };
}
}
}
}
throw Error(`Impossible path`);
};
} }

View File

@@ -57,6 +57,13 @@ export interface StructGetter {
type: TypeUsage; type: TypeUsage;
} }
export interface Path {
expressionType: "Path";
value:
| { type: "Identifier"; name: Identifier }
| { type: "Nested"; parent: Path; name: Identifier };
}
export interface Operation { export interface Operation {
expressionType: "Operation"; expressionType: "Operation";
left: Expression; left: Expression;
@@ -65,12 +72,6 @@ export interface Operation {
type: TypeUsage; type: TypeUsage;
} }
export interface VariableUsage {
expressionType: "VariableUsage";
name: Identifier;
type: TypeUsage;
}
export interface IfExpression { export interface IfExpression {
expressionType: "IfExpression"; expressionType: "IfExpression";
condition: Expression; condition: Expression;
@@ -88,8 +89,8 @@ export interface Expression {
| LiteralString | LiteralString
| LiteralStruct | LiteralStruct
| FunctionCall | FunctionCall
| VariableUsage
| IfExpression | IfExpression
| Path
| StructGetter | StructGetter
| Block | Block
| Operation; | Operation;
@@ -110,7 +111,7 @@ export interface LetStatement {
export interface AssignmentStatement { export interface AssignmentStatement {
statementType: "AssignmentStatement"; statementType: "AssignmentStatement";
source: VariableUsage | StructGetter; source: { type: "Identifier"; name: Identifier } | { type: "StructGetter"; source: StructGetter };
expression: Expression; expression: Expression;
} }
@@ -139,7 +140,7 @@ export interface Function {
block: Block; block: Block;
} }
export const functionToType = (fn: FunctionDeclaration): TypeUsage => { export const functionToType = (fn: FunctionDeclaration): FunctionTypeUsage => {
return { return {
typeUsage: "FunctionTypeUsage", typeUsage: "FunctionTypeUsage",
arguments: fn.arguments.map((arg) => arg.type), arguments: fn.arguments.map((arg) => arg.type),

View File

@@ -4,8 +4,8 @@ export const boringGrammar = ohm.grammar(String.raw`
Boringlang { Boringlang {
ReturnStatement = "return" Expression ";" ReturnStatement = "return" Expression ";"
LetStatement = "let" identifier (":" TypeUsage)? "=" Expression ";" LetStatement = "let" identifier (":" TypeUsage)? "=" Expression ";"
AssignmentStatement = VariableUsage "=" Expression ";" AssignmentStatement = identifier "=" Expression ";" -- identifier
| StructGetter "=" Expression ";" | StructGetter "=" Expression ";" -- getter
ExpressionStatement = Expression ";" ExpressionStatement = Expression ";"
Statement = ExpressionStatement Statement = ExpressionStatement
| LetStatement | LetStatement
@@ -19,27 +19,33 @@ export const boringGrammar = ohm.grammar(String.raw`
LiteralStructField = identifier ":" Expression LiteralStructField = identifier ":" Expression
LiteralStruct = identifier "{" ListOf<LiteralStructField, ","> "}" LiteralStruct = identifier "{" ListOf<LiteralStructField, ","> "}"
identifier = (letter | "_")+(letter | digit | "_")* identifier = (letter | "_")+(letter | digit | "_")*
FunctionCall = Expression "(" ListOf<Expression, ","> ")"
StructGetter = Expression "." identifier StructGetter = Expression "." identifier
VariableUsage = identifier
IfExpression = "if" "(" Expression ")" Block ("else" Block)? IfExpression = "if" "(" Expression ")" Block ("else" Block)?
Term = LiteralInt Path = Path "::" identifier -- nested
| identifier -- base
PrimaryExpression = LiteralInt
| LiteralFloat | LiteralFloat
| LiteralBool | LiteralBool
| LiteralString | LiteralString
| LiteralStruct | Path -- path
| IfExpression
| Block
| "(" Expression ")" -- parens | "(" Expression ")" -- parens
| VariableUsage StructExpression = LiteralStruct
Factor = Factor "*" Term -- mult | Block
| Factor "/" Term -- div | IfExpression
| Term | PrimaryExpression
Expression = Expression "+" Factor -- plus MemberExpression = MemberExpression "." identifier -- structGetter
| Expression "-" Factor -- minus | StructExpression
| StructGetter CallExpression = CallExpression "." identifier -- structGetter
| FunctionCall | CallExpression "(" ListOf<Expression, ","> ")" -- functionCall
| Factor | MemberExpression "(" ListOf<Expression, ","> ")" -- memberFunctionCall
| MemberExpression
MultExpression = MultExpression "*" CallExpression -- mult
| MultExpression "/" CallExpression -- div
| CallExpression
AddExpression = Expression "+" MultExpression -- plus
| Expression "-" MultExpression -- minus
| MultExpression
Expression = AddExpression
Block = "{" Statement* Expression? "}" Block = "{" Statement* Expression? "}"
NamedTypeUsage = identifier NamedTypeUsage = identifier
TypeUsage = NamedTypeUsage TypeUsage = NamedTypeUsage

View File

@@ -22,6 +22,7 @@ import {
NamedTypeUsage, NamedTypeUsage,
newNever, newNever,
Operation, Operation,
Path,
ReturnStatement, ReturnStatement,
Statement, Statement,
StructField, StructField,
@@ -31,7 +32,6 @@ import {
TraitTypeDeclaration, TraitTypeDeclaration,
TypeDeclaration, TypeDeclaration,
TypeUsage, TypeUsage,
VariableUsage,
} from "./ast"; } from "./ast";
import { boringGrammar } from "./grammar"; import { boringGrammar } from "./grammar";
@@ -44,44 +44,60 @@ function nextUnknown() {
export const semantics = boringGrammar.createSemantics(); export const semantics = boringGrammar.createSemantics();
semantics.addOperation<any>("toAST", { semantics.addOperation<any>("toAST", {
LiteralInt(a): LiteralInt { LiteralInt(a): Expression {
return { return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralInt", expressionType: "LiteralInt",
value: this.sourceString, value: this.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { text: "i64", spanStart: 0, spanEnd: 0 }, name: { text: "i64", spanStart: 0, spanEnd: 0 },
}, },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
LiteralFloat(_1, _2, _3): LiteralFloat { LiteralFloat(_1, _2, _3): Expression {
return { return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralFloat", expressionType: "LiteralFloat",
value: this.sourceString, value: this.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { text: "f64", spanStart: 0, spanEnd: 0 }, name: { text: "f64", spanStart: 0, spanEnd: 0 },
}, },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
LiteralBool(_): LiteralBool { LiteralBool(_): Expression {
return { return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralBool", expressionType: "LiteralBool",
value: this.sourceString, value: this.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { text: "bool", spanStart: 0, spanEnd: 0 }, name: { text: "bool", spanStart: 0, spanEnd: 0 },
}, },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
LiteralString(_1, text, _3): LiteralString { LiteralString(_1, text, _3): Expression {
return { return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralString", expressionType: "LiteralString",
value: text.sourceString, value: text.sourceString,
type: { type: {
typeUsage: "NamedTypeUsage", typeUsage: "NamedTypeUsage",
name: { text: "String", spanStart: 0, spanEnd: 0 }, name: { text: "String", spanStart: 0, spanEnd: 0 },
}, },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
LiteralStructField(identifier, _2, expression): StructField { LiteralStructField(identifier, _2, expression): StructField {
@@ -90,12 +106,16 @@ semantics.addOperation<any>("toAST", {
expression: expression.toAST(), expression: expression.toAST(),
}; };
}, },
LiteralStruct(identifier, _2, fields, _4): LiteralStruct { LiteralStruct(identifier, _2, fields, _4): Expression {
return { return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralStruct", expressionType: "LiteralStruct",
name: identifier.toAST(), name: identifier.toAST(),
fields: fields.asIteration().children.map((c) => c.toAST()), fields: fields.asIteration().children.map((c) => c.toAST()),
type: { typeUsage: "NamedTypeUsage", name: identifier.toAST() }, type: { typeUsage: "NamedTypeUsage", name: identifier.toAST() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
identifier(_1, _2): Identifier { identifier(_1, _2): Identifier {
@@ -105,9 +125,57 @@ semantics.addOperation<any>("toAST", {
spanEnd: this.source.endIdx, spanEnd: this.source.endIdx,
}; };
}, },
FunctionCall(expression, _2, args, _4): FunctionCall { 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): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "StructGetter",
source: expression.toAST(),
attribute: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
CallExpression(expression): Expression {
return expression.toAST();
},
CallExpression_structGetter(expression, _2, identifier): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "StructGetter",
source: expression.toAST(),
attribute: identifier.toAST(),
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()); const resolvedArgs = args.asIteration().children.map((c) => c.toAST());
return { return {
statementType: "Expression",
subExpression: {
expressionType: "FunctionCall", expressionType: "FunctionCall",
source: expression.toAST(), source: expression.toAST(),
arguments: resolvedArgs, arguments: resolvedArgs,
@@ -115,85 +183,125 @@ semantics.addOperation<any>("toAST", {
typeUsage: "UnknownTypeUsage", typeUsage: "UnknownTypeUsage",
name: nextUnknown(), name: nextUnknown(),
}, },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
StructGetter(expression, _2, identifier): StructGetter { CallExpression_memberFunctionCall(expression, _2, args, _4): Expression {
const resolvedArgs = args.asIteration().children.map((c) => c.toAST());
return { 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", expressionType: "StructGetter",
source: expression.toAST(), source: expression.toAST(),
attribute: identifier.toAST(), attribute: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
}, },
VariableUsage(identifier): VariableUsage {
return {
expressionType: "VariableUsage",
name: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
IfExpression(_1, _2, expression, _4, block, _6, elseBlock): IfExpression { IfExpression(_1, _2, expression, _4, block, _6, elseBlock): Expression {
const eb = elseBlock.toAST(); const eb = elseBlock.toAST();
return { return {
statementType: "Expression",
subExpression: {
expressionType: "IfExpression", expressionType: "IfExpression",
condition: expression.toAST(), condition: expression.toAST(),
block: block.toAST(), block: block.toAST(),
else: eb.length > 0 ? eb[0] : null, else: eb.length > 0 ? eb[0] : null,
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
Term(term): Expression { Path_base(identifier): Path {
return term.toAST(); return {
expressionType: "Path",
value: { type: "Identifier", name: identifier.toAST() },
};
}, },
Term_parens(_1, term, _3): Expression { Path_nested(basePath, _2, attrIdent): Path {
return term.toAST(); return {
expressionType: "Path",
value: { type: "Nested", parent: basePath.toAST(), name: attrIdent.toAST() },
};
}, },
Factor(factor): Expression { MultExpression(expression): Expression {
return factor.toAST(); return expression.toAST();
}, },
Expression(expression): Expression { MultExpression_mult(factor, _2, term): Expression {
return { return {
statementType: "Expression", statementType: "Expression",
subExpression: expression.toAST(), subExpression: {
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Expression_plus(expression, _2, factor): Operation {
return {
expressionType: "Operation",
left: expression.toAST(),
op: "+",
right: factor.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Expression_minus(expression, _2, factor): Operation {
return {
expressionType: "Operation",
left: expression.toAST(),
op: "-",
right: factor.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Factor_mult(factor, _2, term): Operation {
return {
expressionType: "Operation", expressionType: "Operation",
left: factor.toAST(), left: factor.toAST(),
op: "*", op: "*",
right: term.toAST(), right: term.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
Factor_div(factor, _2, term): Operation { MultExpression_div(factor, _2, term): Expression {
return { return {
statementType: "Expression",
subExpression: {
expressionType: "Operation", expressionType: "Operation",
left: factor.toAST(), left: factor.toAST(),
op: "/", op: "/",
right: term.toAST(), right: term.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, 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 { Statement(statement): Statement {
return statement.toAST(); return statement.toAST();
}, },
@@ -212,10 +320,17 @@ semantics.addOperation<any>("toAST", {
type: tu.length > 0 ? tu[0] : { typeUsage: "UnknownTypeUsage", name: nextUnknown() }, type: tu.length > 0 ? tu[0] : { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
}; };
}, },
AssignmentStatement(variable, _2, expression, _4): AssignmentStatement { AssignmentStatement_identifier(variable, _2, expression, _4): AssignmentStatement {
return { return {
statementType: "AssignmentStatement", statementType: "AssignmentStatement",
source: variable.toAST(), 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(), expression: expression.toAST(),
}; };
}, },

View File

@@ -1,8 +1,8 @@
import { NamedTypeUsage, TypeUsage } from "../parse/ast"; import { FunctionTypeUsage, NamedTypeUsage, TypeUsage } from "../parse/ast";
interface EnvImpl { interface EnvImpl {
trait: string | null; trait: string | null;
functions: Record<string, TypeUsage>; functions: Record<string, FunctionTypeUsage>;
} }
interface NamedType { interface NamedType {
@@ -35,11 +35,20 @@ export function getAttr(ctx: Context, name: string, field: string) {
let results: TypeUsage[] = []; let results: TypeUsage[] = [];
for (const impl of struct.impls) { for (const impl of struct.impls) {
if (impl.functions[field]) { if (impl.functions[field]) {
results.push(impl.functions[field]); const fn = impl.functions[field];
if (
fn.arguments.length &&
fn.arguments[0].typeUsage == "NamedTypeUsage" &&
fn.arguments[0].name.text === name
) {
const fnCopy = deepCopy(fn);
fnCopy.arguments = fnCopy.arguments.slice(1);
results.push(fnCopy);
}
} }
} }
if (results.length === 0) { if (results.length === 0) {
console.log(JSON.stringify(struct, null, 2));
throw Error(`${name} has no attribue ${field}`); throw Error(`${name} has no attribue ${field}`);
} }
if (results.length > 1) { if (results.length > 1) {

View File

@@ -16,7 +16,7 @@ import {
StructTypeDeclaration, StructTypeDeclaration,
TraitTypeDeclaration, TraitTypeDeclaration,
TypeUsage, TypeUsage,
VariableUsage, Path,
} from "../parse/ast"; } from "../parse/ast";
import { deepCopy, replaceType } from "./context"; import { deepCopy, replaceType } from "./context";
@@ -121,11 +121,14 @@ export class TypeAliasResolver {
withAssignmentStatement = (ctx: AliasContext, statement: AssignmentStatement) => { withAssignmentStatement = (ctx: AliasContext, statement: AssignmentStatement) => {
const result = deepCopy(statement); const result = deepCopy(statement);
if (statement.source.expressionType == "StructGetter") { if (statement.source.type == "StructGetter") {
result.source = this.withStructGetter(ctx, statement.source); result.source = {
type: "StructGetter",
source: this.withStructGetter(ctx, statement.source.source),
};
} }
if (statement.source.expressionType == "VariableUsage") { if (statement.source.type == "Identifier") {
result.source = this.withVariableUsage(ctx, statement.source); result.source = deepCopy(result.source);
} }
result.expression = this.withExpression(ctx, statement.expression); result.expression = this.withExpression(ctx, statement.expression);
return result; return result;
@@ -151,8 +154,8 @@ export class TypeAliasResolver {
if (expression.subExpression.expressionType === "FunctionCall") { if (expression.subExpression.expressionType === "FunctionCall") {
result.subExpression = this.withFunctionCall(ctx, expression.subExpression); result.subExpression = this.withFunctionCall(ctx, expression.subExpression);
} }
if (expression.subExpression.expressionType === "VariableUsage") { if (expression.subExpression.expressionType === "Path") {
result.subExpression = this.withVariableUsage(ctx, expression.subExpression); result.subExpression = deepCopy(expression.subExpression);
} }
if (expression.subExpression.expressionType === "IfExpression") { if (expression.subExpression.expressionType === "IfExpression") {
result.subExpression = this.withIfExpression(ctx, expression.subExpression); result.subExpression = this.withIfExpression(ctx, expression.subExpression);
@@ -196,14 +199,6 @@ export class TypeAliasResolver {
return result; return result;
}; };
withVariableUsage = (ctx: AliasContext, variableUsage: VariableUsage) => {
const result = deepCopy(variableUsage);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
result.type = replaceType(oldName, newType, variableUsage.type);
}
return result;
};
withIfExpression = (ctx: AliasContext, ifExpression: IfExpression) => { withIfExpression = (ctx: AliasContext, ifExpression: IfExpression) => {
const result = deepCopy(ifExpression); const result = deepCopy(ifExpression);
result.condition = this.withExpression(ctx, ifExpression.condition); result.condition = this.withExpression(ctx, ifExpression.condition);

View File

@@ -19,7 +19,9 @@ import {
StructTypeDeclaration, StructTypeDeclaration,
TraitTypeDeclaration, TraitTypeDeclaration,
TypeUsage, TypeUsage,
VariableUsage, Path,
Identifier,
FunctionTypeUsage,
} from "../parse/ast"; } from "../parse/ast";
import { newContext } from "./builtins"; import { newContext } from "./builtins";
import { Context, deepCopy, typeExists } from "./context"; import { Context, deepCopy, typeExists } from "./context";
@@ -45,17 +47,9 @@ export class TypeChecker {
if (ctx.environment[item.name.text]) { if (ctx.environment[item.name.text]) {
throw Error("Duplicate name of trait"); throw Error("Duplicate name of trait");
} }
const functions: Record<string, TypeUsage> = {}; const functions: Record<string, FunctionTypeUsage> = {};
for (const fn of item.functions) { for (const fn of item.functions) {
if ( functions[fn.name.text] = functionToType(fn);
fn.arguments.length &&
fn.arguments[0].type.typeUsage == "NamedTypeUsage" &&
fn.arguments[0].type.name.text === item.name.text
) {
const fnCopy = deepCopy(fn);
fnCopy.arguments = fnCopy.arguments.slice(1);
functions[fn.name.text] = functionToType(fnCopy);
}
} }
ctx.environment[item.name.text] = { ctx.environment[item.name.text] = {
namedEntity: "NamedType", namedEntity: "NamedType",
@@ -86,17 +80,9 @@ export class TypeChecker {
if (!struct || struct.namedEntity !== "NamedType" || struct.isA !== "Struct") { if (!struct || struct.namedEntity !== "NamedType" || struct.isA !== "Struct") {
throw Error("Impl for non-struct"); throw Error("Impl for non-struct");
} }
const functions: Record<string, TypeUsage> = {}; const functions: Record<string, FunctionTypeUsage> = {};
for (const fn of item.functions) { for (const fn of item.functions) {
if ( functions[fn.declaration.name.text] = functionToType(fn.declaration);
fn.declaration.arguments.length &&
fn.declaration.arguments[0].type.typeUsage == "NamedTypeUsage" &&
fn.declaration.arguments[0].type.name.text === item.struct.name.text
) {
const fnCopy = deepCopy(fn.declaration);
fnCopy.arguments = fnCopy.arguments.slice(1);
functions[fn.declaration.name.text] = functionToType(fnCopy);
}
} }
struct.impls.push({ struct.impls.push({
trait: item.trait?.name.text ?? null, trait: item.trait?.name.text ?? null,
@@ -208,18 +194,25 @@ export class TypeChecker {
statement: AssignmentStatement, statement: AssignmentStatement,
typeSystem: TypeSystem, typeSystem: TypeSystem,
) => { ) => {
if (statement.source.expressionType == "StructGetter") { if (statement.source.type == "StructGetter") {
this.withStructGetter(ctx, statement.source, typeSystem); this.withStructGetter(ctx, statement.source.source, typeSystem);
}
if (statement.source.expressionType == "VariableUsage") {
this.withVariableUsage(ctx, statement.source, typeSystem);
}
this.withExpression(ctx, statement.expression, typeSystem);
typeSystem.compare({ typeSystem.compare({
left: statement.source.type, left: statement.source.source.type,
operation: { operation: "equals" }, operation: { operation: "equals" },
right: statement.expression.type, right: statement.expression.type,
}); });
}
if (statement.source.type == "Identifier") {
typeSystem.compare({
left: this.withPath(ctx, {
expressionType: "Path",
value: { type: "Identifier", name: statement.source.name },
}),
operation: { operation: "equals" },
right: statement.expression.type,
});
}
this.withExpression(ctx, statement.expression, typeSystem);
}; };
withLetStatement = (ctx: Context, statement: LetStatement, typeSystem: TypeSystem) => { withLetStatement = (ctx: Context, statement: LetStatement, typeSystem: TypeSystem) => {
@@ -291,12 +284,11 @@ export class TypeChecker {
right: expression.subExpression.type, right: expression.subExpression.type,
}); });
} }
if (expression.subExpression.expressionType === "VariableUsage") { if (expression.subExpression.expressionType === "Path") {
this.withVariableUsage(ctx, expression.subExpression, typeSystem);
typeSystem.compare({ typeSystem.compare({
left: expression.type, left: expression.type,
operation: { operation: "equals" }, operation: { operation: "equals" },
right: expression.subExpression.type, right: this.withPath(ctx, expression.subExpression),
}); });
} }
if (expression.subExpression.expressionType === "IfExpression") { if (expression.subExpression.expressionType === "IfExpression") {
@@ -377,16 +369,41 @@ export class TypeChecker {
} }
}; };
withVariableUsage = (ctx: Context, usage: VariableUsage, typeSystem: TypeSystem) => { withPath = (ctx: Context, path: Path): TypeUsage => {
const variable = ctx.environment[usage.name.text]; const pathList: Identifier[] = [];
if (!variable || variable.namedEntity === "NamedType") { while (path.value.type == "Nested") {
throw new Error(`${usage.name.text} not found.`); pathList.unshift(path.value.name);
path = path.value.parent;
} }
typeSystem.compare({ pathList.unshift(path.value.name);
left: variable.type,
operation: { operation: "equals" }, if (pathList.length === 0 || pathList.length > 2) {
right: usage.type, throw new Error(`Namespaces not yet supported`);
}); }
if (pathList.length === 1) {
const variable = ctx.environment[pathList[0].text];
if (!variable || variable.namedEntity === "NamedType") {
throw new Error(`${pathList[0].text} not found.`);
}
return variable.type;
}
let struct = ctx.environment[pathList[0].text];
if (!struct) {
throw new Error(`Unknown ${pathList[0].text}`);
}
if (struct.namedEntity == "Variable") {
throw new Error(`Cannot "::" variable ${pathList[0].text}`);
}
if (struct.fields[pathList[1].text]) {
return struct.fields[pathList[1].text];
}
for (const impl of struct.impls) {
if (impl.functions[pathList[1].text]) {
return impl.functions[pathList[1].text];
}
}
throw new Error(`Could not find attr ${pathList[1].text}`);
}; };
withIfExpression = (ctx: Context, ifExpression: IfExpression, typeSystem: TypeSystem) => { withIfExpression = (ctx: Context, ifExpression: IfExpression, typeSystem: TypeSystem) => {

View File

@@ -19,7 +19,7 @@ import {
StructTypeDeclaration, StructTypeDeclaration,
TraitTypeDeclaration, TraitTypeDeclaration,
TypeUsage, TypeUsage,
VariableUsage, Path,
} from "../parse/ast"; } from "../parse/ast";
import { newContext } from "./builtins"; import { newContext } from "./builtins";
import { Context, deepCopy, typeExists } from "./context"; import { Context, deepCopy, typeExists } from "./context";
@@ -109,11 +109,11 @@ export class TypeResolver {
withAssignmentStatement = (statement: AssignmentStatement, typeSystem: TypeSystem) => { withAssignmentStatement = (statement: AssignmentStatement, typeSystem: TypeSystem) => {
const result = deepCopy(statement); const result = deepCopy(statement);
if (statement.source.expressionType == "StructGetter") { if (statement.source.type == "StructGetter") {
result.source = this.withStructGetter(statement.source, typeSystem); result.source = {
} type: "StructGetter",
if (statement.source.expressionType == "VariableUsage") { source: this.withStructGetter(statement.source.source, typeSystem),
result.source = this.withVariableUsage(statement.source, typeSystem); };
} }
result.expression = this.withExpression(statement.expression, typeSystem); result.expression = this.withExpression(statement.expression, typeSystem);
return result; return result;
@@ -142,8 +142,8 @@ export class TypeResolver {
if (expression.subExpression.expressionType === "FunctionCall") { if (expression.subExpression.expressionType === "FunctionCall") {
result.subExpression = this.withFunctionCall(expression.subExpression, typeSystem); result.subExpression = this.withFunctionCall(expression.subExpression, typeSystem);
} }
if (expression.subExpression.expressionType === "VariableUsage") { if (expression.subExpression.expressionType === "Path") {
result.subExpression = this.withVariableUsage(expression.subExpression, typeSystem); // paths do not have types
} }
if (expression.subExpression.expressionType === "IfExpression") { if (expression.subExpression.expressionType === "IfExpression") {
result.subExpression = this.withIfExpression(expression.subExpression, typeSystem); result.subExpression = this.withIfExpression(expression.subExpression, typeSystem);
@@ -179,12 +179,6 @@ export class TypeResolver {
return result; return result;
}; };
withVariableUsage = (usage: VariableUsage, typeSystem: TypeSystem) => {
const result = deepCopy(usage);
result.type = typeSystem.resolveType(usage.type);
return result;
};
withIfExpression = (ifExpression: IfExpression, typeSystem: TypeSystem) => { withIfExpression = (ifExpression: IfExpression, typeSystem: TypeSystem) => {
const result = deepCopy(ifExpression); const result = deepCopy(ifExpression);
result.type = typeSystem.resolveType(ifExpression.type); result.type = typeSystem.resolveType(ifExpression.type);

View File

@@ -4,7 +4,9 @@ import { Context, deepCopy, getAttr } from "./context";
export const compareTypes = (typeA: TypeUsage, typeB: TypeUsage) => { export const compareTypes = (typeA: TypeUsage, typeB: TypeUsage) => {
if (typeA.typeUsage !== typeB.typeUsage) { if (typeA.typeUsage !== typeB.typeUsage) {
throw Error(`Mismatched types: ${typeA.typeUsage} ${typeB.typeUsage}`); throw Error(
`Mismatched types: ${JSON.stringify(typeA, null, 2)} ${JSON.stringify(typeB, null, 2)}`,
);
} }
if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") { if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") {
if (typeB.name.text === "Never") { if (typeB.name.text === "Never") {