|
|
|
@ -118,9 +118,11 @@ pub const Expression = union (enum) {
|
|
|
|
|
|
|
|
|
|
invoke: struct {
|
|
|
|
|
object_expression: *Expression,
|
|
|
|
|
argument_expressions: ExpressionList,
|
|
|
|
|
argument_expressions: List,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
const List = coral.list.Stack(Expression);
|
|
|
|
|
|
|
|
|
|
const TableLiteral = coral.list.Stack(struct {
|
|
|
|
|
key_expression: Expression,
|
|
|
|
|
value_expression: Expression,
|
|
|
|
@ -129,8 +131,6 @@ pub const Expression = union (enum) {
|
|
|
|
|
|
|
|
|
|
const ExpressionBuilder = fn (self: *Tree) ParseError!Expression;
|
|
|
|
|
|
|
|
|
|
pub const ExpressionList = coral.list.Stack(Expression);
|
|
|
|
|
|
|
|
|
|
pub const ParseError = error {
|
|
|
|
|
OutOfMemory,
|
|
|
|
|
BadSyntax,
|
|
|
|
@ -140,33 +140,29 @@ pub const Statement = union (enum) {
|
|
|
|
|
@"return": ?Expression,
|
|
|
|
|
|
|
|
|
|
@"if": struct {
|
|
|
|
|
then_block: ConditionalBlock,
|
|
|
|
|
|
|
|
|
|
else_block: ?struct {
|
|
|
|
|
condition_expression: ?Expression,
|
|
|
|
|
block_statements: StatementList,
|
|
|
|
|
},
|
|
|
|
|
condition_expression: Expression,
|
|
|
|
|
block_statements: List,
|
|
|
|
|
else_statement: ?*Statement,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
@"while": ConditionalBlock,
|
|
|
|
|
block: StatementList,
|
|
|
|
|
@"while": struct {
|
|
|
|
|
condition_expression: Expression,
|
|
|
|
|
block_statements: List,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
block: List,
|
|
|
|
|
expression: Expression,
|
|
|
|
|
|
|
|
|
|
const ConditionalBlock = struct {
|
|
|
|
|
condition_expression: Expression,
|
|
|
|
|
block_statements: StatementList,
|
|
|
|
|
};
|
|
|
|
|
const List = coral.list.Stack(Statement);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub const StatementList = coral.list.Stack(Statement);
|
|
|
|
|
|
|
|
|
|
pub const Tree = struct {
|
|
|
|
|
name: []const coral.io.Byte,
|
|
|
|
|
allocator: coral.io.Allocator,
|
|
|
|
|
arena: coral.arena.Stacking,
|
|
|
|
|
error_buffer: coral.list.ByteStack,
|
|
|
|
|
tokenizer: tokens.Tokenizer,
|
|
|
|
|
parsed_statements: StatementList,
|
|
|
|
|
parsed_statements: Statement.List,
|
|
|
|
|
has_returned: bool,
|
|
|
|
|
|
|
|
|
|
pub fn error_message(self: Tree) []const coral.io.Byte {
|
|
|
|
@ -183,7 +179,7 @@ pub const Tree = struct {
|
|
|
|
|
return .{
|
|
|
|
|
.arena = coral.arena.Stacking.make(allocator, 4096),
|
|
|
|
|
.error_buffer = coral.list.ByteStack.make(allocator),
|
|
|
|
|
.parsed_statements = StatementList.make(allocator),
|
|
|
|
|
.parsed_statements = Statement.List.make(allocator),
|
|
|
|
|
.tokenizer = .{.source = ""},
|
|
|
|
|
.allocator = allocator,
|
|
|
|
|
.name = ast_name,
|
|
|
|
@ -191,19 +187,19 @@ pub const Tree = struct {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn parse(self: *Tree, data: []const coral.io.Byte) ParseError!*const StatementList {
|
|
|
|
|
pub fn parse(self: *Tree, data: []const coral.io.Byte) ParseError![]const Statement {
|
|
|
|
|
self.free();
|
|
|
|
|
|
|
|
|
|
self.tokenizer = .{.source = data};
|
|
|
|
|
self.has_returned = false;
|
|
|
|
|
|
|
|
|
|
self.parsed_statements.free();
|
|
|
|
|
self.tokenizer.skip_newlines();
|
|
|
|
|
|
|
|
|
|
self.parsed_statements = try self.parse_statements();
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token == .keyword_end) {
|
|
|
|
|
return self.report("unexpected `end` without matching `do` block");
|
|
|
|
|
while (self.tokenizer.token != .end) {
|
|
|
|
|
try self.parsed_statements.push_one(try self.parse_statement());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &self.parsed_statements;
|
|
|
|
|
return self.parsed_statements.values;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parse_additive = BinaryOperator.builder(parse_equality, &.{
|
|
|
|
@ -211,6 +207,74 @@ pub const Tree = struct {
|
|
|
|
|
.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, &.{
|
|
|
|
|
.greater_than_comparison,
|
|
|
|
|
.greater_equals_comparison,
|
|
|
|
@ -493,7 +557,7 @@ pub const Tree = struct {
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.symbol_paren_left => {
|
|
|
|
|
var argument_expressions = ExpressionList.make(allocator);
|
|
|
|
|
var argument_expressions = Expression.List.make(allocator);
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
self.tokenizer.skip_newlines();
|
|
|
|
@ -533,124 +597,61 @@ pub const Tree = struct {
|
|
|
|
|
return expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_statements(self: *Tree) ParseError!StatementList {
|
|
|
|
|
fn parse_statement(self: *Tree) ParseError!Statement {
|
|
|
|
|
const allocator = self.arena.as_allocator();
|
|
|
|
|
var statements = StatementList.make(allocator);
|
|
|
|
|
|
|
|
|
|
self.tokenizer.skip_newlines();
|
|
|
|
|
switch (self.tokenizer.token) {
|
|
|
|
|
.keyword_return => {
|
|
|
|
|
defer self.tokenizer.skip_newlines();
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
try statements.push_one(parse_statement: {
|
|
|
|
|
switch (self.tokenizer.token) {
|
|
|
|
|
.end, .keyword_end, .keyword_else, .keyword_elif => return statements,
|
|
|
|
|
|
|
|
|
|
.keyword_return => {
|
|
|
|
|
if (self.has_returned) {
|
|
|
|
|
return self.report("multiple returns in function scope but expected only one");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.tokenizer.step();
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
|
|
|
|
|
break: parse_statement .{.@"return" = try self.parse_expression()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
|
|
|
|
|
return self.report("expected end or newline after return statement");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.has_returned = true;
|
|
|
|
|
|
|
|
|
|
break: parse_statement .{.@"return" = null};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.keyword_while => {
|
|
|
|
|
self.tokenizer.step();
|
|
|
|
|
|
|
|
|
|
const condition_expression = try self.parse_expression();
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .symbol_colon) {
|
|
|
|
|
return self.report("expected `:` after `while` statement");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const while_statement = Statement{
|
|
|
|
|
.@"while" = .{
|
|
|
|
|
.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 while_statement;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.keyword_if => {
|
|
|
|
|
self.tokenizer.step();
|
|
|
|
|
|
|
|
|
|
const then_condition_expression = try self.parse_expression();
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .symbol_colon) {
|
|
|
|
|
return self.report("expected `:` after `if` statement condition");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var if_statement = Statement{
|
|
|
|
|
.@"if" = .{
|
|
|
|
|
.then_block = .{
|
|
|
|
|
.block_statements = try self.parse_statements(),
|
|
|
|
|
.condition_expression = then_condition_expression,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.else_block = null,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switch (self.tokenizer.token) {
|
|
|
|
|
.keyword_end => {},
|
|
|
|
|
|
|
|
|
|
.keyword_else => {
|
|
|
|
|
self.tokenizer.step();
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .symbol_colon) {
|
|
|
|
|
return self.report("expected newline after `else` statement");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if_statement.@"if".else_block = .{
|
|
|
|
|
.condition_expression = null,
|
|
|
|
|
.block_statements = try self.parse_statements(),
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.keyword_elif => {
|
|
|
|
|
self.tokenizer.step();
|
|
|
|
|
|
|
|
|
|
const else_condition_expression = try self.parse_expression();
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .symbol_colon) {
|
|
|
|
|
return self.report("expected newline after `elif` statement condition");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if_statement.@"if".else_block = .{
|
|
|
|
|
.condition_expression = else_condition_expression,
|
|
|
|
|
.block_statements = try self.parse_statements(),
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
else => return self.report("expected closing `end`, `elif`, or `else` statement on if block"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.tokenizer.skip_newlines();
|
|
|
|
|
|
|
|
|
|
break: parse_statement if_statement;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
else => break: parse_statement .{.expression = try self.parse_expression()},
|
|
|
|
|
if (self.has_returned) {
|
|
|
|
|
return self.report("multiple returns in lambda scope but expected only one");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
self.tokenizer.step();
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
|
|
|
|
|
return .{.@"return" = try self.parse_expression()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
|
|
|
|
|
return self.report("expected end or newline after return statement");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.has_returned = true;
|
|
|
|
|
|
|
|
|
|
return .{.@"return" = null};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.keyword_while => {
|
|
|
|
|
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 `while` statement");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var statements = Statement.List.make(allocator);
|
|
|
|
|
|
|
|
|
|
self.tokenizer.skip_newlines();
|
|
|
|
|
|
|
|
|
|
while (self.tokenizer.token != .keyword_end) {
|
|
|
|
|
try statements.push_one(try self.parse_statement());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return .{
|
|
|
|
|
.@"while" = .{
|
|
|
|
|
.block_statements = statements,
|
|
|
|
|
.condition_expression = condition_expression,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
.keyword_if => return self.parse_branch(),
|
|
|
|
|
else => return .{.expression = try self.parse_expression()},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|