Compare commits

..

No commits in common. "8065d0d905427bcfee5145fe0be2576ae02caa4f" and "e69451cdd9e8f7fcb176a09c8264dea9f3c4c7cc" have entirely different histories.

9 changed files with 54 additions and 118 deletions

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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 {

View file

@ -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");
},

View file

@ -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];

View file

@ -77,7 +77,6 @@ pub const TokenType = enum {
Println,
Pub,
Var,
EOF,
};

View file

@ -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);