feat: ast structure and utilities
This commit is contained in:
parent
662c38b360
commit
5268a53148
5 changed files with 970 additions and 5 deletions
311
src/Ast.zig
Normal file
311
src/Ast.zig
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
const std = @import("std");
|
||||
const ink = @import("root.zig");
|
||||
const Tokenizer = @import("tokenizer.zig").Tokenizer;
|
||||
const Render = @import("Ast/Render.zig");
|
||||
const Parse = @import("Parse.zig");
|
||||
const Ast = @This();
|
||||
|
||||
filename: []const u8,
|
||||
source: []const u8,
|
||||
root: ?*Node = null,
|
||||
errors: []Error,
|
||||
|
||||
pub const Node = struct {
|
||||
tag: Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
data: Data,
|
||||
|
||||
pub const Tag = enum {
|
||||
file,
|
||||
false_literal,
|
||||
true_literal,
|
||||
number_literal,
|
||||
string_literal,
|
||||
empty_string,
|
||||
identifier,
|
||||
add_expr,
|
||||
subtract_expr,
|
||||
multiply_expr,
|
||||
divide_expr,
|
||||
mod_expr,
|
||||
negate_expr,
|
||||
logical_equality_expr,
|
||||
logical_inequality_expr,
|
||||
logical_and_expr,
|
||||
logical_or_expr,
|
||||
logical_not_expr,
|
||||
logical_greater_expr,
|
||||
logical_greater_or_equal_expr,
|
||||
logical_lesser_or_equal_expr,
|
||||
logical_lesser_expr,
|
||||
call_expr,
|
||||
choice_expr,
|
||||
choice_start_expr,
|
||||
choice_option_expr,
|
||||
choice_inner_expr,
|
||||
divert_expr,
|
||||
selector_expr,
|
||||
assign_stmt,
|
||||
block_stmt,
|
||||
content_stmt,
|
||||
divert_stmt,
|
||||
return_stmt,
|
||||
expr_stmt,
|
||||
choice_stmt,
|
||||
choice_star_stmt,
|
||||
choice_plus_stmt,
|
||||
gather_point_stmt,
|
||||
gathered_stmt,
|
||||
function_prototype,
|
||||
stitch_prototype,
|
||||
knot_prototype,
|
||||
function_decl,
|
||||
stitch_decl,
|
||||
knot_decl,
|
||||
const_decl,
|
||||
var_decl,
|
||||
list_decl,
|
||||
temp_decl,
|
||||
parameter_decl,
|
||||
ref_parameter_decl,
|
||||
string_expr,
|
||||
argument_list,
|
||||
parameter_list,
|
||||
switch_stmt,
|
||||
switch_case,
|
||||
if_stmt,
|
||||
multi_if_stmt,
|
||||
if_branch,
|
||||
else_branch,
|
||||
content,
|
||||
// TODO: Rename to SubstitutionExpr
|
||||
inline_logic_expr,
|
||||
invalid,
|
||||
};
|
||||
|
||||
pub const Data = union {
|
||||
leaf: void,
|
||||
bin: struct {
|
||||
lhs: ?*Node,
|
||||
rhs: ?*Node,
|
||||
},
|
||||
list: struct {
|
||||
items: ?[]*Node,
|
||||
},
|
||||
choice_expr: struct {
|
||||
start_expr: ?*Node,
|
||||
option_expr: ?*Node,
|
||||
inner_expr: ?*Node,
|
||||
},
|
||||
switch_stmt: struct {
|
||||
condition_expr: ?*Node,
|
||||
cases: ?[]*Node,
|
||||
},
|
||||
knot_decl: struct {
|
||||
prototype: *Node,
|
||||
children: ?[]*Node,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
pub const Error = struct {
|
||||
tag: Tag,
|
||||
loc: Location,
|
||||
|
||||
pub const Location = struct {
|
||||
start: usize,
|
||||
end: usize,
|
||||
};
|
||||
|
||||
pub const Tag = enum {
|
||||
panic,
|
||||
unexpected_token,
|
||||
unknown_identifier,
|
||||
assignment_to_const,
|
||||
expected_newline,
|
||||
expected_expression,
|
||||
expected_double_quote,
|
||||
expected_identifier,
|
||||
invalid_expression,
|
||||
invalid_lvalue,
|
||||
too_many_arguments,
|
||||
too_many_parameters,
|
||||
};
|
||||
};
|
||||
|
||||
fn create(
|
||||
gpa: std.mem.Allocator,
|
||||
tag: Node.Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
) !*Node {
|
||||
const node = try gpa.create(Node);
|
||||
node.* = .{
|
||||
.tag = tag,
|
||||
.source_start = source_start,
|
||||
.source_end = source_end,
|
||||
.data = undefined,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn createLeafNode(
|
||||
gpa: std.mem.Allocator,
|
||||
tag: Node.Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
) !*Node {
|
||||
const node = try Ast.create(gpa, tag, source_start, source_end);
|
||||
node.*.data = .{ .leaf = undefined };
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn createBinaryNode(
|
||||
gpa: std.mem.Allocator,
|
||||
tag: Node.Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
lhs: ?*Node,
|
||||
rhs: ?*Node,
|
||||
) !*Node {
|
||||
const node = try Ast.create(gpa, tag, source_start, source_end);
|
||||
node.*.data = .{
|
||||
.bin = .{
|
||||
.lhs = lhs,
|
||||
.rhs = rhs,
|
||||
},
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn createListNode(
|
||||
gpa: std.mem.Allocator,
|
||||
tag: Node.Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
items: ?[]*Node,
|
||||
) !*Node {
|
||||
const node = try Ast.create(gpa, tag, source_start, source_end);
|
||||
node.*.data = .{
|
||||
.list = .{ .items = items },
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn createChoiceExprNode(
|
||||
gpa: std.mem.Allocator,
|
||||
tag: Node.Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
start_expr: ?*Node,
|
||||
option_expr: ?*Node,
|
||||
inner_expr: ?*Node,
|
||||
) !*Node {
|
||||
const node = try Ast.create(gpa, tag, source_start, source_end);
|
||||
node.*.data = .{
|
||||
.choice_expr = .{
|
||||
.start_expr = start_expr,
|
||||
.option_expr = option_expr,
|
||||
.inner_expr = inner_expr,
|
||||
},
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn createSwitchNode(
|
||||
gpa: std.mem.Allocator,
|
||||
tag: Node.Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
condition_expr: ?*Node,
|
||||
cases_list: ?[]*Node,
|
||||
) !*Node {
|
||||
const node = try Ast.create(gpa, tag, source_start, source_end);
|
||||
node.*.data = .{
|
||||
.switch_stmt = .{
|
||||
.condition_expr = condition_expr,
|
||||
.cases = cases_list,
|
||||
},
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn createKnotDeclNode(
|
||||
gpa: std.mem.Allocator,
|
||||
tag: Node.Tag,
|
||||
source_start: usize,
|
||||
source_end: usize,
|
||||
prototype: *Node,
|
||||
children: ?[]*Node,
|
||||
) !*Node {
|
||||
const node = try Ast.create(gpa, tag, source_start, source_end);
|
||||
node.*.data = .{
|
||||
.knot_decl = .{
|
||||
.prototype = prototype,
|
||||
.children = children,
|
||||
},
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn parse(
|
||||
gpa: std.mem.Allocator,
|
||||
source: [:0]const u8,
|
||||
filename: []const u8,
|
||||
_: u32,
|
||||
) !Ast {
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_allocator.deinit();
|
||||
|
||||
var parser: Parse = .{
|
||||
.gpa = gpa,
|
||||
.arena = arena_allocator.allocator(),
|
||||
.tokenizer = Tokenizer.init(source),
|
||||
.token = .{
|
||||
.tag = .invalid,
|
||||
.loc = .{ .start = 0, .end = 0 },
|
||||
},
|
||||
.scratch = .empty,
|
||||
.grammar_stack = .empty,
|
||||
.block_stack = .empty,
|
||||
.choice_stack = .empty,
|
||||
.errors = .empty,
|
||||
.panic_mode = false,
|
||||
.flags = 0,
|
||||
.max_parse_depth = 128,
|
||||
.max_argument_count = 128,
|
||||
};
|
||||
defer parser.deinit();
|
||||
|
||||
const root = try parser.parseFile();
|
||||
return Ast{
|
||||
.filename = filename,
|
||||
.source = source,
|
||||
.root = root,
|
||||
.errors = try parser.errors.toOwnedSlice(gpa),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
ast: Ast,
|
||||
gpa: std.mem.Allocator,
|
||||
writer: *std.Io.Writer,
|
||||
options: Render.Options,
|
||||
) !void {
|
||||
return Render.renderTree(gpa, writer, &ast, options);
|
||||
}
|
||||
|
||||
pub fn renderErrors(
|
||||
ast: Ast,
|
||||
gpa: std.mem.Allocator,
|
||||
writer: *std.Io.Writer,
|
||||
options: Render.Options,
|
||||
) !void {
|
||||
return Render.renderErrors(gpa, writer, &ast, options);
|
||||
}
|
||||
|
||||
pub fn deinit(ast: *Ast, gpa: std.mem.Allocator) void {
|
||||
gpa.free(ast.errors);
|
||||
ast.* = undefined;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue