add type analysis for consts
- add the basics of recursive type resolving for expressions - set err context for enum
This commit is contained in:
parent
1040eef79f
commit
cb8908dc80
6 changed files with 142 additions and 12 deletions
|
@ -1,5 +1,9 @@
|
|||
// import std;
|
||||
|
||||
const (
|
||||
AWOO = 1 + 2
|
||||
)
|
||||
|
||||
enum B {
|
||||
a
|
||||
b
|
||||
|
|
|
@ -73,10 +73,11 @@ pub const BinaryExpr = struct {
|
|||
|
||||
pub const UnaryOperator = enum {
|
||||
Not,
|
||||
Negate,
|
||||
};
|
||||
|
||||
pub const UnaryExpr = struct {
|
||||
op: Token,
|
||||
op: UnaryOperator,
|
||||
right: *Expr,
|
||||
};
|
||||
|
||||
|
|
|
@ -191,8 +191,28 @@ fn printBinOp(inner: var) void {
|
|||
std.debug.warn(")");
|
||||
}
|
||||
|
||||
fn printSingleOp(tok: []const u8, applied: *const Expr) void {
|
||||
std.debug.warn("({}", tok);
|
||||
const unary_operator_tokens = [_][]const u8{
|
||||
"!", "-",
|
||||
};
|
||||
|
||||
const unary_operators = [_]UnaryOperator{
|
||||
.Not, .Negate,
|
||||
};
|
||||
|
||||
fn unOpToStr(op: UnaryOperator) ?[]const u8 {
|
||||
inline for (unary_operators) |val, idx| {
|
||||
if (val == op) return unary_operator_tokens[idx];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn printSingleOp(op: UnaryOperator, applied: *const Expr) void {
|
||||
printSimpleOp(unOpToStr(op), applied);
|
||||
}
|
||||
|
||||
fn printSimpleOp(op: ?[]const u8, applied: *const Expr) void {
|
||||
std.debug.warn("({}", op);
|
||||
printExpr(applied);
|
||||
std.debug.warn(")");
|
||||
}
|
||||
|
@ -201,8 +221,8 @@ pub fn printExpr(expr: *const Expr) void {
|
|||
switch (expr.*) {
|
||||
.Binary => |binary| printBinOp(binary),
|
||||
|
||||
.Unary => |unary| printSingleOp(unary.op.lexeme, unary.right),
|
||||
.Grouping => |expr_ptr| printSingleOp("group", expr_ptr),
|
||||
.Unary => |unary| printSingleOp(unary.op, unary.right),
|
||||
.Grouping => |expr_ptr| printSimpleOp("group", expr_ptr),
|
||||
|
||||
.Literal => |literal| {
|
||||
switch (literal) {
|
||||
|
@ -282,7 +302,7 @@ pub fn printExpr(expr: *const Expr) void {
|
|||
|
||||
pub fn printStmt(ident: usize, stmt: *const Stmt) void {
|
||||
switch (stmt.*) {
|
||||
.Println => |expr| printSingleOp("println", expr),
|
||||
.Println => |expr| printSimpleOp("println", expr),
|
||||
.Expr => |expr| printExpr(expr),
|
||||
|
||||
.If => |ifstmt| {
|
||||
|
@ -405,6 +425,10 @@ pub fn printContext(ctx: CompilationContext) void {
|
|||
}
|
||||
},
|
||||
|
||||
.Const => |typ| {
|
||||
std.debug.warn("const '{}', typ={}\n", kv.key, prettyType(typ));
|
||||
},
|
||||
|
||||
else => {
|
||||
std.debug.warn("TODO handle print of {}\n", kv.value);
|
||||
unreachable;
|
||||
|
|
|
@ -68,6 +68,7 @@ pub const SymbolType = enum {
|
|||
Struct,
|
||||
Enum,
|
||||
Variable,
|
||||
Const,
|
||||
};
|
||||
|
||||
pub const SymbolData = union(SymbolType) {
|
||||
|
@ -78,6 +79,7 @@ pub const SymbolData = union(SymbolType) {
|
|||
// variables (parameters and variables), for the type system
|
||||
// only have types
|
||||
Variable: SymbolUnderlyingType,
|
||||
Const: SymbolUnderlyingType,
|
||||
};
|
||||
|
||||
const builtin_type_identifiers = [_][]const u8{ "i32", "i64", "bool" };
|
||||
|
@ -175,4 +177,8 @@ pub const CompilationContext = struct {
|
|||
.Enum = ident_map,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn insertConst(self: *@This(), constdecl: ast.SingleConst, typ: SymbolUnderlyingType) !void {
|
||||
_ = try self.symbol_table.put(constdecl.name.lexeme, SymbolData{ .Const = typ });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -61,6 +61,24 @@ fn toBinaryOperator(op: Token) !ast.BinaryOperator {
|
|||
return ParseError.UnknownOperator;
|
||||
}
|
||||
|
||||
const unary_operator_tokens = [_][]const u8{
|
||||
"!", "-",
|
||||
};
|
||||
|
||||
const unary_operators = [_]ast.UnaryOperator{
|
||||
.Not, .Negate,
|
||||
};
|
||||
|
||||
fn toUnaryOperator(op: Token) !ast.UnaryOperator {
|
||||
const lex = op.lexeme;
|
||||
|
||||
inline for (unary_operator_tokens) |op_str, idx| {
|
||||
if (std.mem.eql(u8, lex, op_str)) return unary_operators[idx];
|
||||
}
|
||||
|
||||
return ParseError.UnknownOperator;
|
||||
}
|
||||
|
||||
pub const Parser = struct {
|
||||
allocator: *Allocator,
|
||||
scanner: *Scanner,
|
||||
|
@ -241,9 +259,7 @@ pub const Parser = struct {
|
|||
return grouping;
|
||||
}
|
||||
|
||||
fn mkUnary(self: *Parser, op: Token, right: *Expr) !*Expr {
|
||||
std.debug.warn("Unary\n");
|
||||
|
||||
fn mkUnary(self: *Parser, op: ast.UnaryOperator, right: *Expr) !*Expr {
|
||||
var expr = try self.allocator.create(Expr);
|
||||
expr.* = Expr{
|
||||
.Unary = ast.UnaryExpr{
|
||||
|
@ -1015,7 +1031,7 @@ pub const Parser = struct {
|
|||
var op = self.previous();
|
||||
var right = try self.parseUnary();
|
||||
|
||||
return try self.mkUnary(op, right);
|
||||
return try self.mkUnary(try toUnaryOperator(op), right);
|
||||
}
|
||||
|
||||
var expr = try self.parseCall();
|
||||
|
|
|
@ -88,11 +88,80 @@ pub const TypeSolver = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolveExprType(
|
||||
self: *@This(),
|
||||
ctx: *comp.CompilationContext,
|
||||
expr: *const ast.Expr,
|
||||
) anyerror!SymbolUnderlyingType {
|
||||
switch (expr.*) {
|
||||
.Binary => |binary| {
|
||||
var left_type = self.resolveExprType(ctx, binary.left);
|
||||
var right_type = self.resolveExprType(ctx, binary.right);
|
||||
|
||||
return switch (binary.op) {
|
||||
// all numeric operations return numeric types
|
||||
.Add, .Sub, .Mul, .Div, .Mod => left_type,
|
||||
|
||||
// TODO check left and right as numeric
|
||||
.Greater, .GreaterEqual, .Less, .LessEqual => SymbolUnderlyingType{ .Bool = {} },
|
||||
|
||||
// all boolean ops return bools
|
||||
.Equal, .And, .Or => SymbolUnderlyingType{ .Bool = {} },
|
||||
};
|
||||
},
|
||||
|
||||
// for now, unary operators only have .Not
|
||||
.Unary => |unary| {
|
||||
var right_type = self.resolveExprType(ctx, unary.right);
|
||||
return switch (unary.op) {
|
||||
.Negate => right_type,
|
||||
.Not => right_type,
|
||||
};
|
||||
},
|
||||
|
||||
.Literal => |literal| {
|
||||
return switch (literal) {
|
||||
.Bool => SymbolUnderlyingType{ .Bool = {} },
|
||||
|
||||
// TODO determine its i64 depending of parseInt results
|
||||
.Integer => SymbolUnderlyingType{ .Integer32 = {} },
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
},
|
||||
|
||||
.Grouping => |group_expr| return try self.resolveExprType(ctx, group_expr),
|
||||
|
||||
.Struct => |struc| blk: {
|
||||
const name = struc.name.lexeme;
|
||||
var typ = self.resolveGlobalType(ctx, name);
|
||||
if (typ == null) {
|
||||
self.doError("Unknown struct name '{}'\n", name);
|
||||
return CompileError.TypeError;
|
||||
}
|
||||
|
||||
return typ.?;
|
||||
},
|
||||
|
||||
// TODO variable resolution
|
||||
|
||||
// TODO Get (for structs and enums)
|
||||
|
||||
else => {
|
||||
std.debug.warn("TODO resolve expr {}\n", ast.ExprType(expr.*));
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nodePass(
|
||||
self: *@This(),
|
||||
ctx: *comp.CompilationContext,
|
||||
node: *ast.Node,
|
||||
) !void {
|
||||
self.setErrToken(null);
|
||||
self.setErrContext(null);
|
||||
|
||||
switch (node.*) {
|
||||
.Root => unreachable,
|
||||
.FnDecl => |decl| {
|
||||
|
@ -145,11 +214,21 @@ pub const TypeSolver = struct {
|
|||
|
||||
// TODO change enums to u32
|
||||
.Enum => |enu| {
|
||||
self.setErrToken(enu.name);
|
||||
self.setErrContext("enum {}", enu.name.lexeme);
|
||||
|
||||
try ctx.insertEnum(enu);
|
||||
},
|
||||
|
||||
// TODO infer type of expr in const
|
||||
//.ConstDecl => {},
|
||||
.ConstDecl => |constlist| {
|
||||
for (constlist.toSlice()) |constdecl| {
|
||||
self.setErrToken(constdecl.name);
|
||||
self.setErrContext("const {}", constdecl.name.lexeme);
|
||||
|
||||
var expr_type = try self.resolveExprType(ctx, constdecl.expr);
|
||||
try ctx.insertConst(constdecl, expr_type);
|
||||
}
|
||||
},
|
||||
|
||||
else => {
|
||||
std.debug.warn("TODO type analysis of {}\n", node.*);
|
||||
|
|
Loading…
Reference in a new issue