diff --git a/README.md b/README.md index 6eb90c3..434dec4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # rayoko -a programming language +a toy programming language and compiler this time it'll work i promise it wont be like vig plrease @@ -11,3 +11,11 @@ git clone https://gitdab.com/luna/rayoko cd rayoko zig build install --prefix ~/.local/ ``` + +# use + +``` +rayoko examples/hello.ry # outputs to outpath.o +gcc outpath.o examples/hello.c -o hello +./hello +``` diff --git a/examples/hello.c b/examples/hello.c new file mode 100644 index 0000000..fff2531 --- /dev/null +++ b/examples/hello.c @@ -0,0 +1,5 @@ +#include + +int main(void) { + printf("add(2, 2) = %d\n", add(2, 2)); +} diff --git a/examples/hello.ry b/examples/hello.ry index e935ddc..0f97d4f 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -32,22 +32,21 @@ fn test_function() B { } fn multwo(num: i32, double_flag: bool) i32 { - // TODO resolve expr variables - if (true) { + if (double_flag) { var truthy = true; - return 1 * 2; + return num * 2; } else { var falsey = false; - return 1; + return num; } } -fn multwo_with_one(b: i32) i32 { - return multwo(b, false) + b; +fn func_refer_param(b: i32) i32 { + return b * 231 + b; } fn add(a: i32, b: i32) i32 { - return 69 + 69; + return a + b; } fn and_fn() bool { diff --git a/src/analysis.zig b/src/analysis.zig index f5c4897..4081d43 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -458,7 +458,7 @@ pub const TypeSolver = struct { // for a function, we always create a new root scope for it // and force-set it into the current context - var scope = try comp.Scope.create(self.allocator, null, "function"); + var scope = try comp.Scope.create(self.allocator, null, name); errdefer scope.deinit(); // we intentionally insert the function so that: diff --git a/src/ast_printer.zig b/src/ast_printer.zig index a5d23e3..48f9ae2 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -401,7 +401,7 @@ pub fn printContext(ctx: CompilationContext) void { std.debug.warn( "\tparameter {} typ {}\n", param_kv.key, - prettyType(param_kv.value), + prettyType(param_kv.value.typ), ); } diff --git a/src/codegen.zig b/src/codegen.zig index 64c7e72..45d369d 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -31,6 +31,8 @@ pub const Codegen = struct { ctx: *comp.CompilationContext, llvm_table: LLVMTable, + current_function_name: ?[]const u8 = null, + pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen { return Codegen{ .allocator = allocator, @@ -218,7 +220,32 @@ pub const Codegen = struct { return llvm.LLVMBuildStore(builder, null, assign_expr); }, - .Variable => @panic("TODO emit variables"), + .Variable => |vari| { + var kv_opt = self.ctx.metadata_map.get(expr); + + if (kv_opt == null) { + std.debug.warn("variable {} not fully analyzed\n", vari); + return CompileError.EmitError; + } + + // we have metadata, which means we can check if the variable + // is coming from the scope or from the function + + var metadata = kv_opt.?.value; + + return switch (metadata.using) { + .Function => blk: { + var param = metadata.from_function.?.parameters.get(vari.lexeme).?.value; + var llvm_func = self.llvm_table.get(self.current_function_name.?).?.value; + + // may i thank all the demons in hell for giving me + // good energies to make this work + break :blk llvm.LLVMGetParam(llvm_func, @intCast(c_uint, param.idx)); + }, + + .Scope => @panic("TODO local variables"), + }; + }, else => { std.debug.warn("Got unexpected expr {}\n", ast.ExprType(expr.*)); @@ -257,6 +284,8 @@ pub const Codegen = struct { var then_rets = false; var else_rets = false; + self.ctx.setScope(self.ctx.current_scope.?.nextChild()); + for (ifstmt.then_branch.toSlice()) |then_stmt| { // keep emitting until branch has ret @@ -269,6 +298,8 @@ pub const Codegen = struct { } } + self.ctx.dumpScope(); + // only build the br instruction if we didn't ret, because // there can't be any instruction after a terminator // same applies for the else branch @@ -282,6 +313,7 @@ pub const Codegen = struct { // roughly translating to kaleidoscope's // 'Else *ElseV = Else->codegen();' if (ifstmt.else_branch) |else_block| { + self.ctx.setScope(self.ctx.current_scope.?.nextChild()); for (else_block.toSlice()) |else_stmt| { // keep emitting until branch has ret if (!else_rets) @@ -292,6 +324,8 @@ pub const Codegen = struct { else => {}, } } + + self.ctx.dumpScope(); } if (!else_rets) @@ -303,18 +337,30 @@ pub const Codegen = struct { if (then_rets and else_rets) _ = llvm.LLVMBuildUnreachable(builder); - - // phis aren't needed since our ifs arent expressions - - //var phi = llvm.LLVMBuildPhi(builder, llvm.LLVMVoidType(), c"iftmp"); - //var then_bb_val = llvm.LLVMBasicBlockAsValue(then_bb); - //var else_bb_val = llvm.LLVMBasicBlockAsValue(else_bb); - //llvm.LLVMAddIncoming(phi, &then_bb_val, &then_bb, 1); - //llvm.LLVMAddIncoming(phi, &else_bb_val, &else_bb, 1); }, - // TODO - .VarDecl => {}, + .VarDecl => |vardecl| { + // we alaready inferred the type of the variable in the + // analyze pass and the current scope contains the variable's + // type(hopefully), so we resolve it + const name = vardecl.name.lexeme; + var var_metadata = try self.ctx.resolveVarType(name); + + var name_cstr = try std.cstr.addNullByte(self.allocator, name); + errdefer self.allocator.free(name_cstr); + + var fn_symbol = self.getFnSymbol(self.current_function_name.?); + + // TODO add llvm value ref to var metadata as well + var variable = llvm.LLVMBuildAlloca( + builder, + try self.typeToLLVM(var_metadata.typ), + name_cstr.ptr, + ); + + var llvm_expr = try self.emitExpr(builder, vardecl.value); + _ = llvm.LLVMBuildStore(builder, llvm_expr, variable); + }, else => { std.debug.warn("Got unexpected stmt {}\n", stmt.*); @@ -323,6 +369,12 @@ pub const Codegen = struct { } } + fn getFnSymbol(self: *@This(), name: []const u8) comp.FunctionSymbol { + var fn_sym_search = self.ctx.symbol_table.get(name).?.value; + std.debug.assert(comp.SymbolType(fn_sym_search) == .Function); + return fn_sym_search.Function; + } + /// Emit LLVM ir for the given node. fn genNode( self: *Codegen, @@ -333,10 +385,10 @@ pub const Codegen = struct { .Root => @panic("Should not have gotten Root"), .FnDecl => |decl| { const name = decl.func_name.lexeme; + self.current_function_name = name; + std.debug.warn("cgen: genning function '{}'\n", name); - var fn_sym_search = self.ctx.symbol_table.get(name).?.value; - std.debug.assert(comp.SymbolType(fn_sym_search) == .Function); - var fn_sym = fn_sym_search.Function; + var fn_sym = self.getFnSymbol(name); const name_cstr = try std.cstr.addNullByte(self.allocator, name); errdefer self.allocator.free(name_cstr); @@ -347,7 +399,7 @@ pub const Codegen = struct { for (decl.params.toSlice()) |param| { try param_types.append(try self.typeToLLVM(fn_sym.parameters.get( param.name.lexeme, - ).?.value)); + ).?.value.typ)); } var llvm_ret_type = llvm.LLVMFunctionType( @@ -368,17 +420,13 @@ pub const Codegen = struct { var builder = llvm.LLVMCreateBuilder(); llvm.LLVMPositionBuilderAtEnd(builder, entry); + self.ctx.setScope(fn_sym.scope); + defer self.ctx.dumpScope(); + for (decl.body.toSlice()) |stmt| { try self.emitStmt(builder, &stmt); } - //var tmp = llvm.LLVMBuildAdd( - // builder, - // llvm.LLVMGetParam(func, 0), - // llvm.LLVMGetParam(func, 1), - // c"tmp", - //); - std.debug.warn("cgen: generated function '{}'\n", name); }, diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index e50a5e9..b22768c 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -39,8 +39,9 @@ pub const Scope = struct { parent: ?*Scope, env: UnderlyingTypeMap, - /// Used for debug information. + /// List of children in the scope. children: ScopeList, + cur_child_idx: usize = 0, allocator: *std.mem.Allocator, id: ?[]const u8 = null, @@ -64,11 +65,24 @@ pub const Scope = struct { return child; } + pub fn nextChild(self: *@This()) *Scope { + var child = self.children.at(self.cur_child_idx); + self.cur_child_idx += 1; + return child; + } + pub fn deinit(self: *const @This()) void { self.env.deinit(); } }; +pub const Parameter = struct { + idx: usize, + typ: SymbolUnderlyingType, +}; + +pub const ParameterMap = std.StringHashMap(Parameter); + // functions, for our purposes, other than symbols, have: // - a return type // - parameters @@ -78,24 +92,10 @@ 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, + parameters: ParameterMap, parameter_list: TypeList, scope: *Scope, - - /// Find a given identifier in the function. Can resolve to either a parameter - pub fn findSymbol(self: *const @This(), identifier: []const u8) ?SymbolData { - // try to find it on overall variable symbols - - // TODO - - //var var_sym = self.symbols.get(identifier); - //if (var_sym != null) return var_sym.?.value; - - //var param_sym = self.parameters.get(identifier); - //if (param_sym != null) return param_sym.?.value; - return null; - } }; // structs are hashmaps pointing to SymbolUnderlyingType @@ -191,6 +191,7 @@ pub const CompilationContext = struct { /// Set a given scope as the current scope. pub fn setScope(self: *@This(), scope: *Scope) void { + std.debug.warn("==set== set scope to {}\n", scope.id); self.current_scope = scope; } @@ -264,10 +265,13 @@ pub const CompilationContext = struct { param_types: TypeList, scope: *Scope, ) !void { - var type_map = UnderlyingTypeMap.init(self.allocator); + var param_map = ParameterMap.init(self.allocator); for (decl.params.toSlice()) |param, idx| { - _ = try type_map.put(param.name.lexeme, param_types.at(idx)); + _ = try param_map.put(param.name.lexeme, Parameter{ + .idx = idx, + .typ = param_types.at(idx), + }); } const lex = decl.func_name.lexeme; @@ -276,7 +280,7 @@ pub const CompilationContext = struct { .Function = FunctionSymbol{ .decl = decl, .return_type = ret_type, - .parameters = type_map, + .parameters = param_map, .parameter_list = param_types, .scope = scope, }, @@ -353,7 +357,7 @@ pub const CompilationContext = struct { if (self.cur_function) |cur_function| { var kv_opt = cur_function.parameters.get(name); - if (kv_opt) |kv| return VariableMetadata.withParam(cur_function, kv.value); + if (kv_opt) |kv| return VariableMetadata.withParam(cur_function, kv.value.typ); } std.debug.warn("Unknown name {}\n", name);