summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig2
-rw-r--r--examples/test.nya6
-rw-r--r--src/codegen.zig38
-rw-r--r--src/main.zig2
-rw-r--r--src/parser.zig116
-rw-r--r--src/tokenize.zig49
6 files changed, 206 insertions, 7 deletions
diff --git a/build.zig b/build.zig
index e8b9c5e..fac011b 100644
--- a/build.zig
+++ b/build.zig
@@ -52,6 +52,8 @@ pub fn build(b: *std.Build) !void {
.optimize = optimize,
});
+ codegen_unit_tests.root_module.addImport("llvm", llvm.module("llvm"));
+
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
const run_token_unit_tests = b.addRunArtifact(token_unit_tests);
const run_parse_unit_tests = b.addRunArtifact(parse_unit_tests);
diff --git a/examples/test.nya b/examples/test.nya
index 3088546..0b359aa 100644
--- a/examples/test.nya
+++ b/examples/test.nya
@@ -1,6 +1,8 @@
fn main() -> i32 {
const testval = 6;
var testvar = testval;
- testvar = 5;
- return testvar;
+ testvar = 5 + 3 * 7;
+ if (1 == 1) return testvar;
+
+ return 7;
}
diff --git a/src/codegen.zig b/src/codegen.zig
index 59ef03b..51908c1 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -156,6 +156,30 @@ pub const Generator = struct {
_ = if (self.currentFuncIsVoid) core.LLVMBuildRetVoid(self.builder);
}
+ fn genIf(self: *Generator, stmt: parse.NodeStmt) error{ OutOfMemory, Immutable }!void {
+ const ifkind = stmt.kind.ifstmt;
+ const block = ifkind.body;
+ const expr = ifkind.expr;
+
+ const condition = self.genExpr(expr);
+ const func = self.currentFunc.?;
+
+ const then = core.LLVMAppendBasicBlock(func, "then");
+ const elsebb = core.LLVMAppendBasicBlock(func, "elsebb");
+ const cont = core.LLVMAppendBasicBlock(func, "continue");
+
+ _ = core.LLVMBuildCondBr(self.builder, condition, then, elsebb);
+
+ _ = core.LLVMPositionBuilderAtEnd(self.builder, then);
+ try self.genStmt(block.*);
+ _ = core.LLVMBuildBr(self.builder, cont);
+
+ _ = core.LLVMPositionBuilderAtEnd(self.builder, elsebb);
+ _ = core.LLVMBuildBr(self.builder, cont);
+
+ _ = core.LLVMPositionBuilderAtEnd(self.builder, cont);
+ }
+
fn genStmt(self: *Generator, stmt: parse.NodeStmt) !void {
try switch (stmt.kind) {
.exit => |expr| self.genExit(expr),
@@ -163,6 +187,7 @@ pub const Generator = struct {
.defValue => self.genValue(stmt),
.defVar => self.genVar(stmt),
.assignVar => self.genAssign(stmt),
+ .ifstmt => self.genIf(stmt),
else => {},
};
}
@@ -176,6 +201,19 @@ pub const Generator = struct {
break :blk core.LLVMBuildLoad2(self.builder, toLLVMtype(expr.typ.?, table), ptr, "");
},
.intLit => |int| core.LLVMConstInt(core.LLVMInt32TypeInContext(self.context), @intCast(int.intLit), 1),
+ .binaryOp => |exp| blk: {
+ const lhs = self.genExpr(exp.lhs.*);
+ const rhs = self.genExpr(exp.rhs.*);
+
+ break :blk switch (exp.op) {
+ .plus => core.LLVMBuildAdd(self.builder, lhs, rhs, "add"),
+ .minus => core.LLVMBuildSub(self.builder, lhs, rhs, "sub"),
+ .star => core.LLVMBuildMul(self.builder, lhs, rhs, "mul"),
+ .slash => core.LLVMBuildSDiv(self.builder, lhs, rhs, "div"),
+ .eqleql => core.LLVMBuildICmp(self.builder, types.LLVMIntPredicate.LLVMIntEQ, lhs, rhs, "eql"),
+ else => core.LLVMBuildICmp(self.builder, types.LLVMIntPredicate.LLVMIntEQ, lhs, rhs, "eql"),
+ };
+ },
};
}
diff --git a/src/main.zig b/src/main.zig
index 1e54dec..613d8f9 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -59,11 +59,11 @@ pub fn main() !void {
// Codegen
var arena = std.heap.ArenaAllocator.init(allocator);
- defer arena.deinit();
var generator = gen.Generator.init(arena.allocator(), tree);
defer generator.deinit();
const code = try generator.generate();
try outWriter.writeAll(code);
+ arena.deinit();
const binFile = try getFileName(allocator, out_name, "");
defer allocator.free(binFile);
diff --git a/src/parser.zig b/src/parser.zig
index 08f098c..129bf94 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -85,6 +85,7 @@ pub const NodeStmt = struct {
for (blockChildren) |child| try childrenArray.append(child);
},
.function => |fun| try childrenArray.append(fun.block.*.asNode()),
+ .ifstmt => |ifstmt| try childrenArray.append(ifstmt.body.*.asNode()),
}
return try childrenArray.toOwnedSlice();
}
@@ -141,9 +142,40 @@ pub const Parser = struct {
self.nodes.deinit();
}
+ // fn parseExpr(self: *Parser) !NodeExpr {
+ // var typ: ?TypeIdent = null;
+ // const kind = try blk: {
+ // try switch (self.tokens.peek().?) {
+ // .intLit => {
+ // typ = TypeIdent{
+ // .ident = "i32",
+ // .list = false,
+ // };
+ // break :blk ExprKind{ .intLit = (try self.tokens.consume(.intLit)).? };
+ // },
+ // .ident => {
+ // const ident = (try self.tokens.consume(.ident)).?;
+ // typ = TypeIdent{
+ // .ident = "i32",
+ // .list = false,
+ // };
+ // break :blk ExprKind{ .ident = ident };
+ // },
+ // else => break :blk ParsingError.InvalidExpression,
+ // };
+ // };
+ // return NodeExpr{
+ // .id = self.reserveId(),
+ // .kind = kind,
+ // .isConst = kind.isConstant(),
+ // .typ = typ,
+ // .symtable = self.top,
+ // };
+ // }
+
fn parseExpr(self: *Parser) !NodeExpr {
var typ: ?TypeIdent = null;
- const kind = try blk: {
+ const kind: ExprKind = try blk: {
try switch (self.tokens.peek().?) {
.intLit => {
typ = TypeIdent{
@@ -163,26 +195,90 @@ pub const Parser = struct {
else => break :blk ParsingError.InvalidExpression,
};
};
- return NodeExpr{
+
+ const lhs = NodeExpr{
.id = self.reserveId(),
+ .isConst = false,
.kind = kind,
- .isConst = kind.isConstant(),
- .typ = typ,
.symtable = self.top,
+ .typ = typ,
};
+
+ const lhsptr = try self.allocator.create(NodeExpr);
+ lhsptr.* = lhs;
+
+ const precTable: []const []const tok.TokenType = &.{
+ &.{ .plus, .minus },
+ &.{ .star, .slash },
+ &.{.eqleql},
+ };
+
+ return try genBinOp(self, precTable[0], typ, lhsptr) //.
+ orelse try genBinOp(self, precTable[1], typ, lhsptr) //.
+ orelse try genBinOp(self, precTable[2], typ, lhsptr) //.
+ orelse lhs;
+ }
+
+ fn genBinOp(self: *Parser, comptime ops: []const tok.TokenType, typ: ?TypeIdent, lhs: *NodeExpr) ParsingError!?NodeExpr {
+ for (ops) |op|
+ if (self.tokens.peek().? == op) {
+ const oper = self.tokens.next();
+ const rhsptr = try self.allocator.create(NodeExpr);
+ rhsptr.* = try self.parseExpr();
+ const kind = ExprKind{
+ .binaryOp = .{
+ .lhs = lhs,
+ .rhs = rhsptr,
+ .op = oper.?,
+ },
+ };
+ return NodeExpr{
+ .id = self.reserveId(),
+ .isConst = false,
+ .kind = kind,
+ .symtable = self.top,
+ .typ = typ,
+ };
+ };
+ return null;
}
fn parseStmt(self: *Parser) ParsingError!NodeStmt {
return switch (self.tokens.peek().?) {
+ .ifstmt => try self.parseIf(),
.exit => try self.parseExit(),
.constant => try self.parseConstant(),
.variable => try self.parseVariable(),
.ident => try self.parseAssign(),
.fun => try self.parseFunc(),
+ .openBrace => try self.parseBlock(),
else => ParsingError.InvalidStatement,
};
}
+ fn parseIf(self: *Parser) ParsingError!NodeStmt {
+ _ = try self.tokens.consume(.ifstmt); // if
+
+ _ = try self.tokens.consume(.openParen); // (
+ const expr = try self.parseExpr(); // EXPR
+ _ = try self.tokens.consume(.closeParen); // )
+
+ const body = try self.allocator.create(NodeStmt);
+ body.* = try self.parseStmt();
+
+ const kind = StmtKind{
+ .ifstmt = .{
+ .expr = expr,
+ .body = body,
+ },
+ };
+ return NodeStmt{
+ .id = self.reserveId(),
+ .kind = kind,
+ .symtable = self.top,
+ };
+ }
+
fn parseFunc(self: *Parser) ParsingError!NodeStmt {
var typ: ?TypeIdent = null;
_ = try self.tokens.consume(.fun);
@@ -338,12 +434,19 @@ pub const NodeVar = struct {
expr: NodeExpr,
};
+pub const NodeIf = struct {
+ expr: NodeExpr,
+ body: *NodeStmt,
+};
+
pub const NodeExit = NodeExpr;
pub const NodeIntlit = Token;
pub const NodeIdent = Token;
pub const NodeBlock = []const NodeStmt;
+pub const NodeBinOp = Token;
pub const StmtKind = union(enum) {
+ ifstmt: NodeIf,
function: NodeFunction,
exit: NodeExit,
defValue: NodeValue,
@@ -355,6 +458,11 @@ pub const StmtKind = union(enum) {
pub const ExprKind = union(enum) {
intLit: NodeIntlit,
ident: NodeIdent,
+ binaryOp: struct {
+ lhs: *NodeExpr,
+ rhs: *NodeExpr,
+ op: NodeBinOp,
+ },
pub fn isConstant(self: ExprKind) bool {
return switch (self) {
diff --git a/src/tokenize.zig b/src/tokenize.zig
index 770f483..6fc0ebc 100644
--- a/src/tokenize.zig
+++ b/src/tokenize.zig
@@ -11,6 +11,7 @@ pub const TokenType = enum {
ident,
intLit,
// Keywords
+ ifstmt,
constant,
variable,
exit,
@@ -22,6 +23,9 @@ pub const TokenType = enum {
slash,
semiCol,
equal,
+ eqleql,
+ lessthan,
+ greaterthan,
// Symbols
openBrace,
closeBrace,
@@ -35,6 +39,7 @@ pub const Token = union(TokenType) {
ident: []const u8,
intLit: i32,
// Keywords
+ ifstmt,
constant,
variable,
exit,
@@ -46,6 +51,9 @@ pub const Token = union(TokenType) {
slash,
semiCol,
equal,
+ eqleql,
+ lessthan,
+ greaterthan,
// Symbols
openBrace,
closeBrace,
@@ -53,6 +61,35 @@ pub const Token = union(TokenType) {
closeParen,
arrow,
+ pub fn toTokenType(self: Token) TokenType {
+ switch (self) {
+ .ident => .ident,
+ .intLit => .intLit,
+ // Keywords
+ .ifstmt => .ifstmt,
+ .constant => .constant,
+ .variable => .variable,
+ .exit => .exit,
+ .fun => .fun,
+ // => Operators .=>
+ .plus => .plus,
+ .minus => .minus,
+ .star => .star,
+ .slash => .slash,
+ .semiCol => .semiCol,
+ .equal => .equal,
+ .eqleql => .eqleql,
+ .lessthan => .lessthan,
+ .greaterthan => .greaterthan,
+ // => Symbols .=>
+ .openBrace => .openBrace,
+ .closeBrace => .closeBrace,
+ .openParen => .openParen,
+ .closeParen => .closeParen,
+ .arrow => .arrow,
+ }
+ }
+
pub fn fromChar(char: u8) !Token {
return switch (char) {
'+' => .plus,
@@ -65,6 +102,8 @@ pub const Token = union(TokenType) {
'}' => .closeBrace,
'(' => .openParen,
')' => .closeParen,
+ '<' => .lessthan,
+ '>' => .greaterthan,
else => TokenizeError.UnknownToken,
};
}
@@ -75,6 +114,7 @@ pub const Token = union(TokenType) {
if (eql(u8, str, "const")) return .constant;
if (eql(u8, str, "var")) return .variable;
if (eql(u8, str, "fn")) return .fun;
+ if (eql(u8, str, "if")) return .ifstmt;
return Token{ .ident = str };
}
};
@@ -160,6 +200,15 @@ pub const Tokenizer = struct {
while (self.src.peek()) |char| {
try switch (char) {
+ '=' => {
+ self.src.skip();
+ if (self.src.peek().? != '=') {
+ try self.toks.append(.equal);
+ continue;
+ }
+ self.src.skip();
+ try self.toks.append(.eqleql);
+ },
'-' => {
self.src.skip();
if (self.src.peek().? != '>') {