ir
This commit is contained in:
parent
15a7df3f14
commit
48c3b5e224
17
.vscode/launch.json
vendored
17
.vscode/launch.json
vendored
@ -5,13 +5,22 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"type": "cppvsdbg",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug",
|
"name": "Launch",
|
||||||
"program": "${workspaceFolder}/zig-out/bin/compiler.exe",
|
"program": "${workspaceFolder}/zig-out/bin/compiler",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"preLaunchTask": "zig build"
|
"preLaunchTask": "zig build"
|
||||||
}
|
},
|
||||||
|
// {
|
||||||
|
// "type": "cppvsdbg",
|
||||||
|
// "request": "launch",
|
||||||
|
// "name": "Debug",
|
||||||
|
// "program": "${workspaceFolder}/zig-out/bin/compiler.exe",
|
||||||
|
// "args": [],
|
||||||
|
// "cwd": "${workspaceFolder}",
|
||||||
|
// "preLaunchTask": "zig build"
|
||||||
|
// }
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -21,6 +21,7 @@ const syntactic_analyzer = @import("frontend/syntactic_analyzer.zig");
|
|||||||
const Node = syntactic_analyzer.Node;
|
const Node = syntactic_analyzer.Node;
|
||||||
const semantic_analyzer = @import("frontend/semantic_analyzer.zig");
|
const semantic_analyzer = @import("frontend/semantic_analyzer.zig");
|
||||||
const intermediate_representation = @import("backend/intermediate_representation.zig");
|
const intermediate_representation = @import("backend/intermediate_representation.zig");
|
||||||
|
const emit = @import("backend/emit.zig");
|
||||||
|
|
||||||
test {
|
test {
|
||||||
_ = lexical_analyzer;
|
_ = lexical_analyzer;
|
||||||
@ -59,7 +60,8 @@ pub fn init(allocator: Allocator) !*Compilation {
|
|||||||
|
|
||||||
pub const Struct = struct {
|
pub const Struct = struct {
|
||||||
scope: Scope.Index,
|
scope: Scope.Index,
|
||||||
initialization: Value.Index,
|
fields: ArrayList(Field.Index) = .{},
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
@ -69,6 +71,7 @@ pub const Type = union(enum) {
|
|||||||
noreturn,
|
noreturn,
|
||||||
bool,
|
bool,
|
||||||
integer: Integer,
|
integer: Integer,
|
||||||
|
@"struct": Struct.Index,
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
@ -85,16 +88,29 @@ pub const Integer = struct {
|
|||||||
/// A scope contains a bunch of declarations
|
/// A scope contains a bunch of declarations
|
||||||
pub const Scope = struct {
|
pub const Scope = struct {
|
||||||
parent: Scope.Index,
|
parent: Scope.Index,
|
||||||
type: Type.Index,
|
type: Type.Index = Type.Index.invalid,
|
||||||
declarations: AutoHashMap(u32, Declaration.Index) = .{},
|
declarations: AutoHashMap(u32, Declaration.Index) = .{},
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Declaration = union(enum) {
|
pub const ScopeType = enum(u1) {
|
||||||
unresolved: Node.Index,
|
local = 0,
|
||||||
struct_type: Struct,
|
global = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Mutability = enum(u1) {
|
||||||
|
@"const",
|
||||||
|
@"var",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Declaration = struct {
|
||||||
|
scope_type: ScopeType,
|
||||||
|
mutability: Mutability,
|
||||||
|
init_value: Value.Index,
|
||||||
|
name: []const u8,
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
@ -111,12 +127,17 @@ pub const Function = struct {
|
|||||||
pub const Index = Prototype.List.Index;
|
pub const Index = Prototype.List.Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn getBodyBlock(function: Function, module: *Module) *Block {
|
||||||
|
return module.blocks.get(function.body);
|
||||||
|
}
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Block = struct {
|
pub const Block = struct {
|
||||||
foo: u32 = 0,
|
statements: ArrayList(Value.Index) = .{},
|
||||||
|
reaches_end: bool,
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
@ -129,27 +150,61 @@ pub const Field = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Loop = struct {
|
pub const Loop = struct {
|
||||||
foo: u32 = 0,
|
condition: Value.Index,
|
||||||
|
body: Value.Index,
|
||||||
|
breaks: bool,
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Value = struct {
|
const Runtime = struct {
|
||||||
type: union(enum) {
|
foo: u32 = 0,
|
||||||
declaration: Declaration.Index,
|
};
|
||||||
bool_true,
|
|
||||||
bool_false,
|
const Unresolved = struct {
|
||||||
loop: Loop.Index,
|
node_index: Node.Index,
|
||||||
function: Function.Index,
|
};
|
||||||
},
|
|
||||||
is_const: bool,
|
pub const Assignment = struct {
|
||||||
is_comptime: bool,
|
store: Value.Index,
|
||||||
|
load: Value.Index,
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Value = union(enum) {
|
||||||
|
unresolved: Unresolved,
|
||||||
|
declaration: Declaration.Index,
|
||||||
|
void,
|
||||||
|
bool: bool,
|
||||||
|
undefined,
|
||||||
|
loop: Loop.Index,
|
||||||
|
function: Function.Index,
|
||||||
|
block: Block.Index,
|
||||||
|
runtime: Runtime,
|
||||||
|
assign: Assignment.Index,
|
||||||
|
type: Type.Index,
|
||||||
|
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
|
||||||
|
pub fn isComptime(value: Value) bool {
|
||||||
|
return switch (value) {
|
||||||
|
.bool, .void, .undefined, .function => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getType(value: *Value) !void {
|
||||||
|
switch (value.*) {
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Module = struct {
|
pub const Module = struct {
|
||||||
main_package: *Package,
|
main_package: *Package,
|
||||||
import_table: StringArrayHashMap(*File) = .{},
|
import_table: StringArrayHashMap(*File) = .{},
|
||||||
@ -165,6 +220,7 @@ pub const Module = struct {
|
|||||||
types: BlockList(Type) = .{},
|
types: BlockList(Type) = .{},
|
||||||
blocks: BlockList(Block) = .{},
|
blocks: BlockList(Block) = .{},
|
||||||
loops: BlockList(Loop) = .{},
|
loops: BlockList(Loop) = .{},
|
||||||
|
assignments: BlockList(Assignment) = .{},
|
||||||
|
|
||||||
pub const Descriptor = struct {
|
pub const Descriptor = struct {
|
||||||
main_package_path: []const u8,
|
main_package_path: []const u8,
|
||||||
@ -354,7 +410,12 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
|
|||||||
|
|
||||||
const main_declaration = try semantic_analyzer.initialize(compilation, module, packages[0]);
|
const main_declaration = try semantic_analyzer.initialize(compilation, module, packages[0]);
|
||||||
|
|
||||||
try intermediate_representation.initialize(compilation, module, packages[0], main_declaration);
|
var ir = try intermediate_representation.initialize(compilation, module, packages[0], main_declaration);
|
||||||
|
|
||||||
|
switch (@import("builtin").cpu.arch) {
|
||||||
|
.x86_64 => |arch| try emit.get(arch).initialize(compilation.base_allocator, &ir),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generateAST() !void {}
|
fn generateAST() !void {}
|
||||||
|
@ -6,7 +6,13 @@ const assert = std.debug.assert;
|
|||||||
const expect = std.testing.expect;
|
const expect = std.testing.expect;
|
||||||
const expectEqual = std.testing.expectEqual;
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
|
||||||
const ir = @import("ir.zig");
|
const ir = @import("intermediate_representation.zig");
|
||||||
|
|
||||||
|
const data_structures = @import("../data_structures.zig");
|
||||||
|
const ArrayList = data_structures.ArrayList;
|
||||||
|
const AutoHashMap = data_structures.AutoHashMap;
|
||||||
|
|
||||||
|
const jit_callconv = .SysV;
|
||||||
|
|
||||||
const Section = struct {
|
const Section = struct {
|
||||||
content: []align(page_size) u8,
|
content: []align(page_size) u8,
|
||||||
@ -39,8 +45,13 @@ const Result = struct {
|
|||||||
const windows = std.os.windows;
|
const windows = std.os.windows;
|
||||||
break :blk @as([*]align(0x1000) u8, @ptrCast(@alignCast(try windows.VirtualAlloc(null, size, windows.MEM_COMMIT | windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE))))[0..size];
|
break :blk @as([*]align(0x1000) u8, @ptrCast(@alignCast(try windows.VirtualAlloc(null, size, windows.MEM_COMMIT | windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE))))[0..size];
|
||||||
},
|
},
|
||||||
.linux => blk: {
|
.linux, .macos => |os_tag| blk: {
|
||||||
const protection_flags = std.os.PROT.READ | std.os.PROT.WRITE | if (flags.executable) std.os.PROT.EXEC else 0;
|
const execute_flag: switch (os_tag) {
|
||||||
|
.linux => u32,
|
||||||
|
.macos => c_int,
|
||||||
|
else => unreachable,
|
||||||
|
} = if (flags.executable) std.os.PROT.EXEC else 0;
|
||||||
|
const protection_flags: u32 = @intCast(std.os.PROT.READ | std.os.PROT.WRITE | execute_flag);
|
||||||
const mmap_flags = std.os.MAP.ANONYMOUS | std.os.MAP.PRIVATE;
|
const mmap_flags = std.os.MAP.ANONYMOUS | std.os.MAP.PRIVATE;
|
||||||
|
|
||||||
break :blk std.os.mmap(null, size, protection_flags, mmap_flags, -1, 0);
|
break :blk std.os.mmap(null, size, protection_flags, mmap_flags, -1, 0);
|
||||||
@ -60,16 +71,163 @@ const Result = struct {
|
|||||||
image.sections.text.index += 1;
|
image.sections.text.index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getEntryPoint(image: *const Result, comptime Function: type) *const Function {
|
fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
|
||||||
comptime {
|
comptime {
|
||||||
assert(@typeInfo(Function) == .Fn);
|
assert(@typeInfo(FunctionType) == .Fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(image.sections.text.content.len > 0);
|
assert(image.sections.text.content.len > 0);
|
||||||
return @as(*const Function, @ptrCast(&image.sections.text.content[image.entry_point]));
|
return @as(*const FunctionType, @ptrCast(&image.sections.text.content[image.entry_point]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SimpleRelocation = struct {
|
||||||
|
source: u32,
|
||||||
|
write_offset: u8,
|
||||||
|
instruction_len: u8,
|
||||||
|
size: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RelocationManager = struct {
|
||||||
|
in_function_relocations: data_structures.AutoHashMap(ir.BasicBlock.Index, ArrayList(SimpleRelocation)) = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn get(comptime arch: std.Target.Cpu.Arch) type {
|
||||||
|
const backend = switch (arch) {
|
||||||
|
.x86_64 => @import("x86_64.zig"),
|
||||||
|
else => @compileError("Architecture not supported"),
|
||||||
|
};
|
||||||
|
_ = backend;
|
||||||
|
const Function = struct {
|
||||||
|
block_byte_counts: ArrayList(u16),
|
||||||
|
byte_count: u32 = 0,
|
||||||
|
relocations: ArrayList(Relocation) = .{},
|
||||||
|
block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{},
|
||||||
|
const Relocation = struct {
|
||||||
|
source: ir.BasicBlock.Index,
|
||||||
|
destination: ir.BasicBlock.Index,
|
||||||
|
offset_offset: u8,
|
||||||
|
preferred_instruction_len: u8,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const InstructionSelector = struct {
|
||||||
|
functions: ArrayList(Function),
|
||||||
|
};
|
||||||
|
_ = InstructionSelector;
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
|
||||||
|
_ = intermediate;
|
||||||
|
_ = allocator;
|
||||||
|
// var function_iterator = intermediate.functions.iterator();
|
||||||
|
// var instruction_selector = InstructionSelector{
|
||||||
|
// .functions = try ArrayList(Function).initCapacity(allocator, intermediate.functions.len),
|
||||||
|
// };
|
||||||
|
// while (function_iterator.next()) |ir_function| {
|
||||||
|
// const function = instruction_selector.functions.addOneAssumeCapacity();
|
||||||
|
// function.* = .{
|
||||||
|
// .block_byte_counts = try ArrayList(u16).initCapacity(allocator, ir_function.blocks.items.len),
|
||||||
|
// };
|
||||||
|
// try function.block_map.ensureTotalCapacity(allocator, @intCast(ir_function.blocks.items.len));
|
||||||
|
// for (ir_function.blocks.items, 0..) |block_index, index| {
|
||||||
|
// function.block_map.putAssumeCapacity(allocator, block_index, @intCast(index));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (ir_function.blocks.items) |block_index| {
|
||||||
|
// const block = intermediate.blocks.get(block_index);
|
||||||
|
// var block_byte_count: u16 = 0;
|
||||||
|
// for (block.instructions.items) |instruction_index| {
|
||||||
|
// const instruction = intermediate.instructions.get(instruction_index).*;
|
||||||
|
// switch (instruction) {
|
||||||
|
// .phi => unreachable,
|
||||||
|
// .ret => unreachable,
|
||||||
|
// .jump => {
|
||||||
|
// block_byte_count += 2;
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// function.block_byte_counts.appendAssumeCapacity(block_byte_count);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// unreachable;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
|
||||||
|
_ = allocator;
|
||||||
|
var result = try Result.create();
|
||||||
|
_ = result;
|
||||||
|
var relocation_manager = RelocationManager{};
|
||||||
|
_ = relocation_manager;
|
||||||
|
|
||||||
|
var function_iterator = intermediate.functions.iterator();
|
||||||
|
_ = function_iterator;
|
||||||
|
// while (function_iterator.next()) |function| {
|
||||||
|
// defer relocation_manager.in_function_relocations.clearRetainingCapacity();
|
||||||
|
//
|
||||||
|
// for (function.blocks.items) |block_index| {
|
||||||
|
// if (relocation_manager.in_function_relocations.getPtr(block_index)) |relocations| {
|
||||||
|
// const current_offset: i64 = @intCast(result.sections.text.index);
|
||||||
|
// _ = current_offset;
|
||||||
|
// for (relocations.items) |relocation| switch (relocation.size) {
|
||||||
|
// inline @sizeOf(u8), @sizeOf(u32) => |relocation_size| {
|
||||||
|
// const Elem = switch (relocation_size) {
|
||||||
|
// @sizeOf(u8) => u8,
|
||||||
|
// @sizeOf(u32) => u32,
|
||||||
|
// else => unreachable,
|
||||||
|
// };
|
||||||
|
// const Ptr = *align(1) Elem;
|
||||||
|
// _ = Ptr;
|
||||||
|
// const relocation_slice = result.sections.text.content[relocation.source + relocation.write_offset ..][0..relocation_size];
|
||||||
|
// _ = relocation_slice;
|
||||||
|
// // std.math.cast(
|
||||||
|
// //
|
||||||
|
// unreachable;
|
||||||
|
// },
|
||||||
|
// else => unreachable,
|
||||||
|
// };
|
||||||
|
// // const ptr: *align(1) u32 = @ptrCast(&result.sections.text[relocation_source..][0..@sizeOf(u32)]);
|
||||||
|
// // ptr.* =
|
||||||
|
// // try relocations.append(allocator, @intCast(result.sections.text[));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const block = intermediate.blocks.get(block_index);
|
||||||
|
//
|
||||||
|
// for (block.instructions.items) |instruction_index| {
|
||||||
|
// const instruction = intermediate.instructions.get(instruction_index);
|
||||||
|
// switch (instruction.*) {
|
||||||
|
// .jump => |jump_index| {
|
||||||
|
// const jump = intermediate.jumps.get(jump_index);
|
||||||
|
// assert(@as(u32, @bitCast(jump.source)) == @as(u32, @bitCast(block_index)));
|
||||||
|
// const relocation_index = result.sections.text.index + 1;
|
||||||
|
// if (@as(u32, @bitCast(jump.destination)) <= @as(u32, @bitCast(jump.source))) {
|
||||||
|
// unreachable;
|
||||||
|
// } else {
|
||||||
|
// result.appendCode(&(.{jmp_rel_32} ++ .{0} ** @sizeOf(u32)));
|
||||||
|
// const lookup_result = try relocation_manager.in_function_relocations.getOrPut(allocator, jump.destination);
|
||||||
|
// if (!lookup_result.found_existing) {
|
||||||
|
// lookup_result.value_ptr.* = .{};
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// try lookup_result.value_ptr.append(allocator, .{
|
||||||
|
// .source = @intCast(relocation_index),
|
||||||
|
// .write_offset = @sizeOf(u8),
|
||||||
|
// .instruction_len = @sizeOf(u8) + @sizeOf(u32),
|
||||||
|
// .size = @sizeOf(u32),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// else => |t| @panic(@tagName(t)),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// unreachable;
|
||||||
|
// }
|
||||||
|
// unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
const Rex = enum(u8) {
|
const Rex = enum(u8) {
|
||||||
b = upper_4_bits | (1 << 0),
|
b = upper_4_bits | (1 << 0),
|
||||||
x = upper_4_bits | (1 << 1),
|
x = upper_4_bits | (1 << 1),
|
||||||
@ -115,6 +273,7 @@ const prefix_rep = 0xf3;
|
|||||||
const prefix_rex_w = [1]u8{@intFromEnum(Rex.w)};
|
const prefix_rex_w = [1]u8{@intFromEnum(Rex.w)};
|
||||||
const prefix_16_bit_operand = [1]u8{0x66};
|
const prefix_16_bit_operand = [1]u8{0x66};
|
||||||
|
|
||||||
|
const jmp_rel_32 = 0xe9;
|
||||||
const ret = 0xc3;
|
const ret = 0xc3;
|
||||||
const mov_a_imm = [1]u8{0xb8};
|
const mov_a_imm = [1]u8{0xb8};
|
||||||
const mov_reg_imm8: u8 = 0xb0;
|
const mov_reg_imm8: u8 = 0xb0;
|
||||||
@ -142,7 +301,7 @@ test "ret void" {
|
|||||||
var image = try Result.create();
|
var image = try Result.create();
|
||||||
image.appendCodeByte(ret);
|
image.appendCodeByte(ret);
|
||||||
|
|
||||||
const function_pointer = image.getEntryPoint(fn () callconv(.C) void);
|
const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
|
||||||
function_pointer();
|
function_pointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +326,7 @@ test "ret integer" {
|
|||||||
movAImm(&image, expected_number);
|
movAImm(&image, expected_number);
|
||||||
image.appendCodeByte(ret);
|
image.appendCodeByte(ret);
|
||||||
|
|
||||||
const function_pointer = image.getEntryPoint(fn () callconv(.C) Int);
|
const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) Int);
|
||||||
const result = function_pointer();
|
const result = function_pointer();
|
||||||
try expect(result == expected_number);
|
try expect(result == expected_number);
|
||||||
}
|
}
|
||||||
@ -216,7 +375,7 @@ test "ret integer argument" {
|
|||||||
movRmR(&image, Int, .a, .di);
|
movRmR(&image, Int, .a, .di);
|
||||||
image.appendCodeByte(ret);
|
image.appendCodeByte(ret);
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn (Int) callconv(.C) Int);
|
const functionPointer = image.getEntryPoint(fn (Int) callconv(jit_callconv) Int);
|
||||||
const result = functionPointer(number);
|
const result = functionPointer(number);
|
||||||
try expectEqual(number, result);
|
try expectEqual(number, result);
|
||||||
}
|
}
|
||||||
@ -246,7 +405,7 @@ test "ret sub arguments" {
|
|||||||
subRmR(&image, Int, .a, .si);
|
subRmR(&image, Int, .a, .si);
|
||||||
image.appendCodeByte(ret);
|
image.appendCodeByte(ret);
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn (Int, Int) callconv(.C) Int);
|
const functionPointer = image.getEntryPoint(fn (Int, Int) callconv(jit_callconv) Int);
|
||||||
const result = functionPointer(a, b);
|
const result = functionPointer(a, b);
|
||||||
try expectEqual(a - b, result);
|
try expectEqual(a - b, result);
|
||||||
}
|
}
|
||||||
@ -328,7 +487,7 @@ fn TestIntegerBinaryOperation(comptime T: type) type {
|
|||||||
dstRmSrcR(&image, T, test_case.opcode, .a, .si);
|
dstRmSrcR(&image, T, test_case.opcode, .a, .si);
|
||||||
image.appendCodeByte(ret);
|
image.appendCodeByte(ret);
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn (T, T) callconv(.C) T);
|
const functionPointer = image.getEntryPoint(fn (T, T) callconv(jit_callconv) T);
|
||||||
const expected = test_case.callback(a, b);
|
const expected = test_case.callback(a, b);
|
||||||
const result = functionPointer(a, b);
|
const result = functionPointer(a, b);
|
||||||
if (should_log) {
|
if (should_log) {
|
||||||
@ -350,7 +509,7 @@ test "call after" {
|
|||||||
@as(*align(1) u32, @ptrCast(&image.sections.text.content[jump_patch_offset])).* = @intCast(jump_target - jump_source);
|
@as(*align(1) u32, @ptrCast(&image.sections.text.content[jump_patch_offset])).* = @intCast(jump_target - jump_source);
|
||||||
image.appendCodeByte(ret);
|
image.appendCodeByte(ret);
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn () callconv(.C) void);
|
const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
|
||||||
functionPointer();
|
functionPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +528,7 @@ test "call before" {
|
|||||||
image.appendCode(&second_call);
|
image.appendCode(&second_call);
|
||||||
image.appendCodeByte(ret);
|
image.appendCodeByte(ret);
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn () callconv(.C) void);
|
const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
|
||||||
functionPointer();
|
functionPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,243 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const print = std.debug.print;
|
||||||
|
|
||||||
const Compilation = @import("../Compilation.zig");
|
const Compilation = @import("../Compilation.zig");
|
||||||
const Module = Compilation.Module;
|
const Module = Compilation.Module;
|
||||||
const Package = Compilation.Package;
|
const Package = Compilation.Package;
|
||||||
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_declaration: Compilation.Declaration.Index) !void {
|
|
||||||
_ = main_declaration;
|
const data_structures = @import("../data_structures.zig");
|
||||||
|
const ArrayList = data_structures.ArrayList;
|
||||||
|
const BlockList = data_structures.BlockList;
|
||||||
|
|
||||||
|
pub const Result = struct {
|
||||||
|
functions: BlockList(Function) = .{},
|
||||||
|
blocks: BlockList(BasicBlock) = .{},
|
||||||
|
instructions: BlockList(Instruction) = .{},
|
||||||
|
jumps: BlockList(Jump) = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result {
|
||||||
|
_ = main_file;
|
||||||
_ = package;
|
_ = package;
|
||||||
_ = module;
|
print("\nFunction count: {}\n", .{module.functions.len});
|
||||||
_ = compilation;
|
|
||||||
|
var function_iterator = module.functions.iterator();
|
||||||
|
var builder = Builder{
|
||||||
|
.allocator = compilation.base_allocator,
|
||||||
|
.module = module,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (function_iterator.next()) |sema_function| {
|
||||||
|
print("\nFunction: {}\n", .{sema_function});
|
||||||
|
|
||||||
|
try builder.function(sema_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const BasicBlock = struct {
|
||||||
|
instructions: ArrayList(Instruction.Index) = .{},
|
||||||
|
incomplete_phis: ArrayList(Instruction.Index) = .{},
|
||||||
|
filled: bool = false,
|
||||||
|
sealed: bool = false,
|
||||||
|
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
|
||||||
|
fn seal(basic_block: *BasicBlock) void {
|
||||||
|
for (basic_block.incomplete_phis.items) |incomplete_phi| {
|
||||||
|
_ = incomplete_phi;
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_block.sealed = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Instruction = union(enum) {
|
||||||
|
jump: Jump.Index,
|
||||||
|
phi: Phi.Index,
|
||||||
|
ret: Ret,
|
||||||
|
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Phi = struct {
|
||||||
|
foo: u32 = 0,
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Ret = struct {
|
||||||
|
value: Instruction.Index,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Jump = struct {
|
||||||
|
source: BasicBlock.Index,
|
||||||
|
destination: BasicBlock.Index,
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Function = struct {
|
||||||
|
blocks: ArrayList(BasicBlock.Index) = .{},
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Builder = struct {
|
||||||
|
allocator: Allocator,
|
||||||
|
ir: Result = .{},
|
||||||
|
module: *Module,
|
||||||
|
current_basic_block: BasicBlock.Index = BasicBlock.Index.invalid,
|
||||||
|
current_function_index: Function.Index = Function.Index.invalid,
|
||||||
|
|
||||||
|
fn function(builder: *Builder, sema_function: Compilation.Function) !void {
|
||||||
|
builder.current_function_index = try builder.ir.functions.append(builder.allocator, .{});
|
||||||
|
// TODO: arguments
|
||||||
|
builder.current_basic_block = try builder.newBlock();
|
||||||
|
|
||||||
|
const return_type = builder.module.types.get(builder.module.function_prototypes.get(sema_function.prototype).return_type);
|
||||||
|
const is_noreturn = return_type.* == .noreturn;
|
||||||
|
if (!is_noreturn) {
|
||||||
|
const exit_block = try builder.newBlock();
|
||||||
|
const phi = try builder.appendToBlock(exit_block, .{
|
||||||
|
.phi = Phi.Index.invalid,
|
||||||
|
});
|
||||||
|
const ret = try builder.appendToBlock(exit_block, .{
|
||||||
|
.ret = .{
|
||||||
|
.value = phi,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
_ = ret;
|
||||||
|
}
|
||||||
|
const sema_block = sema_function.getBodyBlock(builder.module);
|
||||||
|
try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn });
|
||||||
|
|
||||||
|
try builder.dumpFunction(std.io.getStdErr().writer(), builder.current_function_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dumpFunction(builder: *Builder, writer: anytype, index: Function.Index) !void {
|
||||||
|
const f = builder.ir.functions.get(index);
|
||||||
|
try writer.writeAll("Hello world!\n");
|
||||||
|
print("Function blocks: {}\n", .{f.blocks.items.len});
|
||||||
|
var function_instruction_index: usize = 0;
|
||||||
|
for (f.blocks.items, 0..) |block_index, function_block_index| {
|
||||||
|
print("#{}:\n", .{function_block_index});
|
||||||
|
const function_block = builder.ir.blocks.get(block_index);
|
||||||
|
for (function_block.instructions.items) |instruction_index| {
|
||||||
|
const instruction = builder.ir.instructions.get(instruction_index);
|
||||||
|
print("%{}: {}\n", .{ function_instruction_index, instruction });
|
||||||
|
function_instruction_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blockInsideBasicBlock(builder: *Builder, sema_block: *Compilation.Block, block_index: BasicBlock.Index) !BasicBlock.Index {
|
||||||
|
builder.current_basic_block = block_index;
|
||||||
|
try builder.block(sema_block, .{});
|
||||||
|
return builder.current_basic_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BlockOptions = packed struct {
|
||||||
|
emit_exit_block: bool = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn block(builder: *Builder, sema_block: *Compilation.Block, options: BlockOptions) error{OutOfMemory}!void {
|
||||||
|
for (sema_block.statements.items) |sema_statement_index| {
|
||||||
|
const sema_statement = builder.module.values.get(sema_statement_index);
|
||||||
|
switch (sema_statement.*) {
|
||||||
|
.loop => |loop_index| {
|
||||||
|
const sema_loop = builder.module.loops.get(loop_index);
|
||||||
|
const sema_loop_condition = builder.module.values.get(sema_loop.condition);
|
||||||
|
const sema_loop_body = builder.module.values.get(sema_loop.body);
|
||||||
|
const condition: Compilation.Value.Index = switch (sema_loop_condition.*) {
|
||||||
|
.bool => |bool_value| switch (bool_value) {
|
||||||
|
true => Compilation.Value.Index.invalid,
|
||||||
|
false => unreachable,
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
};
|
||||||
|
|
||||||
|
const original_block = builder.current_basic_block;
|
||||||
|
const jump_to_loop = try builder.append(.{
|
||||||
|
.jump = undefined,
|
||||||
|
});
|
||||||
|
const loop_body_block = try builder.newBlock();
|
||||||
|
const loop_prologue_block = if (options.emit_exit_block) try builder.newBlock() else BasicBlock.Index.invalid;
|
||||||
|
|
||||||
|
const loop_head_block = switch (condition.valid) {
|
||||||
|
false => loop_body_block,
|
||||||
|
true => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.ir.instructions.get(jump_to_loop).jump = try builder.jump(.{
|
||||||
|
.source = original_block,
|
||||||
|
.destination = loop_head_block,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sema_body_block = builder.module.blocks.get(sema_loop_body.block);
|
||||||
|
builder.current_basic_block = try builder.blockInsideBasicBlock(sema_body_block, loop_body_block);
|
||||||
|
if (loop_prologue_block.valid) {
|
||||||
|
builder.ir.blocks.get(loop_prologue_block).seal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sema_body_block.reaches_end) {
|
||||||
|
_ = try builder.append(.{
|
||||||
|
.jump = try builder.jump(.{
|
||||||
|
.source = builder.current_basic_block,
|
||||||
|
.destination = loop_head_block,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.ir.blocks.get(builder.current_basic_block).filled = true;
|
||||||
|
builder.ir.blocks.get(loop_body_block).seal();
|
||||||
|
if (!loop_head_block.eq(loop_body_block)) {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loop_prologue_block.valid) {
|
||||||
|
builder.current_basic_block = loop_prologue_block;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index {
|
||||||
|
const destination_block = builder.ir.blocks.get(jump_descriptor.destination);
|
||||||
|
assert(!destination_block.sealed);
|
||||||
|
return try builder.ir.jumps.append(builder.allocator, jump_descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append(builder: *Builder, instruction: Instruction) !Instruction.Index {
|
||||||
|
assert(builder.current_basic_block.valid);
|
||||||
|
return builder.appendToBlock(builder.current_basic_block, instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn appendToBlock(builder: *Builder, block_index: BasicBlock.Index, instruction: Instruction) !Instruction.Index {
|
||||||
|
const instruction_index = try builder.ir.instructions.append(builder.allocator, instruction);
|
||||||
|
try builder.ir.blocks.get(block_index).instructions.append(builder.allocator, instruction_index);
|
||||||
|
|
||||||
|
return instruction_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn newBlock(builder: *Builder) !BasicBlock.Index {
|
||||||
|
const new_block_index = try builder.ir.blocks.append(builder.allocator, .{});
|
||||||
|
const current_function = builder.ir.functions.get(builder.current_function_index);
|
||||||
|
const function_block_index = current_function.blocks.items.len;
|
||||||
|
try current_function.blocks.append(builder.allocator, new_block_index);
|
||||||
|
|
||||||
|
print("Adding block: {}\n", .{function_block_index});
|
||||||
|
|
||||||
|
return new_block_index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
0
src/backend/x86_64.zig
Normal file
0
src/backend/x86_64.zig
Normal file
@ -36,17 +36,59 @@ pub fn BlockList(comptime T: type) type {
|
|||||||
const List = @This();
|
const List = @This();
|
||||||
|
|
||||||
pub const Index = packed struct(u32) {
|
pub const Index = packed struct(u32) {
|
||||||
valid: bool = true,
|
block: u24,
|
||||||
index: u6,
|
index: u6,
|
||||||
block: u25,
|
_reserved: bool = false,
|
||||||
|
valid: bool = true,
|
||||||
|
|
||||||
pub const invalid = Index{
|
pub const invalid = Index{
|
||||||
.valid = false,
|
.valid = false,
|
||||||
.index = 0,
|
.index = 0,
|
||||||
.block = 0,
|
.block = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn eq(index: Index, other: Index) bool {
|
||||||
|
return @as(u32, @bitCast(index)) == @as(u32, @bitCast(other));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Iterator = struct {
|
||||||
|
block_index: u26,
|
||||||
|
element_index: u7,
|
||||||
|
list: *const List,
|
||||||
|
|
||||||
|
pub fn next(i: *Iterator) ?T {
|
||||||
|
return if (i.nextPointer()) |ptr| ptr.* else null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nextPointer(i: *Iterator) ?*T {
|
||||||
|
if (i.element_index >= item_count) {
|
||||||
|
i.block_index += 1;
|
||||||
|
i.element_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i.block_index < i.list.blocks.items.len) : (i.block_index += 1) {
|
||||||
|
while (i.element_index < item_count) : (i.element_index += 1) {
|
||||||
|
if (i.list.blocks.items[i.block_index].bitset.isSet(i.element_index)) {
|
||||||
|
const index = i.element_index;
|
||||||
|
i.element_index += 1;
|
||||||
|
return &i.list.blocks.items[i.block_index].items[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn iterator(list: *const List) Iterator {
|
||||||
|
return .{
|
||||||
|
.block_index = 0,
|
||||||
|
.element_index = 0,
|
||||||
|
.list = list,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(list: *List, index: Index) *T {
|
pub fn get(list: *List, index: Index) *T {
|
||||||
assert(index.valid);
|
assert(index.valid);
|
||||||
return &list.blocks.items[index.block].items[index.index];
|
return &list.blocks.items[index.block].items[index.index];
|
||||||
@ -59,6 +101,7 @@ pub fn BlockList(comptime T: type) type {
|
|||||||
// Follow the guess
|
// Follow the guess
|
||||||
if (list.blocks.items[list.first_block].allocateIndex()) |index| {
|
if (list.blocks.items[list.first_block].allocateIndex()) |index| {
|
||||||
list.blocks.items[list.first_block].items[index] = element;
|
list.blocks.items[list.first_block].items[index] = element;
|
||||||
|
list.len += 1;
|
||||||
return .{
|
return .{
|
||||||
.index = index,
|
.index = index,
|
||||||
.block = @intCast(list.first_block),
|
.block = @intCast(list.first_block),
|
||||||
@ -69,8 +112,10 @@ pub fn BlockList(comptime T: type) type {
|
|||||||
} else {
|
} else {
|
||||||
const block_index = list.blocks.items.len;
|
const block_index = list.blocks.items.len;
|
||||||
const new_block = list.blocks.addOneAssumeCapacity();
|
const new_block = list.blocks.addOneAssumeCapacity();
|
||||||
|
new_block.* = .{};
|
||||||
const index = new_block.allocateIndex() catch unreachable;
|
const index = new_block.allocateIndex() catch unreachable;
|
||||||
new_block.items[index] = element;
|
new_block.items[index] = element;
|
||||||
|
list.len += 1;
|
||||||
return .{
|
return .{
|
||||||
.index = index,
|
.index = index,
|
||||||
.block = @intCast(block_index),
|
.block = @intCast(block_index),
|
||||||
|
@ -7,10 +7,12 @@ const File = Compilation.File;
|
|||||||
const Module = Compilation.Module;
|
const Module = Compilation.Module;
|
||||||
const Package = Compilation.Package;
|
const Package = Compilation.Package;
|
||||||
|
|
||||||
|
const Assignment = Compilation.Assignment;
|
||||||
const Block = Compilation.Block;
|
const Block = Compilation.Block;
|
||||||
const Declaration = Compilation.Declaration;
|
const Declaration = Compilation.Declaration;
|
||||||
const Field = Compilation.Field;
|
const Field = Compilation.Field;
|
||||||
const Function = Compilation.Function;
|
const Function = Compilation.Function;
|
||||||
|
const Loop = Compilation.Loop;
|
||||||
const Scope = Compilation.Scope;
|
const Scope = Compilation.Scope;
|
||||||
const Struct = Compilation.Struct;
|
const Struct = Compilation.Struct;
|
||||||
const Type = Compilation.Type;
|
const Type = Compilation.Type;
|
||||||
@ -45,90 +47,157 @@ const Analyzer = struct {
|
|||||||
fn comptimeBlock(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index {
|
fn comptimeBlock(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index {
|
||||||
const comptime_node = analyzer.nodes[node_index.unwrap()];
|
const comptime_node = analyzer.nodes[node_index.unwrap()];
|
||||||
|
|
||||||
const comptime_block_node = analyzer.nodes[comptime_node.left.unwrap()];
|
const comptime_block = try analyzer.block(scope, .{ .none = {} }, comptime_node.left);
|
||||||
var statement_node_indices = ArrayList(Node.Index){};
|
return try analyzer.module.values.append(analyzer.allocator, .{
|
||||||
switch (comptime_block_node.id) {
|
.block = comptime_block,
|
||||||
.block_one => {
|
});
|
||||||
try statement_node_indices.append(analyzer.allocator, comptime_block_node.left);
|
|
||||||
},
|
|
||||||
else => |t| @panic(@tagName(t)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var statement_values = ArrayList(Value.Index){};
|
fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Assignment.Index {
|
||||||
|
print("Assign: #{}", .{node_index.value});
|
||||||
for (statement_node_indices.items) |statement_node_index| {
|
|
||||||
const statement_node = analyzer.nodes[statement_node_index.unwrap()];
|
|
||||||
switch (statement_node.id) {
|
|
||||||
.assign => {
|
|
||||||
const assign_expression = try analyzer.assign(scope, statement_node_index);
|
|
||||||
try statement_values.append(analyzer.allocator, assign_expression);
|
|
||||||
},
|
|
||||||
else => |t| @panic(@tagName(t)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
return Value.Index.invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index {
|
|
||||||
const node = analyzer.nodes[node_index.unwrap()];
|
const node = analyzer.nodes[node_index.unwrap()];
|
||||||
|
assert(node.id == .assign);
|
||||||
print("\nAssign. Left: {}. Right: {}\n", .{ node.left, node.right });
|
const Result = struct {
|
||||||
|
left: Value.Index,
|
||||||
|
right: Value.Index,
|
||||||
|
};
|
||||||
|
const result: Result = switch (node.left.valid) {
|
||||||
// In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result```
|
// In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result```
|
||||||
if (node.left.valid) {
|
false => .{
|
||||||
@panic("Not discard");
|
.left = Value.Index.invalid,
|
||||||
|
.right = try analyzer.expression(scope, ExpectType.none, node.right),
|
||||||
|
},
|
||||||
|
true => {
|
||||||
|
const left_node = analyzer.nodes[node.left.unwrap()];
|
||||||
|
print("left node index: {}. Left node: {}", .{ node.left, left_node });
|
||||||
|
// const id = analyzer.tokenIdentifier(.token);
|
||||||
|
// print("id: {s}\n", .{id});
|
||||||
|
const left = try analyzer.expression(scope, ExpectType.none, node.left);
|
||||||
|
_ = left;
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
print("Assignment: L: {}. R: {}\n", .{ result.left, result.right });
|
||||||
|
|
||||||
|
if (result.left.valid and analyzer.module.values.get(result.left).isComptime() and analyzer.module.values.get(result.right).isComptime()) {
|
||||||
|
unreachable;
|
||||||
} else {
|
} else {
|
||||||
return try analyzer.expression(scope, ExpectType{ .none = {} }, node.right);
|
const assignment_index = try analyzer.module.assignments.append(analyzer.allocator, .{
|
||||||
|
.store = result.left,
|
||||||
|
.load = result.right,
|
||||||
|
});
|
||||||
|
return assignment_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) !Block.Index {
|
fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Block.Index {
|
||||||
|
var reaches_end = true;
|
||||||
const block_node = analyzer.nodes[node_index.unwrap()];
|
const block_node = analyzer.nodes[node_index.unwrap()];
|
||||||
var statements = ArrayList(Node.Index){};
|
var statement_nodes = ArrayList(Node.Index){};
|
||||||
switch (block_node.id) {
|
switch (block_node.id) {
|
||||||
.block_one => {
|
.block_one, .comptime_block_one => {
|
||||||
try statements.append(analyzer.allocator, block_node.left);
|
try statement_nodes.append(analyzer.allocator, block_node.left);
|
||||||
},
|
},
|
||||||
.block_zero => {},
|
.block_zero, .comptime_block_zero => {},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
|
|
||||||
for (statements.items) |statement_node_index| {
|
const is_comptime = switch (block_node.id) {
|
||||||
_ = try analyzer.expression(scope, expect_type, statement_node_index);
|
.comptime_block_zero, .comptime_block_one => true,
|
||||||
// const statement_node = analyzer.nodes[statement_node_index.unwrap()];
|
.block_zero, .block_one => false,
|
||||||
//
|
else => |t| @panic(@tagName(t)),
|
||||||
// switch (statement_node.id) {
|
};
|
||||||
// try .simple_while => {
|
_ = is_comptime;
|
||||||
// const while_condition = try analyzer.expression(scope, ExpectType.boolean, statement_node.left);
|
|
||||||
// _ = while_condition;
|
var statements = ArrayList(Value.Index){};
|
||||||
// const while_block = try analyzer.block(scope, expect_type, statement_node.right);
|
|
||||||
// _ = while_block;
|
for (statement_nodes.items) |statement_node_index| {
|
||||||
// unreachable;
|
if (!reaches_end) {
|
||||||
// },
|
unreachable;
|
||||||
// else => |t| @panic(@tagName(t)),
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return try analyzer.module.blocks.append(analyzer.allocator, .{});
|
const statement_node = analyzer.nodes[statement_node_index.unwrap()];
|
||||||
|
const statement_value = switch (statement_node.id) {
|
||||||
|
inline .assign, .simple_while => |statement_id| blk: {
|
||||||
|
const specific_value_index = switch (statement_id) {
|
||||||
|
.assign => try analyzer.assign(scope, statement_node_index),
|
||||||
|
.simple_while => statement: {
|
||||||
|
const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{
|
||||||
|
.condition = Value.Index.invalid,
|
||||||
|
.body = Value.Index.invalid,
|
||||||
|
.breaks = false,
|
||||||
|
});
|
||||||
|
const loop_structure = analyzer.module.loops.get(loop_index);
|
||||||
|
const while_condition = try analyzer.expression(scope, ExpectType.boolean, statement_node.left);
|
||||||
|
const while_body = try analyzer.expression(scope, expect_type, statement_node.right);
|
||||||
|
loop_structure.condition = while_condition;
|
||||||
|
loop_structure.body = while_body;
|
||||||
|
|
||||||
|
reaches_end = loop_structure.breaks or while_condition.valid;
|
||||||
|
|
||||||
|
break :statement loop_index;
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
const value = @unionInit(Value, switch (statement_id) {
|
||||||
|
.assign => "assign",
|
||||||
|
.simple_while => "loop",
|
||||||
|
else => unreachable,
|
||||||
|
}, specific_value_index);
|
||||||
|
const value_index = try analyzer.module.values.append(analyzer.allocator, value);
|
||||||
|
break :blk value_index;
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
};
|
||||||
|
try statements.append(analyzer.allocator, statement_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) error{OutOfMemory}!Value.Index {
|
return try analyzer.module.blocks.append(analyzer.allocator, .{
|
||||||
const node = analyzer.nodes[node_index.unwrap()];
|
.statements = statements,
|
||||||
return switch (node.id) {
|
.reaches_end = reaches_end,
|
||||||
.identifier => blk: {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn whileExpression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node: Node) !Loop.Index {
|
||||||
|
_ = node;
|
||||||
|
_ = expect_type;
|
||||||
|
_ = scope;
|
||||||
|
_ = analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, value: *Value) !void {
|
||||||
|
const node_index = switch (value.*) {
|
||||||
|
.unresolved => |unresolved| unresolved.node_index,
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
};
|
||||||
|
value.* = try analyzer.resolveNode(scope, expect_type, node_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doIdentifier(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node: Node) !Value.Index {
|
||||||
|
assert(node.id == .identifier);
|
||||||
const identifier_hash = try analyzer.identifierFromToken(node.token);
|
const identifier_hash = try analyzer.identifierFromToken(node.token);
|
||||||
// TODO: search in upper scopes too
|
// TODO: search in upper scopes too
|
||||||
const identifier_scope_lookup = try scope.declarations.getOrPut(analyzer.allocator, identifier_hash);
|
const identifier_scope_lookup = try scope.declarations.getOrPut(analyzer.allocator, identifier_hash);
|
||||||
if (identifier_scope_lookup.found_existing) {
|
if (identifier_scope_lookup.found_existing) {
|
||||||
const declaration_index = identifier_scope_lookup.value_ptr.*;
|
const declaration_index = identifier_scope_lookup.value_ptr.*;
|
||||||
const declaration = analyzer.module.declarations.get(declaration_index);
|
const declaration = analyzer.module.declarations.get(declaration_index);
|
||||||
break :blk try analyzer.analyzeDeclaration(scope, declaration);
|
const init_value = analyzer.module.values.get(declaration.init_value);
|
||||||
|
try analyzer.resolve(scope, expect_type, init_value);
|
||||||
|
if (init_value.* != .runtime and declaration.mutability == .@"const") {
|
||||||
|
return declaration.init_value;
|
||||||
|
} else {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
@panic("TODO: not found");
|
@panic("TODO: not found");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
fn resolveNode(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Value {
|
||||||
|
const node = analyzer.nodes[node_index.unwrap()];
|
||||||
|
return switch (node.id) {
|
||||||
|
.identifier => unreachable,
|
||||||
.compiler_intrinsic_one => blk: {
|
.compiler_intrinsic_one => blk: {
|
||||||
const intrinsic_name = analyzer.tokenIdentifier(node.token + 1);
|
const intrinsic_name = analyzer.tokenIdentifier(node.token + 1);
|
||||||
const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse unreachable;
|
const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse unreachable;
|
||||||
@ -148,14 +217,9 @@ const Analyzer = struct {
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file_struct_declaration_index = try analyzeFile(analyzer.allocator, analyzer.module, imported_file.file);
|
break :blk .{
|
||||||
break :blk try analyzer.module.values.append(analyzer.allocator, .{
|
.type = try analyzeFile(analyzer.allocator, analyzer.module, imported_file.file),
|
||||||
.type = .{
|
};
|
||||||
.declaration = file_struct_declaration_index,
|
|
||||||
},
|
|
||||||
.is_const = true,
|
|
||||||
.is_comptime = true,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
@ -174,15 +238,27 @@ const Analyzer = struct {
|
|||||||
.prototype = function_prototype_index,
|
.prototype = function_prototype_index,
|
||||||
.body = function_body,
|
.body = function_body,
|
||||||
});
|
});
|
||||||
const value_index = try analyzer.module.values.append(analyzer.allocator, .{
|
break :blk .{
|
||||||
.type = .{
|
|
||||||
.function = function_index,
|
.function = function_index,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
.is_const = true,
|
.keyword_true => unreachable,
|
||||||
.is_comptime = true,
|
.simple_while => unreachable,
|
||||||
});
|
// .assign => try analyzer.assign(scope, node_index),
|
||||||
break :blk value_index;
|
.block_zero, .block_one => blk: {
|
||||||
|
const block_index = try analyzer.block(scope, expect_type, node_index);
|
||||||
|
break :blk .{
|
||||||
|
.block = block_index,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) !Value.Index {
|
||||||
|
const node = analyzer.nodes[node_index.unwrap()];
|
||||||
|
return switch (node.id) {
|
||||||
|
.identifier => analyzer.doIdentifier(scope, expect_type, node),
|
||||||
.keyword_true => blk: {
|
.keyword_true => blk: {
|
||||||
switch (expect_type) {
|
switch (expect_type) {
|
||||||
.none => {},
|
.none => {},
|
||||||
@ -195,22 +271,9 @@ const Analyzer = struct {
|
|||||||
|
|
||||||
break :blk bool_true;
|
break :blk bool_true;
|
||||||
},
|
},
|
||||||
.simple_while => blk: {
|
.block_zero => try analyzer.module.values.append(analyzer.allocator, .{
|
||||||
const while_condition = try analyzer.expression(scope, ExpectType.boolean, node.left);
|
.block = try analyzer.block(scope, expect_type, node_index),
|
||||||
_ = while_condition;
|
}),
|
||||||
const while_body = try analyzer.block(scope, expect_type, node.right);
|
|
||||||
_ = while_body;
|
|
||||||
const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{});
|
|
||||||
const value_index = try analyzer.module.values.append(analyzer.allocator, .{
|
|
||||||
.type = .{
|
|
||||||
.loop = loop_index,
|
|
||||||
},
|
|
||||||
// TODO:
|
|
||||||
.is_const = false,
|
|
||||||
.is_comptime = false,
|
|
||||||
});
|
|
||||||
break :blk value_index;
|
|
||||||
},
|
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -249,44 +312,36 @@ const Analyzer = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn analyzeDeclaration(analyzer: *Analyzer, scope: *Scope, declaration: *Declaration) !Value.Index {
|
fn analyzeDeclaration(analyzer: *Analyzer, scope: *Scope, declaration: *Declaration) !Value.Index {
|
||||||
switch (declaration.*) {
|
_ = declaration;
|
||||||
.unresolved => |node_index| {
|
_ = scope;
|
||||||
const declaration_node = analyzer.nodes[node_index.unwrap()];
|
_ = analyzer;
|
||||||
return switch (declaration_node.id) {
|
// switch (declaration.*) {
|
||||||
.simple_variable_declaration => blk: {
|
// .unresolved => |node_index| {
|
||||||
const expect_type = switch (declaration_node.left.valid) {
|
// const declaration_node = analyzer.nodes[node_index.unwrap()];
|
||||||
true => unreachable,
|
// return switch (declaration_node.id) {
|
||||||
false => @unionInit(ExpectType, "none", {}),
|
// .simple_variable_declaration => blk: {
|
||||||
};
|
// const expect_type = switch (declaration_node.left.valid) {
|
||||||
|
// true => unreachable,
|
||||||
const initialization_expression = try analyzer.expression(scope, expect_type, declaration_node.right);
|
// false => @unionInit(ExpectType, "none", {}),
|
||||||
const value = analyzer.module.values.get(initialization_expression);
|
// };
|
||||||
if (value.is_comptime and value.is_const) {
|
//
|
||||||
break :blk initialization_expression;
|
// const initialization_expression = try analyzer.expression(scope, expect_type, declaration_node.right);
|
||||||
}
|
// const value = analyzer.module.values.get(initialization_expression);
|
||||||
|
// if (value.is_comptime and value.is_const) {
|
||||||
unreachable;
|
// break :blk initialization_expression;
|
||||||
},
|
// }
|
||||||
else => |t| @panic(@tagName(t)),
|
//
|
||||||
};
|
// unreachable;
|
||||||
},
|
// },
|
||||||
.struct_type => unreachable,
|
// else => |t| @panic(@tagName(t)),
|
||||||
}
|
// };
|
||||||
|
// },
|
||||||
|
// .struct_type => unreachable,
|
||||||
|
// }
|
||||||
|
|
||||||
@panic("TODO: analyzeDeclaration");
|
@panic("TODO: analyzeDeclaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn containerMember(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !void {
|
|
||||||
const node = analyzer.nodes[node_index.unwrap()];
|
|
||||||
switch (node.id) {
|
|
||||||
.simple_variable_declaration => {},
|
|
||||||
.@"comptime" => {
|
|
||||||
_ = try analyzer.comptimeBlock(scope, node_index);
|
|
||||||
},
|
|
||||||
else => std.debug.panic("Tag: {}", .{node.id}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn globalSymbolDeclaration(analyzer: *Analyzer, symbol_declaration: SymbolDeclaration) !void {
|
fn globalSymbolDeclaration(analyzer: *Analyzer, symbol_declaration: SymbolDeclaration) !void {
|
||||||
if (symbol_declaration.type_node.get()) |type_node_index| {
|
if (symbol_declaration.type_node.get()) |type_node_index| {
|
||||||
_ = type_node_index;
|
_ = type_node_index;
|
||||||
@ -339,21 +394,24 @@ const Analyzer = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn structDeclaration(analyzer: *Analyzer, parent_scope: Scope.Index, container_declaration: syntactic_analyzer.ContainerDeclaration, index: Node.Index) !Declaration.Index {
|
fn structType(analyzer: *Analyzer, parent_scope: Scope.Index, container_declaration: syntactic_analyzer.ContainerDeclaration, index: Node.Index) !Type.Index {
|
||||||
_ = index;
|
_ = index;
|
||||||
const new_scope = try analyzer.allocateScope(parent_scope, Type.Index.invalid);
|
const new_scope = try analyzer.allocateScope(.{ .parent = parent_scope });
|
||||||
const scope = new_scope.ptr;
|
const scope = new_scope.ptr;
|
||||||
|
|
||||||
const is_file = !parent_scope.valid;
|
const is_file = !parent_scope.valid;
|
||||||
assert(is_file);
|
assert(is_file);
|
||||||
// TODO: do it properly
|
|
||||||
const declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{
|
const struct_index = try analyzer.module.structs.append(analyzer.allocator, .{
|
||||||
.struct_type = .{
|
|
||||||
.scope = new_scope.index,
|
.scope = new_scope.index,
|
||||||
.initialization = if (is_file) Value.Index.invalid else unreachable,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
// TODO:
|
const struct_type = analyzer.module.structs.get(struct_index);
|
||||||
|
const type_index = try analyzer.module.types.append(analyzer.allocator, .{
|
||||||
|
.@"struct" = struct_index,
|
||||||
|
});
|
||||||
|
scope.type = type_index;
|
||||||
|
|
||||||
|
_ = struct_type;
|
||||||
assert(container_declaration.members.len > 0);
|
assert(container_declaration.members.len > 0);
|
||||||
|
|
||||||
const count = blk: {
|
const count = blk: {
|
||||||
@ -391,6 +449,11 @@ const Analyzer = struct {
|
|||||||
switch (declaration_node.id) {
|
switch (declaration_node.id) {
|
||||||
.@"comptime" => {},
|
.@"comptime" => {},
|
||||||
.simple_variable_declaration => {
|
.simple_variable_declaration => {
|
||||||
|
const mutability: Compilation.Mutability = switch (analyzer.tokens[declaration_node.token].id) {
|
||||||
|
.fixed_keyword_const => .@"const",
|
||||||
|
.fixed_keyword_var => .@"var",
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
};
|
||||||
const expected_identifier_token_index = declaration_node.token + 1;
|
const expected_identifier_token_index = declaration_node.token + 1;
|
||||||
const expected_identifier_token = analyzer.tokens[expected_identifier_token_index];
|
const expected_identifier_token = analyzer.tokens[expected_identifier_token_index];
|
||||||
if (expected_identifier_token.id != .identifier) {
|
if (expected_identifier_token.id != .identifier) {
|
||||||
@ -416,7 +479,14 @@ const Analyzer = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const container_declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{
|
const container_declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{
|
||||||
.unresolved = declaration_node_index,
|
.name = declaration_name,
|
||||||
|
.scope_type = .global,
|
||||||
|
.mutability = mutability,
|
||||||
|
.init_value = try analyzer.module.values.append(analyzer.allocator, .{
|
||||||
|
.unresolved = .{
|
||||||
|
.node_index = declaration_node.right,
|
||||||
|
},
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
scope_lookup.value_ptr.* = container_declaration_index;
|
scope_lookup.value_ptr.* = container_declaration_index;
|
||||||
@ -429,8 +499,9 @@ const Analyzer = struct {
|
|||||||
for (declaration_nodes.items) |declaration_node_index| {
|
for (declaration_nodes.items) |declaration_node_index| {
|
||||||
const declaration_node = analyzer.nodes[declaration_node_index.unwrap()];
|
const declaration_node = analyzer.nodes[declaration_node_index.unwrap()];
|
||||||
switch (declaration_node.id) {
|
switch (declaration_node.id) {
|
||||||
.@"comptime", .simple_variable_declaration => try analyzer.containerMember(scope, declaration_node_index),
|
.@"comptime" => _ = try analyzer.comptimeBlock(scope, declaration_node_index),
|
||||||
else => unreachable,
|
.simple_variable_declaration => {},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +512,7 @@ const Analyzer = struct {
|
|||||||
@panic("TODO: fields");
|
@panic("TODO: fields");
|
||||||
}
|
}
|
||||||
|
|
||||||
return declaration_index;
|
return type_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberType = enum {
|
const MemberType = enum {
|
||||||
@ -494,11 +565,8 @@ const Analyzer = struct {
|
|||||||
index: Scope.Index,
|
index: Scope.Index,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn allocateScope(analyzer: *Analyzer, parent_scope: Scope.Index, scope_type: Type.Index) !ScopeAllocation {
|
fn allocateScope(analyzer: *Analyzer, scope_value: Scope) !ScopeAllocation {
|
||||||
const scope_index = try analyzer.module.scopes.append(analyzer.allocator, .{
|
const scope_index = try analyzer.module.scopes.append(analyzer.allocator, scope_value);
|
||||||
.parent = parent_scope,
|
|
||||||
.type = scope_type,
|
|
||||||
});
|
|
||||||
const scope = analyzer.module.scopes.get(scope_index);
|
const scope = analyzer.module.scopes.get(scope_index);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@ -512,6 +580,9 @@ const ExpectType = union(enum) {
|
|||||||
none,
|
none,
|
||||||
type_index: Type.Index,
|
type_index: Type.Index,
|
||||||
|
|
||||||
|
pub const none = ExpectType{
|
||||||
|
.none = {},
|
||||||
|
};
|
||||||
pub const boolean = ExpectType{
|
pub const boolean = ExpectType{
|
||||||
.type_index = type_boolean,
|
.type_index = type_boolean,
|
||||||
};
|
};
|
||||||
@ -562,7 +633,7 @@ const HardwareSignedIntegerType = enum {
|
|||||||
const offset = HardwareUnsignedIntegerType.offset + @typeInfo(HardwareUnsignedIntegerType).Enum.fields.len;
|
const offset = HardwareUnsignedIntegerType.offset + @typeInfo(HardwareUnsignedIntegerType).Enum.fields.len;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) !Declaration.Index {
|
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) !Type.Index {
|
||||||
inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| {
|
inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| {
|
||||||
_ = try module.types.append(compilation.base_allocator, @unionInit(Type, enum_field.name, {}));
|
_ = try module.types.append(compilation.base_allocator, @unionInit(Type, enum_field.name, {}));
|
||||||
}
|
}
|
||||||
@ -596,25 +667,17 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package)
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = try module.values.append(compilation.base_allocator, .{
|
_ = try module.values.append(compilation.base_allocator, .{
|
||||||
.type = .{
|
.bool = false,
|
||||||
.bool_false = {},
|
|
||||||
},
|
|
||||||
.is_const = true,
|
|
||||||
.is_comptime = true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
_ = try module.values.append(compilation.base_allocator, .{
|
_ = try module.values.append(compilation.base_allocator, .{
|
||||||
.type = .{
|
.bool = true,
|
||||||
.bool_true = {},
|
|
||||||
},
|
|
||||||
.is_const = true,
|
|
||||||
.is_comptime = true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return analyzeExistingPackage(compilation, module, package);
|
return analyzeExistingPackage(compilation, module, package);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, package: *Package) !Declaration.Index {
|
pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, package: *Package) !Type.Index {
|
||||||
const package_import = try module.importPackage(compilation.base_allocator, package);
|
const package_import = try module.importPackage(compilation.base_allocator, package);
|
||||||
assert(!package_import.is_new);
|
assert(!package_import.is_new);
|
||||||
const package_file = package_import.file;
|
const package_file = package_import.file;
|
||||||
@ -622,7 +685,7 @@ pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, packag
|
|||||||
return try analyzeFile(compilation.base_allocator, module, package_file);
|
return try analyzeFile(compilation.base_allocator, module, package_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Declaration.Index {
|
pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Type.Index {
|
||||||
assert(file.status == .parsed);
|
assert(file.status == .parsed);
|
||||||
|
|
||||||
var analyzer = Analyzer{
|
var analyzer = Analyzer{
|
||||||
@ -634,7 +697,7 @@ pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Declarat
|
|||||||
.module = module,
|
.module = module,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = try analyzer.structDeclaration(Scope.Index.invalid, try mainNodeToContainerDeclaration(allocator, file), .{ .value = 0 });
|
const result = try analyzer.structType(Scope.Index.invalid, try mainNodeToContainerDeclaration(allocator, file), .{ .value = 0 });
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,10 @@ pub const Result = struct {
|
|||||||
time: u64,
|
time: u64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Options = packed struct {
|
||||||
|
is_comptime: bool,
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: pack it to be more efficient
|
// TODO: pack it to be more efficient
|
||||||
pub const Node = packed struct(u128) {
|
pub const Node = packed struct(u128) {
|
||||||
token: u32,
|
token: u32,
|
||||||
@ -68,6 +72,8 @@ pub const Node = packed struct(u128) {
|
|||||||
function_definition = 16,
|
function_definition = 16,
|
||||||
keyword_noreturn = 17,
|
keyword_noreturn = 17,
|
||||||
keyword_true = 18,
|
keyword_true = 18,
|
||||||
|
comptime_block_zero = 19,
|
||||||
|
comptime_block_one = 20,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,7 +117,7 @@ const Analyzer = struct {
|
|||||||
.fixed_keyword_comptime => switch (analyzer.tokens[analyzer.token_i + 1].id) {
|
.fixed_keyword_comptime => switch (analyzer.tokens[analyzer.token_i + 1].id) {
|
||||||
.left_brace => blk: {
|
.left_brace => blk: {
|
||||||
analyzer.token_i += 1;
|
analyzer.token_i += 1;
|
||||||
const comptime_block = try analyzer.block();
|
const comptime_block = try analyzer.block(.{ .is_comptime = true });
|
||||||
|
|
||||||
break :blk .{
|
break :blk .{
|
||||||
.id = .@"comptime",
|
.id = .@"comptime",
|
||||||
@ -181,7 +187,9 @@ const Analyzer = struct {
|
|||||||
fn function(analyzer: *Analyzer) !Node.Index {
|
fn function(analyzer: *Analyzer) !Node.Index {
|
||||||
const token = analyzer.token_i;
|
const token = analyzer.token_i;
|
||||||
const function_prototype = try analyzer.functionPrototype();
|
const function_prototype = try analyzer.functionPrototype();
|
||||||
const function_body = try analyzer.block();
|
const is_comptime = false;
|
||||||
|
_ = is_comptime;
|
||||||
|
const function_body = try analyzer.block(.{ .is_comptime = false });
|
||||||
return analyzer.addNode(.{
|
return analyzer.addNode(.{
|
||||||
.id = .function_definition,
|
.id = .function_definition,
|
||||||
.token = token,
|
.token = token,
|
||||||
@ -223,40 +231,14 @@ const Analyzer = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block(analyzer: *Analyzer) !Node.Index {
|
fn block(analyzer: *Analyzer, options: Options) !Node.Index {
|
||||||
const left_brace = try analyzer.expectToken(.left_brace);
|
const left_brace = try analyzer.expectToken(.left_brace);
|
||||||
const node_heap_top = analyzer.temporal_node_heap.items.len;
|
const node_heap_top = analyzer.temporal_node_heap.items.len;
|
||||||
defer analyzer.temporal_node_heap.shrinkRetainingCapacity(node_heap_top);
|
defer analyzer.temporal_node_heap.shrinkRetainingCapacity(node_heap_top);
|
||||||
|
|
||||||
while (analyzer.tokens[analyzer.token_i].id != .right_brace) {
|
while (analyzer.tokens[analyzer.token_i].id != .right_brace) {
|
||||||
const statement_index = try analyzer.statement();
|
|
||||||
try analyzer.temporal_node_heap.append(analyzer.allocator, statement_index);
|
|
||||||
}
|
|
||||||
_ = try analyzer.expectToken(.right_brace);
|
|
||||||
|
|
||||||
const statement_array = analyzer.temporal_node_heap.items[node_heap_top..];
|
|
||||||
const node: Node = switch (statement_array.len) {
|
|
||||||
0 => .{
|
|
||||||
.id = .block_zero,
|
|
||||||
.token = left_brace,
|
|
||||||
.left = Node.Index.invalid,
|
|
||||||
.right = Node.Index.invalid,
|
|
||||||
},
|
|
||||||
1 => .{
|
|
||||||
.id = .block_one,
|
|
||||||
.token = left_brace,
|
|
||||||
.left = statement_array[0],
|
|
||||||
.right = Node.Index.invalid,
|
|
||||||
},
|
|
||||||
else => |len| std.debug.panic("len: {}", .{len}),
|
|
||||||
};
|
|
||||||
return analyzer.addNode(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn statement(analyzer: *Analyzer) !Node.Index {
|
|
||||||
// TODO: more stuff before
|
|
||||||
const first_statement_token = analyzer.tokens[analyzer.token_i];
|
const first_statement_token = analyzer.tokens[analyzer.token_i];
|
||||||
return switch (first_statement_token.id) {
|
const statement_index = switch (first_statement_token.id) {
|
||||||
.identifier => switch (analyzer.tokens[analyzer.token_i + 1].id) {
|
.identifier => switch (analyzer.tokens[analyzer.token_i + 1].id) {
|
||||||
.colon => {
|
.colon => {
|
||||||
unreachable;
|
unreachable;
|
||||||
@ -269,12 +251,40 @@ const Analyzer = struct {
|
|||||||
break :blk result;
|
break :blk result;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.fixed_keyword_while => try analyzer.whileStatement(),
|
.fixed_keyword_while => try analyzer.whileStatement(options),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
try analyzer.temporal_node_heap.append(analyzer.allocator, statement_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn whileStatement(analyzer: *Analyzer) error{ OutOfMemory, unexpected_token, not_implemented }!Node.Index {
|
_ = try analyzer.expectToken(.right_brace);
|
||||||
|
|
||||||
|
const statement_array = analyzer.temporal_node_heap.items[node_heap_top..];
|
||||||
|
const node: Node = switch (statement_array.len) {
|
||||||
|
0 => .{
|
||||||
|
.id = switch (options.is_comptime) {
|
||||||
|
true => .comptime_block_zero,
|
||||||
|
false => .block_zero,
|
||||||
|
},
|
||||||
|
.token = left_brace,
|
||||||
|
.left = Node.Index.invalid,
|
||||||
|
.right = Node.Index.invalid,
|
||||||
|
},
|
||||||
|
1 => .{
|
||||||
|
.id = switch (options.is_comptime) {
|
||||||
|
true => .comptime_block_one,
|
||||||
|
false => .block_one,
|
||||||
|
},
|
||||||
|
.token = left_brace,
|
||||||
|
.left = statement_array[0],
|
||||||
|
.right = Node.Index.invalid,
|
||||||
|
},
|
||||||
|
else => |len| std.debug.panic("len: {}", .{len}),
|
||||||
|
};
|
||||||
|
return analyzer.addNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn whileStatement(analyzer: *Analyzer, options: Options) error{ OutOfMemory, unexpected_token, not_implemented }!Node.Index {
|
||||||
const while_identifier_index = try analyzer.expectToken(.fixed_keyword_while);
|
const while_identifier_index = try analyzer.expectToken(.fixed_keyword_while);
|
||||||
|
|
||||||
_ = try analyzer.expectToken(.left_parenthesis);
|
_ = try analyzer.expectToken(.left_parenthesis);
|
||||||
@ -282,7 +292,7 @@ const Analyzer = struct {
|
|||||||
const while_condition = try analyzer.expression();
|
const while_condition = try analyzer.expression();
|
||||||
_ = try analyzer.expectToken(.right_parenthesis);
|
_ = try analyzer.expectToken(.right_parenthesis);
|
||||||
|
|
||||||
const while_block = try analyzer.block();
|
const while_block = try analyzer.block(options);
|
||||||
|
|
||||||
return analyzer.addNode(.{
|
return analyzer.addNode(.{
|
||||||
.id = .simple_while,
|
.id = .simple_while,
|
||||||
@ -300,7 +310,7 @@ const Analyzer = struct {
|
|||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
return analyzer.addNode(.{
|
const node = Node{
|
||||||
.id = expression_id,
|
.id = expression_id,
|
||||||
.token = blk: {
|
.token = blk: {
|
||||||
const token_i = analyzer.token_i;
|
const token_i = analyzer.token_i;
|
||||||
@ -309,7 +319,9 @@ const Analyzer = struct {
|
|||||||
},
|
},
|
||||||
.left = expr,
|
.left = expr,
|
||||||
.right = try analyzer.expression(),
|
.right = try analyzer.expression(),
|
||||||
});
|
};
|
||||||
|
std.debug.print("assign:\nleft: {}.\nright: {}\n", .{ node.left, node.right });
|
||||||
|
return analyzer.addNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compilerIntrinsic(analyzer: *Analyzer) !Node.Index {
|
fn compilerIntrinsic(analyzer: *Analyzer) !Node.Index {
|
||||||
@ -405,7 +417,8 @@ const Analyzer = struct {
|
|||||||
else => try analyzer.curlySuffixExpression(),
|
else => try analyzer.curlySuffixExpression(),
|
||||||
},
|
},
|
||||||
.string_literal, .fixed_keyword_true, .fixed_keyword_false => try analyzer.curlySuffixExpression(),
|
.string_literal, .fixed_keyword_true, .fixed_keyword_false => try analyzer.curlySuffixExpression(),
|
||||||
.left_brace => try analyzer.block(),
|
// todo:?
|
||||||
|
// .left_brace => try analyzer.block(),
|
||||||
else => |id| {
|
else => |id| {
|
||||||
log.warn("By default, calling curlySuffixExpression with {s}", .{@tagName(id)});
|
log.warn("By default, calling curlySuffixExpression with {s}", .{@tagName(id)});
|
||||||
unreachable;
|
unreachable;
|
||||||
@ -519,6 +532,7 @@ const Analyzer = struct {
|
|||||||
.colon => unreachable,
|
.colon => unreachable,
|
||||||
else => blk: {
|
else => blk: {
|
||||||
const identifier = analyzer.getIdentifier(token);
|
const identifier = analyzer.getIdentifier(token);
|
||||||
|
std.debug.print("identifier: {s}\n", .{identifier});
|
||||||
analyzer.token_i += 1;
|
analyzer.token_i += 1;
|
||||||
if (equal(u8, identifier, "_")) {
|
if (equal(u8, identifier, "_")) {
|
||||||
break :blk Node.Index.invalid;
|
break :blk Node.Index.invalid;
|
||||||
@ -549,10 +563,9 @@ const Analyzer = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn addNode(analyzer: *Analyzer, node: Node) !Node.Index {
|
fn addNode(analyzer: *Analyzer, node: Node) !Node.Index {
|
||||||
std.debug.print("Adding node {s}\n", .{@tagName(node.id)});
|
|
||||||
const index = analyzer.nodes.items.len;
|
const index = analyzer.nodes.items.len;
|
||||||
try analyzer.nodes.append(analyzer.allocator, node);
|
try analyzer.nodes.append(analyzer.allocator, node);
|
||||||
|
std.debug.print("Adding node #{} {s}\n", .{ index, @tagName(node.id) });
|
||||||
return Node.Index{
|
return Node.Index{
|
||||||
.value = @intCast(index),
|
.value = @intCast(index),
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user