Compare commits
5 Commits
3f48be3420
...
81fd718403
Author | SHA1 | Date |
---|---|---|
Luna | 81fd718403 | |
Luna | d235ce0d13 | |
Luna | 77c88fad34 | |
Luna | dfb7989123 | |
Luna | 849bbc0e94 |
10
README.md
10
README.md
|
@ -1,6 +1,6 @@
|
||||||
# rayoko
|
# rayoko
|
||||||
|
|
||||||
a programming language
|
a toy programming language and compiler
|
||||||
|
|
||||||
this time it'll work i promise it wont be like vig plrease
|
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
|
cd rayoko
|
||||||
zig build install --prefix ~/.local/
|
zig build install --prefix ~/.local/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# use
|
||||||
|
|
||||||
|
```
|
||||||
|
rayoko examples/hello.ry # outputs to outpath.o
|
||||||
|
gcc outpath.o examples/hello.c -o hello
|
||||||
|
./hello
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("add(2, 2) = %d\n", add(2, 2));
|
||||||
|
}
|
|
@ -32,22 +32,21 @@ fn test_function() B {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multwo(num: i32, double_flag: bool) i32 {
|
fn multwo(num: i32, double_flag: bool) i32 {
|
||||||
// TODO resolve expr variables
|
if (double_flag) {
|
||||||
if (true) {
|
|
||||||
var truthy = true;
|
var truthy = true;
|
||||||
return 1 * 2;
|
return num * 2;
|
||||||
} else {
|
} else {
|
||||||
var falsey = false;
|
var falsey = false;
|
||||||
return 1;
|
return num;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multwo_with_one(b: i32) i32 {
|
fn func_refer_param(b: i32) i32 {
|
||||||
return multwo(b, false) + b;
|
return b * 231 + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(a: i32, b: i32) i32 {
|
fn add(a: i32, b: i32) i32 {
|
||||||
return 69 + 69;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn and_fn() bool {
|
fn and_fn() bool {
|
||||||
|
|
|
@ -458,7 +458,7 @@ pub const TypeSolver = struct {
|
||||||
|
|
||||||
// for a function, we always create a new root scope for it
|
// for a function, we always create a new root scope for it
|
||||||
// and force-set it into the current context
|
// 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();
|
errdefer scope.deinit();
|
||||||
|
|
||||||
// we intentionally insert the function so that:
|
// we intentionally insert the function so that:
|
||||||
|
|
|
@ -401,7 +401,7 @@ pub fn printContext(ctx: CompilationContext) void {
|
||||||
std.debug.warn(
|
std.debug.warn(
|
||||||
"\tparameter {} typ {}\n",
|
"\tparameter {} typ {}\n",
|
||||||
param_kv.key,
|
param_kv.key,
|
||||||
prettyType(param_kv.value),
|
prettyType(param_kv.value.typ),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ pub const Codegen = struct {
|
||||||
ctx: *comp.CompilationContext,
|
ctx: *comp.CompilationContext,
|
||||||
llvm_table: LLVMTable,
|
llvm_table: LLVMTable,
|
||||||
|
|
||||||
|
current_function_name: ?[]const u8 = null,
|
||||||
|
|
||||||
pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen {
|
pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen {
|
||||||
return Codegen{
|
return Codegen{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
@ -218,7 +220,32 @@ pub const Codegen = struct {
|
||||||
return llvm.LLVMBuildStore(builder, null, assign_expr);
|
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 => {
|
else => {
|
||||||
std.debug.warn("Got unexpected expr {}\n", ast.ExprType(expr.*));
|
std.debug.warn("Got unexpected expr {}\n", ast.ExprType(expr.*));
|
||||||
|
@ -257,6 +284,8 @@ pub const Codegen = struct {
|
||||||
var then_rets = false;
|
var then_rets = false;
|
||||||
var else_rets = false;
|
var else_rets = false;
|
||||||
|
|
||||||
|
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
|
||||||
|
|
||||||
for (ifstmt.then_branch.toSlice()) |then_stmt| {
|
for (ifstmt.then_branch.toSlice()) |then_stmt| {
|
||||||
|
|
||||||
// keep emitting until branch has ret
|
// 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
|
// only build the br instruction if we didn't ret, because
|
||||||
// there can't be any instruction after a terminator
|
// there can't be any instruction after a terminator
|
||||||
// same applies for the else branch
|
// same applies for the else branch
|
||||||
|
@ -282,6 +313,7 @@ pub const Codegen = struct {
|
||||||
// roughly translating to kaleidoscope's
|
// roughly translating to kaleidoscope's
|
||||||
// 'Else *ElseV = Else->codegen();'
|
// 'Else *ElseV = Else->codegen();'
|
||||||
if (ifstmt.else_branch) |else_block| {
|
if (ifstmt.else_branch) |else_block| {
|
||||||
|
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
|
||||||
for (else_block.toSlice()) |else_stmt| {
|
for (else_block.toSlice()) |else_stmt| {
|
||||||
// keep emitting until branch has ret
|
// keep emitting until branch has ret
|
||||||
if (!else_rets)
|
if (!else_rets)
|
||||||
|
@ -292,6 +324,8 @@ pub const Codegen = struct {
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.ctx.dumpScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!else_rets)
|
if (!else_rets)
|
||||||
|
@ -303,18 +337,30 @@ pub const Codegen = struct {
|
||||||
|
|
||||||
if (then_rets and else_rets)
|
if (then_rets and else_rets)
|
||||||
_ = llvm.LLVMBuildUnreachable(builder);
|
_ = 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 => {
|
else => {
|
||||||
std.debug.warn("Got unexpected stmt {}\n", stmt.*);
|
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.
|
/// Emit LLVM ir for the given node.
|
||||||
fn genNode(
|
fn genNode(
|
||||||
self: *Codegen,
|
self: *Codegen,
|
||||||
|
@ -333,10 +385,10 @@ pub const Codegen = struct {
|
||||||
.Root => @panic("Should not have gotten Root"),
|
.Root => @panic("Should not have gotten Root"),
|
||||||
.FnDecl => |decl| {
|
.FnDecl => |decl| {
|
||||||
const name = decl.func_name.lexeme;
|
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;
|
var fn_sym = self.getFnSymbol(name);
|
||||||
std.debug.assert(comp.SymbolType(fn_sym_search) == .Function);
|
|
||||||
var fn_sym = fn_sym_search.Function;
|
|
||||||
|
|
||||||
const name_cstr = try std.cstr.addNullByte(self.allocator, name);
|
const name_cstr = try std.cstr.addNullByte(self.allocator, name);
|
||||||
errdefer self.allocator.free(name_cstr);
|
errdefer self.allocator.free(name_cstr);
|
||||||
|
@ -347,7 +399,7 @@ pub const Codegen = struct {
|
||||||
for (decl.params.toSlice()) |param| {
|
for (decl.params.toSlice()) |param| {
|
||||||
try param_types.append(try self.typeToLLVM(fn_sym.parameters.get(
|
try param_types.append(try self.typeToLLVM(fn_sym.parameters.get(
|
||||||
param.name.lexeme,
|
param.name.lexeme,
|
||||||
).?.value));
|
).?.value.typ));
|
||||||
}
|
}
|
||||||
|
|
||||||
var llvm_ret_type = llvm.LLVMFunctionType(
|
var llvm_ret_type = llvm.LLVMFunctionType(
|
||||||
|
@ -368,17 +420,13 @@ pub const Codegen = struct {
|
||||||
var builder = llvm.LLVMCreateBuilder();
|
var builder = llvm.LLVMCreateBuilder();
|
||||||
llvm.LLVMPositionBuilderAtEnd(builder, entry);
|
llvm.LLVMPositionBuilderAtEnd(builder, entry);
|
||||||
|
|
||||||
|
self.ctx.setScope(fn_sym.scope);
|
||||||
|
defer self.ctx.dumpScope();
|
||||||
|
|
||||||
for (decl.body.toSlice()) |stmt| {
|
for (decl.body.toSlice()) |stmt| {
|
||||||
try self.emitStmt(builder, &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);
|
std.debug.warn("cgen: generated function '{}'\n", name);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,9 @@ pub const Scope = struct {
|
||||||
parent: ?*Scope,
|
parent: ?*Scope,
|
||||||
env: UnderlyingTypeMap,
|
env: UnderlyingTypeMap,
|
||||||
|
|
||||||
/// Used for debug information.
|
/// List of children in the scope.
|
||||||
children: ScopeList,
|
children: ScopeList,
|
||||||
|
cur_child_idx: usize = 0,
|
||||||
|
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
id: ?[]const u8 = null,
|
id: ?[]const u8 = null,
|
||||||
|
@ -64,11 +65,24 @@ pub const Scope = struct {
|
||||||
return child;
|
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 {
|
pub fn deinit(self: *const @This()) void {
|
||||||
self.env.deinit();
|
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:
|
// functions, for our purposes, other than symbols, have:
|
||||||
// - a return type
|
// - a return type
|
||||||
// - parameters
|
// - parameters
|
||||||
|
@ -78,24 +92,10 @@ pub const FunctionSymbol = struct {
|
||||||
|
|
||||||
/// Parameters for a function are also a table instead of an ArrayList
|
/// Parameters for a function are also a table instead of an ArrayList
|
||||||
/// because we want to resolve identifiers to them.
|
/// because we want to resolve identifiers to them.
|
||||||
parameters: UnderlyingTypeMap,
|
parameters: ParameterMap,
|
||||||
parameter_list: TypeList,
|
parameter_list: TypeList,
|
||||||
|
|
||||||
scope: *Scope,
|
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
|
// structs are hashmaps pointing to SymbolUnderlyingType
|
||||||
|
@ -191,6 +191,7 @@ pub const CompilationContext = struct {
|
||||||
|
|
||||||
/// Set a given scope as the current scope.
|
/// Set a given scope as the current scope.
|
||||||
pub fn setScope(self: *@This(), scope: *Scope) void {
|
pub fn setScope(self: *@This(), scope: *Scope) void {
|
||||||
|
std.debug.warn("==set== set scope to {}\n", scope.id);
|
||||||
self.current_scope = scope;
|
self.current_scope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,10 +265,13 @@ pub const CompilationContext = struct {
|
||||||
param_types: TypeList,
|
param_types: TypeList,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
) !void {
|
) !void {
|
||||||
var type_map = UnderlyingTypeMap.init(self.allocator);
|
var param_map = ParameterMap.init(self.allocator);
|
||||||
|
|
||||||
for (decl.params.toSlice()) |param, idx| {
|
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;
|
const lex = decl.func_name.lexeme;
|
||||||
|
@ -276,7 +280,7 @@ pub const CompilationContext = struct {
|
||||||
.Function = FunctionSymbol{
|
.Function = FunctionSymbol{
|
||||||
.decl = decl,
|
.decl = decl,
|
||||||
.return_type = ret_type,
|
.return_type = ret_type,
|
||||||
.parameters = type_map,
|
.parameters = param_map,
|
||||||
.parameter_list = param_types,
|
.parameter_list = param_types,
|
||||||
.scope = scope,
|
.scope = scope,
|
||||||
},
|
},
|
||||||
|
@ -353,7 +357,7 @@ pub const CompilationContext = struct {
|
||||||
|
|
||||||
if (self.cur_function) |cur_function| {
|
if (self.cur_function) |cur_function| {
|
||||||
var kv_opt = cur_function.parameters.get(name);
|
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);
|
std.debug.warn("Unknown name {}\n", name);
|
||||||
|
|
Loading…
Reference in New Issue