206 lines
5.6 KiB
Zig
206 lines
5.6 KiB
Zig
const Expr = @import("./Expr.zig");
|
|
|
|
const Stmt = @import("./Stmt.zig");
|
|
|
|
const coral = @import("coral");
|
|
|
|
const tokens = @import("./tokens.zig");
|
|
|
|
pub const Declaration = struct {
|
|
identifier: []const coral.io.Byte,
|
|
|
|
is: packed struct {
|
|
readonly: bool = false,
|
|
captured: bool = false,
|
|
} = .{},
|
|
};
|
|
|
|
pub const Environment = struct {
|
|
captures: [capture_max]u8 = [_]u8{0} ** capture_max,
|
|
capture_count: u8 = 0,
|
|
declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
|
declaration_count: u8 = 0,
|
|
argument_count: u8 = 0,
|
|
statement: ?*const Stmt = null,
|
|
enclosing: ?*Environment = null,
|
|
|
|
const DeclareError = coral.io.AllocationError || error {
|
|
DeclarationExists,
|
|
};
|
|
|
|
const capture_max = coral.math.max_int(@typeInfo(u8).Int);
|
|
|
|
const declaration_max = coral.math.max_int(@typeInfo(u8).Int);
|
|
|
|
pub fn create_enclosed(self: *Environment, root: *Root) coral.io.AllocationError!*Environment {
|
|
return coral.io.allocate_one(root.arena.as_allocator(), Environment{
|
|
.enclosing = self,
|
|
});
|
|
}
|
|
|
|
fn declare(self: *Environment, declaration: Declaration) DeclareError!*const Declaration {
|
|
if (self.declaration_count == self.declarations.len) {
|
|
return error.OutOfMemory;
|
|
}
|
|
|
|
{
|
|
var environment = self;
|
|
|
|
while (true) {
|
|
var remaining_count = environment.declaration_count;
|
|
|
|
while (remaining_count != 0) {
|
|
remaining_count -= 1;
|
|
|
|
if (coral.io.are_equal(environment.declarations[remaining_count].identifier, declaration.identifier)) {
|
|
return error.DeclarationExists;
|
|
}
|
|
}
|
|
|
|
environment = environment.enclosing orelse break;
|
|
}
|
|
}
|
|
|
|
const declaration_slot = &self.declarations[self.declaration_count];
|
|
|
|
declaration_slot.* = declaration;
|
|
self.declaration_count += 1;
|
|
|
|
return declaration_slot;
|
|
}
|
|
|
|
pub fn declare_argument(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
|
coral.debug.assert(self.declaration_count <= self.argument_count);
|
|
|
|
defer self.argument_count += 1;
|
|
|
|
return self.declare(.{
|
|
.identifier = identifier,
|
|
.is = .{.readonly = true},
|
|
});
|
|
}
|
|
|
|
pub fn declare_constant(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
|
return self.declare(.{
|
|
.identifier = identifier,
|
|
.is = .{.readonly = true},
|
|
});
|
|
}
|
|
|
|
pub fn declare_variable(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
|
return self.declare(.{.identifier = identifier});
|
|
}
|
|
|
|
pub fn resolve_declaration(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
|
var environment = self;
|
|
var ancestry = @as(usize, 0);
|
|
|
|
while (true) : (ancestry += 1) {
|
|
var remaining_count = environment.declaration_count;
|
|
|
|
while (remaining_count != 0) {
|
|
remaining_count -= 1;
|
|
|
|
const declaration = &environment.declarations[remaining_count];
|
|
|
|
if (coral.io.are_equal(declaration.identifier, identifier)) {
|
|
if (ancestry != 0) {
|
|
declaration.is.captured = true;
|
|
environment = self;
|
|
|
|
while (ancestry != 0) : (ancestry -= 1) {
|
|
if (environment.capture_count == environment.captures.len) {
|
|
return error.OutOfMemory;
|
|
}
|
|
|
|
environment.captures[environment.capture_count] = remaining_count;
|
|
environment.capture_count += 1;
|
|
|
|
coral.debug.assert(environment.enclosing != null);
|
|
|
|
environment = environment.enclosing.?;
|
|
}
|
|
}
|
|
|
|
return declaration;
|
|
}
|
|
}
|
|
|
|
environment = environment.enclosing orelse return null;
|
|
}
|
|
}
|
|
};
|
|
|
|
pub const ParseError = coral.io.AllocationError || error {
|
|
BadSyntax,
|
|
};
|
|
|
|
pub const Root = struct {
|
|
arena: coral.arena.Stacking,
|
|
environment: Environment,
|
|
error_messages: MessageList,
|
|
|
|
const MessageList = coral.list.Stack([]coral.io.Byte);
|
|
|
|
pub fn report_error(self: *Root, stream: *tokens.Stream, comptime format: []const u8, args: anytype) ParseError {
|
|
const allocator = self.arena.as_allocator();
|
|
|
|
try self.error_messages.push_one(try coral.utf8.alloc_formatted(allocator, "{line}: {message}", .{
|
|
.message = try coral.utf8.alloc_formatted(allocator, format, args),
|
|
.line = stream.lines_stepped,
|
|
}));
|
|
|
|
return error.BadSyntax;
|
|
}
|
|
|
|
pub fn report_declare_error(self: *Root, stream: *tokens.Stream, identifier: []const coral.io.Byte, @"error": Environment.DeclareError) ParseError {
|
|
return switch (@"error") {
|
|
error.OutOfMemory => error.OutOfMemory,
|
|
|
|
error.DeclarationExists => self.report_error(stream, "declaration `{identifier}` already exists", .{
|
|
.identifier = identifier,
|
|
}),
|
|
};
|
|
}
|
|
|
|
pub fn create_expr(self: *Root, expr: Expr) coral.io.AllocationError!*Expr {
|
|
return coral.io.allocate_one(self.arena.as_allocator(), expr);
|
|
}
|
|
|
|
pub fn create_stmt(self: *Root, stmt: Stmt) coral.io.AllocationError!*Stmt {
|
|
return coral.io.allocate_one(self.arena.as_allocator(), stmt);
|
|
}
|
|
|
|
pub fn free(self: *Root) void {
|
|
self.error_messages.free();
|
|
self.arena.free();
|
|
}
|
|
|
|
pub fn make(allocator: coral.io.Allocator) coral.io.AllocationError!Root {
|
|
const arena_page_size = 4096;
|
|
|
|
return .{
|
|
.arena = coral.arena.Stacking.make(allocator, arena_page_size),
|
|
.error_messages = MessageList.make(allocator),
|
|
.environment = .{},
|
|
};
|
|
}
|
|
|
|
pub fn parse(self: *Root, stream: *tokens.Stream) ParseError!void {
|
|
stream.skip_newlines();
|
|
|
|
const first_statement = try Stmt.parse(self, stream, &self.environment);
|
|
var current_statement = first_statement;
|
|
|
|
while (stream.token != .end) {
|
|
const next_statement = try Stmt.parse(self, stream, &self.environment);
|
|
|
|
current_statement.next = next_statement;
|
|
current_statement = next_statement;
|
|
}
|
|
|
|
self.environment.statement = first_statement;
|
|
}
|
|
};
|
|
|