Make Local Declarations Explicit #38
|
@ -70,8 +70,18 @@ pub const RuntimeEnv = struct {
|
||||||
const OpcodeList = coral.list.Stack(Opcode);
|
const OpcodeList = coral.list.Stack(Opcode);
|
||||||
|
|
||||||
const CompilationUnit = struct {
|
const CompilationUnit = struct {
|
||||||
local_identifiers_buffer: [255][]const coral.io.Byte = [_][]const coral.io.Byte{""} ** 255,
|
locals_buffer: [255]Local = [_]Local{.{}} ** 255,
|
||||||
local_identifiers_count: u8 = 0,
|
locals_count: u8 = 0,
|
||||||
|
|
||||||
|
const ResolvedLocal = struct {
|
||||||
|
index: u8,
|
||||||
|
is_readonly: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Local = struct {
|
||||||
|
identifier: []const coral.io.Byte = "",
|
||||||
|
is_readonly: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
fn compile_expression(self: *CompilationUnit, chunk: *Chunk, expression: ast.Expression) RuntimeError!void {
|
fn compile_expression(self: *CompilationUnit, chunk: *Chunk, expression: ast.Expression) RuntimeError!void {
|
||||||
const number_format = coral.utf8.DecimalFormat{
|
const number_format = coral.utf8.DecimalFormat{
|
||||||
|
@ -204,20 +214,24 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
.local_get => |local_get| {
|
.local_get => |local_get| {
|
||||||
try chunk.opcodes.push_one(.{
|
try chunk.opcodes.push_one(.{
|
||||||
.push_local = self.resolve_local(local_get.identifier) orelse {
|
.push_local = (self.resolve_local(local_get.identifier) orelse {
|
||||||
return chunk.env.raise(error.OutOfMemory, "undefined local");
|
return chunk.env.raise(error.OutOfMemory, "undefined local");
|
||||||
},
|
}).index,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.local_set => |local_set| {
|
.local_set => |local_set| {
|
||||||
try self.compile_expression(chunk, local_set.value_expression.*);
|
try self.compile_expression(chunk, local_set.value_expression.*);
|
||||||
|
|
||||||
try chunk.opcodes.push_one(.{
|
const resolved_local = self.resolve_local(local_set.identifier) orelse {
|
||||||
.local_set = self.resolve_local(local_set.identifier) orelse {
|
|
||||||
return chunk.env.raise(error.BadSyntax, "undefined local");
|
return chunk.env.raise(error.BadSyntax, "undefined local");
|
||||||
},
|
};
|
||||||
});
|
|
||||||
|
if (resolved_local.is_readonly) {
|
||||||
|
return chunk.env.raise(error.BadSyntax, "cannot set a read-only declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
try chunk.opcodes.push_one(.{.local_set = resolved_local.index});
|
||||||
},
|
},
|
||||||
|
|
||||||
.field_get => |field_get| {
|
.field_get => |field_get| {
|
||||||
|
@ -266,15 +280,23 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.local_var => |local_var| {
|
.declare => |declare| {
|
||||||
try self.compile_expression(chunk, local_var.assigned_expression);
|
if (self.resolve_local(declare.identifier) != null) {
|
||||||
|
return chunk.env.raise(error.BadSyntax, "declaration shadows existing one");
|
||||||
|
}
|
||||||
|
|
||||||
if (self.local_identifiers_count == self.local_identifiers_buffer.len) {
|
try self.compile_expression(chunk, declare.assigned_expression);
|
||||||
|
|
||||||
|
if (self.locals_count == self.locals_buffer.len) {
|
||||||
return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals");
|
return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.local_identifiers_buffer[self.local_identifiers_count] = local_var.identifier;
|
self.locals_buffer[self.locals_count] = .{
|
||||||
self.local_identifiers_count += 1;
|
.identifier = declare.identifier,
|
||||||
|
.is_readonly = declare.storage != .variant,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.locals_count += 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
.block => |block| {
|
.block => |block| {
|
||||||
|
@ -326,16 +348,21 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?u8 {
|
fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?ResolvedLocal {
|
||||||
if (self.local_identifiers_count == 0) {
|
if (self.locals_count == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = @as(u8, self.local_identifiers_count - 1);
|
var index = @as(u8, self.locals_count - 1);
|
||||||
|
|
||||||
while (true) : (index -= 1) {
|
while (true) : (index -= 1) {
|
||||||
if (coral.io.are_equal(local_identifier, self.local_identifiers_buffer[index])) {
|
const local = &self.locals_buffer[index];
|
||||||
return index;
|
|
||||||
|
if (coral.io.are_equal(local_identifier, local.identifier)) {
|
||||||
|
return .{
|
||||||
|
.index = index,
|
||||||
|
.is_readonly = local.is_readonly,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
|
|
|
@ -129,6 +129,11 @@ pub const Expression = union (enum) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const DeclarationStorage = enum {
|
||||||
|
variant,
|
||||||
|
readonly,
|
||||||
|
};
|
||||||
|
|
||||||
const ExpressionBuilder = fn (self: *Tree) ParseError!Expression;
|
const ExpressionBuilder = fn (self: *Tree) ParseError!Expression;
|
||||||
|
|
||||||
pub const ParseError = error {
|
pub const ParseError = error {
|
||||||
|
@ -139,7 +144,8 @@ pub const ParseError = error {
|
||||||
pub const Statement = union (enum) {
|
pub const Statement = union (enum) {
|
||||||
@"return": ?Expression,
|
@"return": ?Expression,
|
||||||
|
|
||||||
local_var: struct {
|
declare: struct {
|
||||||
|
storage: DeclarationStorage,
|
||||||
identifier: []const coral.io.Byte,
|
identifier: []const coral.io.Byte,
|
||||||
assigned_expression: Expression,
|
assigned_expression: Expression,
|
||||||
},
|
},
|
||||||
|
@ -672,8 +678,34 @@ pub const Tree = struct {
|
||||||
self.tokenizer.skip_newlines();
|
self.tokenizer.skip_newlines();
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.local_var = .{
|
.declare = .{
|
||||||
.assigned_expression = try self.parse_expression(),
|
.assigned_expression = try self.parse_expression(),
|
||||||
|
.storage = .variant,
|
||||||
|
.identifier = identifier,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_let => {
|
||||||
|
self.tokenizer.skip_newlines();
|
||||||
|
|
||||||
|
const identifier = switch (self.tokenizer.token) {
|
||||||
|
.identifier => |identifier| identifier,
|
||||||
|
else => return self.report("expected identifier after `let` declaration statement"),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.tokenizer.skip_newlines();
|
||||||
|
|
||||||
|
if (self.tokenizer.token != .symbol_equals) {
|
||||||
|
return self.report("expected `=` after declaration identifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tokenizer.skip_newlines();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.declare = .{
|
||||||
|
.assigned_expression = try self.parse_expression(),
|
||||||
|
.storage = .readonly,
|
||||||
.identifier = identifier,
|
.identifier = identifier,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -45,6 +45,7 @@ pub const Token = union(enum) {
|
||||||
keyword_else,
|
keyword_else,
|
||||||
keyword_elif,
|
keyword_elif,
|
||||||
keyword_var,
|
keyword_var,
|
||||||
|
keyword_let,
|
||||||
|
|
||||||
pub fn text(self: Token) []const coral.io.Byte {
|
pub fn text(self: Token) []const coral.io.Byte {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
|
@ -93,6 +94,7 @@ pub const Token = union(enum) {
|
||||||
.keyword_elif => "elif",
|
.keyword_elif => "elif",
|
||||||
.keyword_else => "else",
|
.keyword_else => "else",
|
||||||
.keyword_var => "var",
|
.keyword_var => "var",
|
||||||
|
.keyword_let => "let",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -231,6 +233,14 @@ pub const Tokenizer = struct {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'l' => {
|
||||||
|
if (coral.io.ends_with(identifier, "et")) {
|
||||||
|
self.token = .keyword_let;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
'n' => {
|
'n' => {
|
||||||
if (coral.io.ends_with(identifier, "il")) {
|
if (coral.io.ends_with(identifier, "il")) {
|
||||||
self.token = .keyword_nil;
|
self.token = .keyword_nil;
|
||||||
|
|
Loading…
Reference in New Issue