add type analysis for consts

- add the basics of recursive type resolving for expressions
 - set err context for enum
This commit is contained in:
Luna 2019-09-25 14:34:32 -03:00
parent 1040eef79f
commit cb8908dc80
6 changed files with 142 additions and 12 deletions

View file

@ -1,5 +1,9 @@
// import std; // import std;
const (
AWOO = 1 + 2
)
enum B { enum B {
a a
b b

View file

@ -73,10 +73,11 @@ pub const BinaryExpr = struct {
pub const UnaryOperator = enum { pub const UnaryOperator = enum {
Not, Not,
Negate,
}; };
pub const UnaryExpr = struct { pub const UnaryExpr = struct {
op: Token, op: UnaryOperator,
right: *Expr, right: *Expr,
}; };

View file

@ -191,8 +191,28 @@ fn printBinOp(inner: var) void {
std.debug.warn(")"); std.debug.warn(")");
} }
fn printSingleOp(tok: []const u8, applied: *const Expr) void { const unary_operator_tokens = [_][]const u8{
std.debug.warn("({}", tok); "!", "-",
};
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); printExpr(applied);
std.debug.warn(")"); std.debug.warn(")");
} }
@ -201,8 +221,8 @@ pub fn printExpr(expr: *const Expr) void {
switch (expr.*) { switch (expr.*) {
.Binary => |binary| printBinOp(binary), .Binary => |binary| printBinOp(binary),
.Unary => |unary| printSingleOp(unary.op.lexeme, unary.right), .Unary => |unary| printSingleOp(unary.op, unary.right),
.Grouping => |expr_ptr| printSingleOp("group", expr_ptr), .Grouping => |expr_ptr| printSimpleOp("group", expr_ptr),
.Literal => |literal| { .Literal => |literal| {
switch (literal) { switch (literal) {
@ -282,7 +302,7 @@ pub fn printExpr(expr: *const Expr) void {
pub fn printStmt(ident: usize, stmt: *const Stmt) void { pub fn printStmt(ident: usize, stmt: *const Stmt) void {
switch (stmt.*) { switch (stmt.*) {
.Println => |expr| printSingleOp("println", expr), .Println => |expr| printSimpleOp("println", expr),
.Expr => |expr| printExpr(expr), .Expr => |expr| printExpr(expr),
.If => |ifstmt| { .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 => { else => {
std.debug.warn("TODO handle print of {}\n", kv.value); std.debug.warn("TODO handle print of {}\n", kv.value);
unreachable; unreachable;

View file

@ -68,6 +68,7 @@ pub const SymbolType = enum {
Struct, Struct,
Enum, Enum,
Variable, Variable,
Const,
}; };
pub const SymbolData = union(SymbolType) { pub const SymbolData = union(SymbolType) {
@ -78,6 +79,7 @@ pub const SymbolData = union(SymbolType) {
// variables (parameters and variables), for the type system // variables (parameters and variables), for the type system
// only have types // only have types
Variable: SymbolUnderlyingType, Variable: SymbolUnderlyingType,
Const: SymbolUnderlyingType,
}; };
const builtin_type_identifiers = [_][]const u8{ "i32", "i64", "bool" }; const builtin_type_identifiers = [_][]const u8{ "i32", "i64", "bool" };
@ -175,4 +177,8 @@ pub const CompilationContext = struct {
.Enum = ident_map, .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 });
}
}; };

View file

@ -61,6 +61,24 @@ fn toBinaryOperator(op: Token) !ast.BinaryOperator {
return ParseError.UnknownOperator; 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 { pub const Parser = struct {
allocator: *Allocator, allocator: *Allocator,
scanner: *Scanner, scanner: *Scanner,
@ -241,9 +259,7 @@ pub const Parser = struct {
return grouping; return grouping;
} }
fn mkUnary(self: *Parser, op: Token, right: *Expr) !*Expr { fn mkUnary(self: *Parser, op: ast.UnaryOperator, right: *Expr) !*Expr {
std.debug.warn("Unary\n");
var expr = try self.allocator.create(Expr); var expr = try self.allocator.create(Expr);
expr.* = Expr{ expr.* = Expr{
.Unary = ast.UnaryExpr{ .Unary = ast.UnaryExpr{
@ -1015,7 +1031,7 @@ pub const Parser = struct {
var op = self.previous(); var op = self.previous();
var right = try self.parseUnary(); var right = try self.parseUnary();
return try self.mkUnary(op, right); return try self.mkUnary(try toUnaryOperator(op), right);
} }
var expr = try self.parseCall(); var expr = try self.parseCall();

View file

@ -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( pub fn nodePass(
self: *@This(), self: *@This(),
ctx: *comp.CompilationContext, ctx: *comp.CompilationContext,
node: *ast.Node, node: *ast.Node,
) !void { ) !void {
self.setErrToken(null);
self.setErrContext(null);
switch (node.*) { switch (node.*) {
.Root => unreachable, .Root => unreachable,
.FnDecl => |decl| { .FnDecl => |decl| {
@ -145,11 +214,21 @@ pub const TypeSolver = struct {
// TODO change enums to u32 // TODO change enums to u32
.Enum => |enu| { .Enum => |enu| {
self.setErrToken(enu.name);
self.setErrContext("enum {}", enu.name.lexeme);
try ctx.insertEnum(enu); try ctx.insertEnum(enu);
}, },
// TODO infer type of expr in const .ConstDecl => |constlist| {
//.ConstDecl => {}, 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 => { else => {
std.debug.warn("TODO type analysis of {}\n", node.*); std.debug.warn("TODO type analysis of {}\n", node.*);