diff --git a/examples/hello.ry b/examples/hello.ry index 3c0c53c..b8aec3a 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -1,9 +1,6 @@ -const ( - test_var = 1 + 3 -) +// import std; fn f() i32 { - var a = 3; return 2; } @@ -11,6 +8,10 @@ fn f2() i32 { return f() + 2; } +const ( + piss = 1 + 3 +) + enum B { a b diff --git a/src/ast.zig b/src/ast.zig index cb166d1..c68eefe 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -97,6 +97,9 @@ pub const AssignExpr = struct { pub const ExprType = enum { Assign, + // vardecls as expressions is a hack + VarDecl, + Binary, Unary, Literal, @@ -109,6 +112,11 @@ pub const ExprType = enum { Set, }; +pub const VarDecl = struct { + assign: AssignExpr, + mutable: bool = false, +}; + pub const CallExpr = struct { callee: *Expr, paren: Token, @@ -140,6 +148,7 @@ pub const SetExpr = struct { pub const Expr = union(ExprType) { Assign: AssignExpr, + VarDecl: VarDecl, Binary: BinaryExpr, Unary: UnaryExpr, @@ -172,16 +181,10 @@ pub const ForStmt = struct { block: Block, }; -pub const VarDeclStmt = struct { - name: Token, - value: *Expr, -}; - pub const Stmt = union(enum) { Expr: *Expr, Println: *Expr, - VarDecl: VarDeclStmt, If: IfStmt, Loop: LoopStmt, For: ForStmt, @@ -258,18 +261,6 @@ pub const Stmt = union(enum) { return stmt; } - - pub fn mkVarDecl(allocator: *std.mem.Allocator, name: Token, value: *Expr) !*Stmt { - var stmt = try allocator.create(Stmt); - stmt.* = Stmt{ - .VarDecl = VarDeclStmt{ - .name = name, - .value = value, - }, - }; - - return stmt; - } }; pub const FieldList = std.ArrayList(StructField); diff --git a/src/ast_printer.zig b/src/ast_printer.zig index 82ecfde..0e9b549 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -239,6 +239,18 @@ pub fn printExpr(expr: *const Expr) void { .Variable => |token| std.debug.warn("{}", token.lexeme), + .VarDecl => |decl| { + if (decl.mutable) { + std.debug.warn("(mut "); + } else { + std.debug.warn("("); + } + + std.debug.warn("let {} ", decl.assign.name.lexeme); + printExpr(decl.assign.value); + std.debug.warn(")"); + }, + .Assign => |assign| { std.debug.warn("(set "); std.debug.warn("{} ", assign.name.lexeme); @@ -293,12 +305,6 @@ pub fn printStmt(ident: usize, stmt: *const Stmt) void { .Println => |expr| printSimpleOp("println", expr), .Expr => |expr| printExpr(expr), - .VarDecl => |decl| { - std.debug.warn("(let {} ", decl.name.lexeme); - printExpr(decl.value); - std.debug.warn(")"); - }, - .If => |ifstmt| { std.debug.warn("(if "); printExpr(ifstmt.condition); diff --git a/src/codegen.zig b/src/codegen.zig index 012373c..1be2e5a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -79,6 +79,8 @@ pub const Codegen = struct { // TODO VarDecl add things to the symbol table // TODO Assign modify symbol table + + // TODO Calls fetch symbol table, check arity of it at codegen level return switch (expr.*) { // TODO handle all literals, construct llvm values for them @@ -166,6 +168,7 @@ pub const Codegen = struct { .Call => |call| { const name = call.callee.*.Variable.lexeme; + //var sym = try self.ctx.fetchGlobalSymbol(func_name, .Function); var llvm_func = self.llvm_table.get(name); if (llvm_func == null) { @@ -173,6 +176,7 @@ pub const Codegen = struct { return CompileError.EmitError; } + // TODO args var args = LLVMValueList.init(self.allocator); errdefer args.deinit(); @@ -192,22 +196,6 @@ pub const Codegen = struct { ); }, - // TODO finish this - .Assign => |assign| { - // TODO find assign.name on the "parent context", we should have - // a way to do name resolution that is completely relative to - // where we currently are, and go up in scope. so that we find - // the LLVMValueRef. - - // we will also need to repeat the step for the type resolver - - //var typ = self.findCurrent(assign.name); - var assign_expr = try self.emitExpr(builder, assign.value); - - // TODO rm null - return llvm.LLVMBuildStore(builder, null, assign_expr); - }, - else => { std.debug.warn("Got unexpected expr {}\n", ast.ExprType(expr.*)); return CompileError.EmitError; @@ -302,7 +290,7 @@ pub const Codegen = struct { }, else => { - std.debug.warn("Got unexpected stmt {}\n", stmt.*); + std.debug.warn("Got unexpected statement {}\n", stmt.*); return CompileError.EmitError; }, } @@ -354,6 +342,7 @@ pub const Codegen = struct { llvm.LLVMPositionBuilderAtEnd(builder, entry); for (decl.body.toSlice()) |stmt| { + // TODO custom function context for us try self.emitStmt(builder, &stmt); } diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index f223bf9..827c460 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -29,12 +29,6 @@ pub const SymbolUnderlyingType = union(SymbolUnderlyingTypeEnum) { Enum: []const u8, }; -pub const Scope = std.StringHashMap(SymbolUnderlyingType); -pub const Environment = struct { - parent: *Environment, - scope: Scope, -}; - // functions, for our purposes, other than symbols, have: // - a return type // - TODO parameters @@ -45,7 +39,7 @@ pub const FunctionSymbol = struct { /// Parameters for a function are also a table instead of an ArrayList /// because we want to resolve identifiers to them. parameters: UnderlyingTypeMap, - env: *Environment, + symbols: SymbolTable, /// Find a given identifier in the function. Can resolve to either a parameter pub fn findSymbol(self: *const @This(), identifier: []const u8) ?SymbolData { diff --git a/src/parsers.zig b/src/parsers.zig index 04f9bf1..361fc66 100644 --- a/src/parsers.zig +++ b/src/parsers.zig @@ -295,6 +295,21 @@ pub const Parser = struct { return expr; } + fn mkVarDecl(self: *@This(), name: Token, value: *Expr, mutable: bool) !*Expr { + var vardecl = try self.allocator.create(Expr); + vardecl.* = Expr{ + .VarDecl = ast.VarDecl{ + .assign = ast.AssignExpr{ + .name = name, + .value = value, + }, + .mutable = mutable, + }, + }; + + return vardecl; + } + fn mkCall(self: *@This(), callee: *Expr, paren: Token, args: ast.ExprList) !*Expr { var expr = try self.allocator.create(Expr); expr.* = Expr{ @@ -701,7 +716,6 @@ pub const Parser = struct { fn parseStmt(self: *@This()) anyerror!*Stmt { return switch (self.peek().typ) { - .Var => try self.parseVarDecl(), .If => try self.parseIfStmt(), .Loop => try self.parseLoop(), .For => try self.parseForStmt(), @@ -711,14 +725,6 @@ pub const Parser = struct { }; } - fn parseVarDecl(self: *@This()) !*Stmt { - _ = try self.consumeSingle(.Var); - var name = try self.consumeSingle(.Identifier); - _ = try self.consumeSingle(.Equal); - var value = try self.parseExpr(); - return try Stmt.mkVarDecl(self.allocator, name, value); - } - /// Parse a list of statements. fn parseBlock(self: *@This()) !*Node { var stmts = try self.parseBlockInternal(ast.StmtList); @@ -834,8 +840,8 @@ pub const Parser = struct { fn parseAssignment(self: *@This()) anyerror!*Expr { // there can be two assignments coming out of this function: - // - an assignment to a variable with = - // - an update to a variable with +=, -= + // - a mutable/immutable variable declaration with := + // - an assignment to a variable with =, +=, -= // one is a statement, other is an expression. since the normal result // of this is an Expr, we wrap variable assignments in an Expr as well. @@ -849,7 +855,7 @@ pub const Parser = struct { var expr = try self.parseOr(); if (self.compareAnyOf(&[_]TokenType{ - .Equal, .PlusEqual, .MinusEqual, .StarEqual, + .ColonEqual, .Equal, .PlusEqual, .MinusEqual, .StarEqual, .SlashEqual, })) { return try self.finishAssignment(expr, mutable); @@ -865,7 +871,8 @@ pub const Parser = struct { var value = try self.parseAssignment(); // expr can be a (Variable|Set) - // op_tok can be one of two categories: + // op_tok can be one of three categories: + // - ColonEqual, parses to VarDecl (only when expr = Variable) // - Equal, parses to (Assign|Set) // - Inplace (+=, -=, *=, /=), parses to (Assign|Set) @@ -886,6 +893,7 @@ pub const Parser = struct { switch (expr.*) { .Variable => { switch (op_tok.typ) { + .ColonEqual => return try self.mkVarDecl(expr.Variable, value, mutable), .Equal => return try self.mkAssign(expr.Variable, value), .PlusEqual, .MinusEqual, .StarEqual, .SlashEqual => { @@ -901,7 +909,6 @@ pub const Parser = struct { .Get => |get| { switch (op_tok.typ) { - // TODO remove .ColonEqual from language .ColonEqual => { return self.doError("can not initialize struct field"); }, diff --git a/src/scanners.zig b/src/scanners.zig index 24f2116..5271235 100644 --- a/src/scanners.zig +++ b/src/scanners.zig @@ -54,7 +54,6 @@ const keywords = [_][]const u8{ "pub", "and", "or", - "var", }; const keyword_ttypes = [_]TokenType{ @@ -87,12 +86,9 @@ const keyword_ttypes = [_]TokenType{ .Pub, .And, .Or, - .Var, }; fn getKeyword(keyword: []const u8) ?TokenType { - std.debug.assert(keywords.len == keyword_ttypes.len); - for (keywords) |kw, idx| { if (std.mem.eql(u8, keyword, kw)) { return keyword_ttypes[idx]; diff --git a/src/tokens.zig b/src/tokens.zig index 75aa8f9..1df9b37 100644 --- a/src/tokens.zig +++ b/src/tokens.zig @@ -77,7 +77,6 @@ pub const TokenType = enum { Println, Pub, - Var, EOF, }; diff --git a/src/types.zig b/src/types.zig index 0015b2c..8afa4c3 100644 --- a/src/types.zig +++ b/src/types.zig @@ -170,45 +170,6 @@ pub const TypeSolver = struct { } } - pub fn stmtPass( - self: *@This(), - ctx: *comp.CompilationContext, - stmt: ast.Stmt, - ) !void { - switch (stmt) { - - // There are no side-effects to the type system when the statement - // is just an expression or a println. we just resolve it - // to ensure we dont have type errors. - .Expr => |expr_ptr| try self.resolveExprType(ctx, expr_ptr), - .Println => |expr_ptr| try self.resolveExprType(ctx, expr_ptr), - - // VarDecl means we check the type of the expression and - // insert it into the context, however we need to know a pointer - // to where we are, scope-wise, we don't have that info here, - // so it should be implicit into the context. - .VarDecl => @panic("TODO vardecl"), - - // If create two scopes for each branch of the if - .If => @panic("TODO ifstmt"), - - // Loop (creates 1 scope) asserts that the expression - // type is a bool - .Loop => @panic("TODO loop"), - - // For (creates 1 scope) receives arrays, which we dont have yet - .For => @panic("TODO for"), - - // Returns dont cause any type system things as they deal with - // values, however, we must ensure that the expression type - // matches the function type (must fetch from context, or we could - // pull a hack with err contexts, lol) - .Return => @panic("TODO return"), - - else => unreachable, - } - } - pub fn nodePass( self: *@This(), ctx: *comp.CompilationContext, @@ -233,14 +194,6 @@ pub const TypeSolver = struct { try parameters.append(param_type.?); } - // TODO scopes: bump scope - - for (decl.body.toSlice()) |stmt| { - try self.stmtPass(ctx, stmt); - } - - // TODO scopes: down scope - // TODO symbols and scope resolution, that's // its own can of worms var symbols = comp.SymbolTable.init(self.allocator);