Consolidate variable operations into expressions

This commit is contained in:
kayomn 2023-07-30 23:18:43 +01:00
parent b6f7ab1edb
commit 3173dd52fe
3 changed files with 239 additions and 355 deletions

View File

@ -5,7 +5,7 @@ test = {
.message = "don't you lecture me with your 30 dollar scripting language" .message = "don't you lecture me with your 30 dollar scripting language"
} }
# test.message = "game is loading" test.message = "game is loading"
@log_info(test.message) @log_info(test.message)

View File

@ -18,7 +18,8 @@ pub const Expression = union (enum) {
symbol_literal: []const coral.io.Byte, symbol_literal: []const coral.io.Byte,
table_literal: TableLiteral, table_literal: TableLiteral,
grouped_expression: *Expression, grouped_expression: *Expression,
resolve_local: []const coral.io.Byte, get_local: []const coral.io.Byte,
set_local: []const coral.io.Byte,
get_field: struct { get_field: struct {
object_expression: *Expression, object_expression: *Expression,
@ -31,11 +32,6 @@ pub const Expression = union (enum) {
value_expression: *Expression, value_expression: *Expression,
}, },
call_system: struct {
identifier: []const coral.io.Byte,
argument_expressions: List,
},
binary_operation: struct { binary_operation: struct {
operator: BinaryOperator, operator: BinaryOperator,
lhs_expression: *Expression, lhs_expression: *Expression,
@ -91,18 +87,11 @@ pub const ParseError = error {
const Self = @This(); const Self = @This();
pub const Statement = union (enum) { pub const Statement = union (enum) {
return_nothing, @"return": struct {
return_expression: Expression, expression: ?Expression,
},
assign_local: struct {
identifier: []const coral.io.Byte,
expression: Expression, expression: Expression,
},
call_system: struct {
identifier: []const coral.io.Byte,
argument_expressions: Expression.List,
},
const List = coral.list.Stack(Statement); const List = coral.list.Stack(Statement);
}; };
@ -186,12 +175,12 @@ pub fn list_statements(self: Self) []const Statement {
pub fn parse(self: *Self, data: []const coral.io.Byte) ParseError!void { pub fn parse(self: *Self, data: []const coral.io.Byte) ParseError!void {
self.tokenizer = .{.source = data}; self.tokenizer = .{.source = data};
const allocator = self.arena.as_allocator();
var has_returned = false; var has_returned = false;
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
while (true) { while (true) {
try self.statements.push_one(parse_statement: {
switch (self.tokenizer.token) { switch (self.tokenizer.token) {
.end => return, .end => return,
@ -200,91 +189,43 @@ pub fn parse(self: *Self, data: []const coral.io.Byte) ParseError!void {
return self.report("multiple returns in function scope but expected only one"); return self.report("multiple returns in function scope but expected only one");
} }
try self.statements.push_one(get_statement: {
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) {
break: get_statement .{.return_expression = 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) {
return self.report("expected end or newline after return statement"); return self.report("expected end or newline after return statement");
} }
break: get_statement .return_nothing;
});
has_returned = true; has_returned = true;
break: parse_statement .{
.@"return" = .{
.expression = null,
},
};
}, },
.identifier => |identifier| { else => {
self.tokenizer.step(); break: parse_statement .{
.expression = try self.parse_expression()
switch (self.tokenizer.token) { };
.end, .newline => return self.report("statement has no effect"), },
.symbol_equals => {
self.tokenizer.step();
if (self.tokenizer.token == .end) {
return self.report("expected expression after `=`");
} }
try self.statements.push_one(.{
.assign_local = .{
.expression = try self.parse_expression(),
.identifier = identifier,
},
}); });
},
else => return self.report("expected `=` after local"),
}
},
.special_identifier => |identifier| {
self.tokenizer.step();
switch (self.tokenizer.token) {
.end, .newline => return self.report("system call is missing arguments"),
.symbol_paren_left => {
self.tokenizer.step();
var expressions_list = Expression.List.make(allocator);
while (true) {
if (self.tokenizer.token == .symbol_paren_right) {
break;
}
try expressions_list.push_one(try self.parse_expression());
switch (self.tokenizer.token) {
.symbol_comma => continue,
.symbol_paren_right => break,
else => return self.report("expected `)` or argument after `(`"),
} }
} }
self.tokenizer.skip_newlines(); const parse_additive = binary_operation_parser(parse_equality, &.{
.addition,
try self.statements.push_one(.{ .subtraction,
.call_system = .{
.argument_expressions = expressions_list,
.identifier = identifier,
},
}); });
},
else => return self.report("expected `=` after local"),
}
},
else => return self.report("invalid statement"),
}
}
}
const parse_comparison = binary_operation_parser(parse_term, &.{ const parse_comparison = binary_operation_parser(parse_term, &.{
.greater_than_comparison, .greater_than_comparison,
@ -297,15 +238,38 @@ const parse_equality = binary_operation_parser(parse_comparison, &.{
.equals_comparison, .equals_comparison,
}); });
const parse_expression = binary_operation_parser(parse_equality, &.{ pub fn parse_expression(self: *Self) ParseError!Expression {
.addition, const allocator = self.arena.as_allocator();
.subtraction, const expression = try parse_additive(self);
});
if (self.tokenizer.token == .symbol_equals) {
self.tokenizer.skip_newlines();
if (self.tokenizer.token == .end) {
return self.report("expected assignment after `=`");
}
return switch (expression) {
.get_local => |get_local| .{.set_local = get_local},
.get_field => |get_field| .{
.set_field = .{
.object_expression = get_field.object_expression,
.identifier = get_field.identifier,
.value_expression = try coral.io.allocate_one(allocator, try self.parse_expression()),
},
},
else => self.report("expected local or field on left-hand side of expression"),
};
}
return expression;
}
fn parse_factor(self: *Self) ParseError!Expression { fn parse_factor(self: *Self) ParseError!Expression {
const allocator = self.arena.as_allocator(); const allocator = self.arena.as_allocator();
var expression = @as(Expression, get: {
switch (self.tokenizer.token) { switch (self.tokenizer.token) {
.symbol_paren_left => { .symbol_paren_left => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
@ -322,88 +286,62 @@ fn parse_factor(self: *Self) ParseError!Expression {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get Expression{.grouped_expression = try coral.io.allocate_one(allocator, expression)}; return Expression{.grouped_expression = try coral.io.allocate_one(allocator, expression)};
}, },
.keyword_nil => { .keyword_nil => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .nil_literal; return .nil_literal;
}, },
.keyword_true => { .keyword_true => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .true_literal; return .true_literal;
}, },
.keyword_false => { .keyword_false => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .false_literal; return .false_literal;
}, },
.number => |value| { .number => |value| {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .{.number_literal = value}; return .{.number_literal = value};
}, },
.string => |value| { .string => |value| {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .{.string_literal = value}; return .{.string_literal = value};
}, },
.special_identifier => |identifier| { .identifier => |local_identifier| {
var expression = Expression{.get_local = local_identifier};
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
var expression_list = Expression.List.make(allocator); while (self.tokenizer.token == .symbol_period) {
while (true) {
switch (self.tokenizer.token) {
.end => return self.report("expected expression or `)` after `(`"),
.symbol_paren_right => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .{ expression = .{
.call_system = .{ .get_field = .{
.identifier = identifier, .identifier = switch (self.tokenizer.token) {
.argument_expressions = expression_list, .identifier => |field_identifier| field_identifier,
else => return self.report("expected identifier after `.`"),
},
.object_expression = try coral.io.allocate_one(allocator, expression),
}, },
}; };
},
else => {
try expression_list.push_one(try self.parse_expression());
switch (self.tokenizer.token) {
.end => return self.report("expected `,` or `)` after argument"),
.symbol_comma => continue,
.symbol_paren_right => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .{
.call_system = .{
.identifier = identifier,
.argument_expressions = expression_list,
},
};
},
else => return self.report("expected `,` or `)` after argument"),
} }
},
}
}
},
.identifier => |identifier| { return expression;
self.tokenizer.skip_newlines();
break: get .{.resolve_local = identifier};
}, },
.symbol_brace_left => { .symbol_brace_left => {
@ -416,7 +354,7 @@ fn parse_factor(self: *Self) ParseError!Expression {
.symbol_brace_right => { .symbol_brace_right => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .{.table_literal = table_literal}; return .{.table_literal = table_literal};
}, },
.symbol_bracket_left => { .symbol_bracket_left => {
@ -458,7 +396,7 @@ fn parse_factor(self: *Self) ParseError!Expression {
.symbol_brace_right => { .symbol_brace_right => {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: get .{.table_literal = table_literal}; return .{.table_literal = table_literal};
}, },
else => return self.report("expected `,` or `}` after expression"), else => return self.report("expected `,` or `}` after expression"),
@ -477,7 +415,7 @@ fn parse_factor(self: *Self) ParseError!Expression {
return self.report("expected expression after numeric negation (`-`)"); return self.report("expected expression after numeric negation (`-`)");
} }
break: get .{ return .{
.unary_operation = .{ .unary_operation = .{
.expression = try coral.io.allocate_one(allocator, try self.parse_factor()), .expression = try coral.io.allocate_one(allocator, try self.parse_factor()),
.operator = .numeric_negation, .operator = .numeric_negation,
@ -492,7 +430,7 @@ fn parse_factor(self: *Self) ParseError!Expression {
return self.report("expected expression after boolean negation (`!`)"); return self.report("expected expression after boolean negation (`!`)");
} }
break: get .{ return .{
.unary_operation = .{ .unary_operation = .{
.expression = try coral.io.allocate_one(allocator, try self.parse_factor()), .expression = try coral.io.allocate_one(allocator, try self.parse_factor()),
.operator = .boolean_negation, .operator = .boolean_negation,
@ -502,37 +440,6 @@ fn parse_factor(self: *Self) ParseError!Expression {
else => return self.report("unexpected token in expression"), else => return self.report("unexpected token in expression"),
} }
});
while (self.tokenizer.token == .symbol_period) {
self.tokenizer.skip_newlines();
const identifier = switch (self.tokenizer.token) {
.identifier => |identifier| identifier,
else => return self.report("expected identifier after `.`"),
};
self.tokenizer.skip_newlines();
expression = switch (self.tokenizer.token) {
.symbol_equals => .{
.set_field = .{
.value_expression = try coral.io.allocate_one(allocator, try self.parse_expression()),
.object_expression = try coral.io.allocate_one(allocator, expression),
.identifier = identifier,
},
},
else => .{
.get_field = .{
.object_expression = try coral.io.allocate_one(allocator, expression),
.identifier = identifier,
},
},
};
}
return expression;
} }
const parse_term = binary_operation_parser(parse_factor, &.{ const parse_term = binary_operation_parser(parse_factor, &.{

View File

@ -99,14 +99,22 @@ const AstCompiler = struct {
try self.compile_expression(grouped_expression.*); try self.compile_expression(grouped_expression.*);
}, },
.resolve_local => |local| { .get_local => |get_local| {
try self.chunk.append_opcode(.{ try self.chunk.append_opcode(.{
.push_local = self.resolve_local(local) orelse { .push_local = self.resolve_local(get_local) orelse {
return self.chunk.env.raise(error.OutOfMemory, "undefined local"); return self.chunk.env.raise(error.OutOfMemory, "undefined local");
}, },
}); });
}, },
.set_local => |set_local| {
if (self.resolve_local(set_local)) |index| {
try self.chunk.append_opcode(.{.set_local = index});
} else {
try self.declare_local(set_local);
}
},
.get_field => |get_field| { .get_field => |get_field| {
try self.compile_expression(get_field.object_expression.*); try self.compile_expression(get_field.object_expression.*);
@ -127,51 +135,20 @@ const AstCompiler = struct {
try self.compile_expression(set_field.value_expression.*); try self.compile_expression(set_field.value_expression.*);
try self.chunk.append_opcode(.set_dynamic); try self.chunk.append_opcode(.set_dynamic);
}, },
.call_system => |call| {
if (call.argument_expressions.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
return self.chunk.env.raise(error.OutOfMemory, "functions may receive a maximum of 255 locals");
}
for (call.argument_expressions.values) |argument_expression| {
try self.compile_expression(argument_expression);
}
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
try self.chunk.append_opcode(.{.syscall = @intCast(call.argument_expressions.values.len)});
try self.chunk.append_opcode(.pop);
},
} }
} }
fn compile_statement(self: *AstCompiler, statement: Ast.Statement) kym.RuntimeError!void { fn compile_statement(self: *AstCompiler, statement: Ast.Statement) kym.RuntimeError!void {
switch (statement) { switch (statement) {
.return_expression => |return_expression| try self.compile_expression(return_expression), .@"return" => |@"return"| {
.return_nothing => try self.chunk.append_opcode(.push_nil), if (@"return".expression) |expression| {
try self.compile_expression(expression);
.assign_local => |local| {
try self.compile_expression(local.expression);
if (self.resolve_local(local.identifier)) |index| {
try self.chunk.append_opcode(.{.set_local = index});
} else { } else {
try self.declare_local(local.identifier); try self.chunk.append_opcode(.push_nil);
} }
}, },
.call_system => |call| { .expression => |expression| try self.compile_expression(expression),
if (call.argument_expressions.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
return self.chunk.env.raise(error.OutOfMemory, "functions may receive a maximum of 255 locals");
}
for (call.argument_expressions.values) |argument_expression| {
try self.compile_expression(argument_expression);
}
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
try self.chunk.append_opcode(.{.syscall = @intCast(call.argument_expressions.values.len)});
try self.chunk.append_opcode(.pop);
}
} }
} }