Compare commits

..

No commits in common. "77af4d067be345fd59cd7a670042a997816d7528" and "bb1e689c5a3e02019ea714abdcb1defe949bf883" have entirely different histories.

4 changed files with 121 additions and 203 deletions

View File

@ -1,20 +1,12 @@
i = 0 i = 0
while i < 5: while i < 5 do
@print("hello, world") @print("hello, world")
i = i + 1 i = i + 1
end end
if i > 6:
@print("`i` greater than `6`")
elif i == 4:
@print("`i` is equal to `4`")
else:
@print("i'unno")
end
return { return {
.title = "Game", .title = "Game",
.width = 1280, .width = 1280,

View File

@ -36,7 +36,6 @@ pub const RuntimeEnv = struct {
}; };
const Opcode = union (enum) { const Opcode = union (enum) {
pop,
push_nil, push_nil,
push_true, push_true,
push_false, push_false,
@ -136,9 +135,9 @@ pub const RuntimeEnv = struct {
.subtraction => .sub, .subtraction => .sub,
.multiplication => .mul, .multiplication => .mul,
.divsion => .div, .divsion => .div,
.greater_equals_comparison => .cge, .greater_equals_comparison => .eql,
.greater_than_comparison => .cgt, .greater_than_comparison => .cgt,
.equals_comparison => .eql, .equals_comparison => .cge,
.less_than_comparison => .clt, .less_than_comparison => .clt,
.less_equals_comparison => .cle, .less_equals_comparison => .cle,
}); });
@ -264,19 +263,13 @@ pub const RuntimeEnv = struct {
fn compile_statement(self: *CompilationUnit, chunk: *Chunk, statement: ast.Statement) RuntimeError!void { fn compile_statement(self: *CompilationUnit, chunk: *Chunk, statement: ast.Statement) RuntimeError!void {
switch (statement) { switch (statement) {
.@"return" => |@"return"| { .@"return" => |@"return"| {
if (@"return") |expression| { if (@"return".expression) |expression| {
try self.compile_expression(chunk, expression); try self.compile_expression(chunk, expression);
} else { } else {
try chunk.opcodes.push_one(.push_nil); try chunk.opcodes.push_one(.push_nil);
} }
}, },
.block => |block| {
for (block.values) |block_statement| {
try self.compile_statement(chunk, block_statement);
}
},
.@"while" => |@"while"| { .@"while" => |@"while"| {
try self.compile_expression(chunk, @"while".condition_expression); try self.compile_expression(chunk, @"while".condition_expression);
try chunk.opcodes.push_one(.{.jf = 0}); try chunk.opcodes.push_one(.{.jf = 0});
@ -287,6 +280,8 @@ pub const RuntimeEnv = struct {
try self.compile_statement(chunk, block_statement); try self.compile_statement(chunk, block_statement);
} }
coral.debug.assert(chunk.opcodes.values.len < coral.math.max_int(@typeInfo(u32).Int));
chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1); chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1);
try self.compile_expression(chunk, @"while".condition_expression); try self.compile_expression(chunk, @"while".condition_expression);
@ -303,20 +298,12 @@ pub const RuntimeEnv = struct {
try self.compile_statement(chunk, block_statement); try self.compile_statement(chunk, block_statement);
} }
coral.debug.assert(chunk.opcodes.values.len < coral.math.max_int(@typeInfo(u32).Int));
chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1); chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1);
if (@"if".else_statement) |else_statement| {
try self.compile_statement(chunk, else_statement.*);
}
}, },
.expression => |expression| { .expression => |expression| try self.compile_expression(chunk, expression),
try self.compile_expression(chunk, expression);
if (expression == .invoke) {
try chunk.opcodes.push_one(.pop);
}
},
} }
} }
@ -339,10 +326,10 @@ pub const RuntimeEnv = struct {
} }
}; };
fn compile(self: *Chunk, statements: []const ast.Statement) RuntimeError!void { fn compile(self: *Chunk, statements: *const ast.StatementList) RuntimeError!void {
var unit = CompilationUnit{}; var unit = CompilationUnit{};
for (statements) |statement| { for (statements.values) |statement| {
try unit.compile_statement(self, statement); try unit.compile_statement(self, statement);
} }
} }
@ -377,12 +364,6 @@ pub const RuntimeEnv = struct {
while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) { while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) {
switch (self.opcodes.values[opcode_cursor]) { switch (self.opcodes.values[opcode_cursor]) {
.pop => {
if (try self.pop_local()) |ref| {
self.env.discard(ref);
}
},
.push_nil => try self.env.locals.push_one(null), .push_nil => try self.env.locals.push_one(null),
.push_true => try self.env.locals.push_one(try self.env.new_boolean(true)), .push_true => try self.env.locals.push_one(try self.env.new_boolean(true)),
.push_false => try self.env.locals.push_one(try self.env.new_boolean(false)), .push_false => try self.env.locals.push_one(try self.env.new_boolean(false)),

View File

@ -118,11 +118,9 @@ pub const Expression = union (enum) {
invoke: struct { invoke: struct {
object_expression: *Expression, object_expression: *Expression,
argument_expressions: List, argument_expressions: ExpressionList,
}, },
const List = coral.list.Stack(Expression);
const TableLiteral = coral.list.Stack(struct { const TableLiteral = coral.list.Stack(struct {
key_expression: Expression, key_expression: Expression,
value_expression: Expression, value_expression: Expression,
@ -131,30 +129,29 @@ pub const Expression = union (enum) {
const ExpressionBuilder = fn (self: *Tree) ParseError!Expression; const ExpressionBuilder = fn (self: *Tree) ParseError!Expression;
pub const ExpressionList = coral.list.Stack(Expression);
pub const ParseError = error { pub const ParseError = error {
OutOfMemory, OutOfMemory,
BadSyntax, BadSyntax,
}; };
pub const Statement = union (enum) { pub const Statement = union (enum) {
@"return": ?Expression, @"return": struct {
expression: ?Expression,
@"if": struct {
condition_expression: Expression,
block_statements: List,
else_statement: ?*Statement,
}, },
@"while": struct { @"if": ConditionalBlock,
condition_expression: Expression, @"while": ConditionalBlock,
block_statements: List,
},
block: List,
expression: Expression, expression: Expression,
const List = coral.list.Stack(Statement); const ConditionalBlock = struct {
condition_expression: Expression,
block_statements: StatementList,
}; };
};
pub const StatementList = coral.list.Stack(Statement);
pub const Tree = struct { pub const Tree = struct {
name: []const coral.io.Byte, name: []const coral.io.Byte,
@ -162,7 +159,7 @@ pub const Tree = struct {
arena: coral.arena.Stacking, arena: coral.arena.Stacking,
error_buffer: coral.list.ByteStack, error_buffer: coral.list.ByteStack,
tokenizer: tokens.Tokenizer, tokenizer: tokens.Tokenizer,
parsed_statements: Statement.List, parsed_statements: StatementList,
has_returned: bool, has_returned: bool,
pub fn error_message(self: Tree) []const coral.io.Byte { pub fn error_message(self: Tree) []const coral.io.Byte {
@ -179,7 +176,7 @@ pub const Tree = struct {
return .{ return .{
.arena = coral.arena.Stacking.make(allocator, 4096), .arena = coral.arena.Stacking.make(allocator, 4096),
.error_buffer = coral.list.ByteStack.make(allocator), .error_buffer = coral.list.ByteStack.make(allocator),
.parsed_statements = Statement.List.make(allocator), .parsed_statements = StatementList.make(allocator),
.tokenizer = .{.source = ""}, .tokenizer = .{.source = ""},
.allocator = allocator, .allocator = allocator,
.name = ast_name, .name = ast_name,
@ -187,19 +184,19 @@ pub const Tree = struct {
}; };
} }
pub fn parse(self: *Tree, data: []const coral.io.Byte) ParseError![]const Statement { pub fn parse(self: *Tree, data: []const coral.io.Byte) ParseError!*const StatementList {
self.free();
self.tokenizer = .{.source = data}; self.tokenizer = .{.source = data};
self.has_returned = false; self.has_returned = false;
self.tokenizer.skip_newlines(); self.parsed_statements.free();
while (self.tokenizer.token != .end) { self.parsed_statements = try self.parse_statements();
try self.parsed_statements.push_one(try self.parse_statement());
if (self.tokenizer.token == .keyword_end) {
return self.report("unexpected `end` without matching `do` block");
} }
return self.parsed_statements.values; return &self.parsed_statements;
} }
const parse_additive = BinaryOperator.builder(parse_equality, &.{ const parse_additive = BinaryOperator.builder(parse_equality, &.{
@ -207,74 +204,6 @@ pub const Tree = struct {
.subtraction, .subtraction,
}); });
fn parse_branch(self: *Tree) ParseError!Statement {
const allocator = self.arena.as_allocator();
defer self.tokenizer.skip_newlines();
self.tokenizer.step();
const condition_expression = try self.parse_expression();
if (self.tokenizer.token != .symbol_colon) {
return self.report("expected `:` after `if` statement condition");
}
var statements = Statement.List.make(allocator);
self.tokenizer.skip_newlines();
while (true) {
switch (self.tokenizer.token) {
.keyword_end => {
return .{
.@"if" = .{
.condition_expression = condition_expression,
.block_statements = statements,
.else_statement = null,
},
};
},
.keyword_else => {
self.tokenizer.step();
if (self.tokenizer.token != .symbol_colon) {
return self.report("expected `:` after `if` statement condition");
}
var else_statements = Statement.List.make(allocator);
self.tokenizer.skip_newlines();
while (self.tokenizer.token != .keyword_end) {
try else_statements.push_one(try self.parse_statement());
}
return .{
.@"if" = .{
.else_statement = try coral.io.allocate_one(allocator, Statement{.block = else_statements}),
.condition_expression = condition_expression,
.block_statements = statements,
},
};
},
.keyword_elif => {
return .{
.@"if" = .{
.else_statement = try coral.io.allocate_one(allocator, try self.parse_branch()),
.condition_expression = condition_expression,
.block_statements = statements,
},
};
},
else => try statements.push_one(try self.parse_statement()),
}
}
}
const parse_comparison = BinaryOperator.builder(parse_term, &.{ const parse_comparison = BinaryOperator.builder(parse_term, &.{
.greater_than_comparison, .greater_than_comparison,
.greater_equals_comparison, .greater_equals_comparison,
@ -557,7 +486,7 @@ pub const Tree = struct {
}, },
.symbol_paren_left => { .symbol_paren_left => {
var argument_expressions = Expression.List.make(allocator); var argument_expressions = ExpressionList.make(allocator);
while (true) { while (true) {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
@ -597,21 +526,25 @@ pub const Tree = struct {
return expression; return expression;
} }
fn parse_statement(self: *Tree) ParseError!Statement { fn parse_statements(self: *Tree) ParseError!StatementList {
const allocator = self.arena.as_allocator(); var statements = StatementList.make(self.arena.as_allocator());
self.tokenizer.skip_newlines();
while (true) {
try statements.push_one(parse_statement: {
switch (self.tokenizer.token) { switch (self.tokenizer.token) {
.keyword_return => { .end, .keyword_end => return statements,
defer self.tokenizer.skip_newlines();
.keyword_return => {
if (self.has_returned) { if (self.has_returned) {
return self.report("multiple returns in lambda scope but expected only one"); return self.report("multiple returns in function scope but expected only one");
} }
self.tokenizer.step(); self.tokenizer.step();
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) { if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
return .{.@"return" = try self.parse_expression()}; break: parse_statement .{.@"return" = .{.expression = try self.parse_expression()}};
} }
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) { if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
@ -620,38 +553,66 @@ pub const Tree = struct {
self.has_returned = true; self.has_returned = true;
return .{.@"return" = null}; break: parse_statement .{.@"return" = .{.expression = null}};
}, },
.keyword_while => { .keyword_while => {
defer self.tokenizer.skip_newlines();
self.tokenizer.step(); self.tokenizer.step();
const condition_expression = try self.parse_expression(); const condition_expression = try self.parse_expression();
if (self.tokenizer.token != .symbol_colon) { if (self.tokenizer.token != .keyword_do) {
return self.report("expected `:` after `while` statement"); return self.report("expected `do` block after `while` statement");
} }
var statements = Statement.List.make(allocator); self.tokenizer.step();
self.tokenizer.skip_newlines(); const while_statement = Statement{
while (self.tokenizer.token != .keyword_end) {
try statements.push_one(try self.parse_statement());
}
return .{
.@"while" = .{ .@"while" = .{
.block_statements = statements, .block_statements = try self.parse_statements(),
.condition_expression = condition_expression, .condition_expression = condition_expression,
}, },
}; };
if (self.tokenizer.token != .keyword_end) {
return self.report("expected `end` after block");
}
self.tokenizer.skip_newlines();
break: parse_statement while_statement;
}, },
.keyword_if => return self.parse_branch(), .keyword_if => {
else => return .{.expression = try self.parse_expression()}, self.tokenizer.step();
const condition_expression = try self.parse_expression();
if (self.tokenizer.token != .keyword_do) {
return self.report("expected `do` block after `if` statement");
}
self.tokenizer.step();
const if_statement = Statement{
.@"if" = .{
.block_statements = try self.parse_statements(),
.condition_expression = condition_expression,
},
};
if (self.tokenizer.token != .keyword_end) {
return self.report("expected `end` after block");
}
self.tokenizer.skip_newlines();
break: parse_statement if_statement;
},
else => break: parse_statement .{.expression = try self.parse_expression()},
}
});
} }
} }

View File

@ -21,7 +21,7 @@ pub const Token = union(enum) {
symbol_bracket_left, symbol_bracket_left,
symbol_bracket_right, symbol_bracket_right,
symbol_period, symbol_period,
symbol_colon, symbol_lambda,
symbol_less_than, symbol_less_than,
symbol_less_equals, symbol_less_equals,
symbol_greater_than, symbol_greater_than,
@ -42,8 +42,6 @@ pub const Token = union(enum) {
keyword_do, keyword_do,
keyword_end, keyword_end,
keyword_while, keyword_while,
keyword_else,
keyword_elif,
pub fn text(self: Token) []const coral.io.Byte { pub fn text(self: Token) []const coral.io.Byte {
return switch (self) { return switch (self) {
@ -68,7 +66,7 @@ pub const Token = union(enum) {
.symbol_bracket_left => "[", .symbol_bracket_left => "[",
.symbol_bracket_right => "]", .symbol_bracket_right => "]",
.symbol_period => ".", .symbol_period => ".",
.symbol_colon => ":", .symbol_lambda => "=>",
.symbol_less_than => "<", .symbol_less_than => "<",
.symbol_less_equals => "<=", .symbol_less_equals => "<=",
.symbol_greater_than => ">", .symbol_greater_than => ">",
@ -89,8 +87,6 @@ pub const Token = union(enum) {
.keyword_do => "do", .keyword_do => "do",
.keyword_end => "end", .keyword_end => "end",
.keyword_while => "while", .keyword_while => "while",
.keyword_elif => "elif",
.keyword_else => "else",
}; };
} }
}; };
@ -98,7 +94,7 @@ pub const Token = union(enum) {
pub const Tokenizer = struct { pub const Tokenizer = struct {
source: []const coral.io.Byte, source: []const coral.io.Byte,
lines_stepped: usize = 1, lines_stepped: usize = 1,
token: Token = .newline, token: Token = .end,
pub fn skip_newlines(self: *Tokenizer) void { pub fn skip_newlines(self: *Tokenizer) void {
self.step(); self.step();
@ -194,18 +190,6 @@ pub const Tokenizer = struct {
}, },
'e' => { 'e' => {
if (coral.io.ends_with(identifier, "lse")) {
self.token = .keyword_else;
return;
}
if (coral.io.ends_with(identifier, "lif")) {
self.token = .keyword_elif;
return;
}
if (coral.io.ends_with(identifier, "nd")) { if (coral.io.ends_with(identifier, "nd")) {
self.token = .keyword_end; self.token = .keyword_end;
@ -394,13 +378,6 @@ pub const Tokenizer = struct {
return; return;
}, },
':' => {
self.token = .symbol_colon;
cursor += 1;
return;
},
'=' => { '=' => {
cursor += 1; cursor += 1;
@ -413,6 +390,13 @@ pub const Tokenizer = struct {
return; return;
}, },
'>' => {
cursor += 1;
self.token = .symbol_lambda;
return;
},
else => {}, else => {},
} }
} }