Merge pull request #25 from birth-software/allocator
implement page allocator
This commit is contained in:
commit
5fb06cd515
@ -17,6 +17,7 @@ const StringHashMap = data_structures.StringHashMap;
|
||||
const StringArrayHashMap = data_structures.StringArrayHashMap;
|
||||
|
||||
const lexical_analyzer = @import("frontend/lexical_analyzer.zig");
|
||||
const Token = lexical_analyzer.Token;
|
||||
const syntactic_analyzer = @import("frontend/syntactic_analyzer.zig");
|
||||
const Node = syntactic_analyzer.Node;
|
||||
const semantic_analyzer = @import("frontend/semantic_analyzer.zig");
|
||||
@ -177,7 +178,28 @@ pub fn init(allocator: Allocator) !void {
|
||||
|
||||
pub const Struct = struct {
|
||||
scope: Scope.Index,
|
||||
fields: ArrayList(Field.Index) = .{},
|
||||
fields: ArrayList(ContainerField.Index) = .{},
|
||||
backing_type: Type.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const ContainerField = struct {
|
||||
name: u32,
|
||||
type: Type.Index,
|
||||
default_value: Value.Index,
|
||||
parent: Type.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const ContainerInitialization = struct {
|
||||
field_initializations: ArrayList(Value.Index),
|
||||
type: Type.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
@ -214,27 +236,36 @@ pub const Array = struct {
|
||||
};
|
||||
|
||||
pub const Type = union(enum) {
|
||||
any,
|
||||
void,
|
||||
noreturn,
|
||||
bool,
|
||||
type,
|
||||
comptime_int,
|
||||
integer: Type.Integer,
|
||||
slice: Slice,
|
||||
slice: Type.Slice,
|
||||
pointer: Pointer,
|
||||
@"struct": Struct.Index,
|
||||
@"enum": Enum.Index,
|
||||
function: Function.Prototype.Index,
|
||||
array: Array,
|
||||
optional: Optional,
|
||||
|
||||
const Slice = struct {
|
||||
const Optional = struct {
|
||||
element_type: Type.Index,
|
||||
};
|
||||
const Pointer = struct {
|
||||
|
||||
pub const Slice = struct {
|
||||
element_type: Type.Index,
|
||||
@"const": bool,
|
||||
};
|
||||
|
||||
pub const Pointer = struct {
|
||||
element_type: Type.Index,
|
||||
many: bool,
|
||||
@"const": bool,
|
||||
};
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
@ -282,6 +313,16 @@ pub const Type = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getBitSize(type_info: Type) u64 {
|
||||
return switch (type_info) {
|
||||
.integer => |integer| integer.bit_count,
|
||||
.pointer => 8,
|
||||
.bool => 1,
|
||||
.comptime_int => @panic("This call should never happen"),
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getAlignment(type_info: Type) u64 {
|
||||
return switch (type_info) {
|
||||
.integer => |integer| @min(16, integer.getSize()),
|
||||
@ -290,6 +331,7 @@ pub const Type = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
pub const any = FixedTypeKeyword.any.toType();
|
||||
pub const @"void" = FixedTypeKeyword.void.toType();
|
||||
pub const boolean = FixedTypeKeyword.bool.toType();
|
||||
pub const ssize = FixedTypeKeyword.ssize.toType();
|
||||
@ -318,6 +360,7 @@ pub const Type = union(enum) {
|
||||
|
||||
// Each time an enum is added here, a corresponding insertion in the initialization must be made
|
||||
pub const Intrinsic = enum {
|
||||
//@"asm", this is processed separately as it need special parsing
|
||||
@"error",
|
||||
import,
|
||||
syscall,
|
||||
@ -332,6 +375,7 @@ pub const FixedTypeKeyword = enum {
|
||||
ssize,
|
||||
type,
|
||||
comptime_int,
|
||||
any,
|
||||
|
||||
const offset = 0;
|
||||
|
||||
@ -382,9 +426,10 @@ pub const extra_common_type_data = blk: {
|
||||
|
||||
/// A scope contains a bunch of declarations
|
||||
pub const Scope = struct {
|
||||
declarations: AutoHashMap(u32, Declaration.Index) = .{},
|
||||
declarations: data_structures.AutoArrayHashMap(u32, Declaration.Index) = .{},
|
||||
parent: Scope.Index,
|
||||
file: File.Index,
|
||||
token: Token.Index,
|
||||
type: Type.Index = Type.Index.invalid,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
@ -409,6 +454,7 @@ pub const Declaration = struct {
|
||||
name: u32,
|
||||
argument_index: ?u32,
|
||||
type: Type.Index,
|
||||
scope: Scope.Index,
|
||||
|
||||
pub const Reference = struct {
|
||||
value: Declaration.Index,
|
||||
@ -465,15 +511,6 @@ pub const Block = struct {
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const Field = struct {
|
||||
name: u32,
|
||||
type: Type.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const Loop = struct {
|
||||
condition: Value.Index,
|
||||
body: Value.Index,
|
||||
@ -484,10 +521,6 @@ pub const Loop = struct {
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
const Runtime = struct {
|
||||
foo: u32 = 0,
|
||||
};
|
||||
|
||||
const Unresolved = struct {
|
||||
node_index: Node.Index,
|
||||
};
|
||||
@ -519,6 +552,7 @@ pub const Call = struct {
|
||||
value: Value.Index,
|
||||
arguments: ArgumentList.Index,
|
||||
type: Type.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
@ -560,25 +594,47 @@ pub const BinaryOperation = struct {
|
||||
pub const Id = enum {
|
||||
add,
|
||||
sub,
|
||||
logical_and,
|
||||
logical_xor,
|
||||
logical_or,
|
||||
bit_and,
|
||||
bit_xor,
|
||||
bit_or,
|
||||
multiply,
|
||||
divide,
|
||||
shift_left,
|
||||
shift_right,
|
||||
compare_equal,
|
||||
compare_greater_than,
|
||||
compare_greater_or_equal,
|
||||
compare_less_than,
|
||||
compare_less_or_equal,
|
||||
};
|
||||
};
|
||||
|
||||
pub const UnaryOperation = struct {
|
||||
value: Value.Index,
|
||||
type: Type.Index,
|
||||
id: Id,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
|
||||
pub const Id = enum {
|
||||
boolean_not,
|
||||
negation,
|
||||
address_of,
|
||||
pointer_dereference,
|
||||
};
|
||||
};
|
||||
|
||||
pub const CallingConvention = enum {
|
||||
system_v,
|
||||
naked,
|
||||
};
|
||||
|
||||
pub const Branch = struct {
|
||||
condition: Value.Index,
|
||||
true_expression: Value.Index,
|
||||
false_expression: Value.Index,
|
||||
expression: Value.Index,
|
||||
taken_expression: Value.Index,
|
||||
not_taken_expression: Value.Index,
|
||||
reaches_end: bool,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
@ -586,19 +642,118 @@ pub const Branch = struct {
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const FieldAccess = struct {
|
||||
declaration_reference: Value.Index,
|
||||
field: ContainerField.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const Slice = struct {
|
||||
sliceable: Value.Index,
|
||||
start: Value.Index,
|
||||
end: Value.Index,
|
||||
type: Type.Index,
|
||||
|
||||
pub const Access = struct {
|
||||
value: Value.Index,
|
||||
field: Field,
|
||||
type: Type.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = Slice.Access.List.Index;
|
||||
pub const Allocation = Slice.Access.List.Allocation;
|
||||
};
|
||||
|
||||
pub const Field = enum {
|
||||
ptr,
|
||||
len,
|
||||
};
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const IndexedAccess = struct {
|
||||
indexed_expression: Value.Index,
|
||||
index_expression: Value.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const OptionalCheck = struct {
|
||||
value: Value.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const OptionalUnwrap = struct {
|
||||
value: Value.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const Assembly = struct {
|
||||
pub const Instruction = struct {
|
||||
id: u32,
|
||||
operands: []const Operand,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const Operand = union(enum) {
|
||||
register: u32,
|
||||
number_literal: u64,
|
||||
value_index: Value.Index,
|
||||
};
|
||||
|
||||
pub const Block = struct {
|
||||
instructions: []const Assembly.Instruction.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
pub const Allocation = List.Allocation;
|
||||
};
|
||||
|
||||
pub const x86_64 = struct {
|
||||
pub const Instruction = enum {
|
||||
@"and",
|
||||
call,
|
||||
xor,
|
||||
};
|
||||
|
||||
pub const Register = enum {
|
||||
ebp,
|
||||
rsp,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pub const Value = union(enum) {
|
||||
unresolved: Unresolved,
|
||||
declaration: Declaration.Index,
|
||||
declaration_reference: Declaration.Reference,
|
||||
void,
|
||||
bool: bool,
|
||||
undefined,
|
||||
@"unreachable",
|
||||
pointer_null_literal,
|
||||
optional_null_literal,
|
||||
unresolved: Unresolved,
|
||||
declaration: Declaration.Index,
|
||||
declaration_reference: Declaration.Reference,
|
||||
loop: Loop.Index,
|
||||
function_definition: Function.Index,
|
||||
function_declaration: Function.Index,
|
||||
block: Block.Index,
|
||||
runtime: Runtime,
|
||||
assign: Assignment.Index,
|
||||
type: Type.Index,
|
||||
integer: Integer,
|
||||
@ -613,8 +768,17 @@ pub const Value = union(enum) {
|
||||
sign_extend: Cast.Index,
|
||||
zero_extend: Cast.Index,
|
||||
binary_operation: BinaryOperation.Index,
|
||||
unary_operation: UnaryOperation.Index,
|
||||
branch: Branch.Index,
|
||||
cast: Cast.Index,
|
||||
container_initialization: ContainerInitialization.Index,
|
||||
field_access: FieldAccess.Index,
|
||||
slice_access: Slice.Access.Index,
|
||||
indexed_access: IndexedAccess.Index,
|
||||
optional_check: OptionalCheck.Index,
|
||||
optional_unwrap: OptionalUnwrap.Index,
|
||||
slice: Slice.Index,
|
||||
assembly_block: Assembly.Block.Index,
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
pub const Index = List.Index;
|
||||
@ -632,11 +796,20 @@ pub const Value = union(enum) {
|
||||
|
||||
pub fn isComptime(value: *Value, module: *Module) bool {
|
||||
return switch (value.*) {
|
||||
.bool, .void, .undefined, .function_definition, .type, .enum_field => true,
|
||||
.integer => |integer| integer.type.eq(Type.comptime_int),
|
||||
.call => false,
|
||||
.binary_operation => false,
|
||||
.declaration_reference => |declaration_reference| module.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.get(module.declarations.get(declaration_reference.value).init_value), module),
|
||||
.bool, .void, .undefined, .function_definition, .type, .enum_field => true,
|
||||
// TODO:
|
||||
.call,
|
||||
.syscall,
|
||||
.binary_operation,
|
||||
.container_initialization,
|
||||
.cast,
|
||||
.optional_unwrap,
|
||||
.pointer_null_literal,
|
||||
.indexed_access,
|
||||
=> false,
|
||||
// TODO:
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
}
|
||||
@ -654,73 +827,36 @@ pub const Value = union(enum) {
|
||||
.binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type,
|
||||
.bool => Type.boolean,
|
||||
.declaration => Type.void,
|
||||
.container_initialization => |container_initialization| module.container_initializations.get(container_initialization).type,
|
||||
.syscall => Type.usize,
|
||||
.unary_operation => |unary_operation_index| module.unary_operations.get(unary_operation_index).type,
|
||||
.pointer_null_literal => semantic_analyzer.optional_pointer_to_any_type,
|
||||
.optional_null_literal => semantic_analyzer.optional_any,
|
||||
.field_access => |field_access_index| module.container_fields.get(module.field_accesses.get(field_access_index).field).type,
|
||||
.cast => |cast_index| module.casts.get(cast_index).type,
|
||||
.slice => |slice_index| module.slices.get(slice_index).type,
|
||||
.slice_access => |slice_access_index| module.slice_accesses.get(slice_access_index).type,
|
||||
.optional_check => Type.boolean,
|
||||
.indexed_access => |indexed_access_index| blk: {
|
||||
const indexed_expression = module.values.get(module.indexed_accesses.get(indexed_access_index).indexed_expression);
|
||||
const indexed_expression_type_index = indexed_expression.getType(module);
|
||||
const indexed_expression_type = module.types.get(indexed_expression_type_index);
|
||||
break :blk switch (indexed_expression_type.*) {
|
||||
.slice => |slice| slice.element_type,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// pub fn setType(value: *Value, new_type: Type.Index) void {
|
||||
// switch (value.*) {
|
||||
// .integer => value.integer.type = new_type,
|
||||
// else => |t| @panic(@tagName(t)),
|
||||
// }
|
||||
// }
|
||||
const TypeCheckError = error{
|
||||
integer_size,
|
||||
pointer_many_differ,
|
||||
pointer_element_type_differ,
|
||||
};
|
||||
|
||||
pub fn typeCheck(value: *Value, module: *Module, type_to_check_index: Type.Index) TypeCheckError!void {
|
||||
const value_type_index = value.getType(module);
|
||||
|
||||
if (!value_type_index.eq(type_to_check_index)) {
|
||||
const value_type = module.types.get(value_type_index);
|
||||
const check_type = module.types.get(type_to_check_index);
|
||||
if (std.meta.activeTag(value_type.*) == std.meta.activeTag(check_type.*)) {
|
||||
switch (value_type.*) {
|
||||
.integer => |coercee_int| {
|
||||
if (check_type.integer.getSize() < coercee_int.getSize()) {
|
||||
return error.integer_size;
|
||||
}
|
||||
},
|
||||
.pointer => |coercee_pointer| {
|
||||
if (coercee_pointer.many != check_type.pointer.many) {
|
||||
return error.pointer_many_differ;
|
||||
}
|
||||
|
||||
if (!coercee_pointer.element_type.eq(check_type.pointer.element_type)) {
|
||||
if (check_type.pointer.many) {
|
||||
const coercee_element_type = module.types.get(coercee_pointer.element_type);
|
||||
switch (coercee_element_type.*) {
|
||||
.array => |array| if (!array.element_type.eq(check_type.pointer.element_type)) {
|
||||
return error.pointer_element_type_differ;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
} else {
|
||||
switch (check_type.*) {
|
||||
.integer => {
|
||||
switch (value_type.*) {
|
||||
.comptime_int => switch (value.*) {
|
||||
.integer => value.integer.type = type_to_check_index,
|
||||
.declaration_reference => value.declaration_reference.type = type_to_check_index,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Module = struct {
|
||||
@ -735,7 +871,6 @@ pub const Module = struct {
|
||||
function_definitions: BlockList(Function) = .{},
|
||||
function_declarations: BlockList(Function) = .{},
|
||||
function_prototypes: BlockList(Function.Prototype) = .{},
|
||||
fields: BlockList(Field) = .{},
|
||||
types: BlockList(Type) = .{},
|
||||
blocks: BlockList(Block) = .{},
|
||||
loops: BlockList(Loop) = .{},
|
||||
@ -747,12 +882,28 @@ pub const Module = struct {
|
||||
string_literals: StringKeyMap([]const u8) = .{},
|
||||
enums: BlockList(Enum) = .{},
|
||||
enum_fields: BlockList(Enum.Field) = .{},
|
||||
function_name_map: data_structures.AutoArrayHashMap(Function.Index, u32) = .{},
|
||||
container_fields: BlockList(ContainerField) = .{},
|
||||
container_initializations: BlockList(ContainerInitialization) = .{},
|
||||
function_map: data_structures.AutoArrayHashMap(Function.Index, Declaration.Index) = .{},
|
||||
type_map: data_structures.AutoArrayHashMap(Type.Index, Declaration.Index) = .{},
|
||||
arrays: BlockList(Array) = .{},
|
||||
casts: BlockList(Cast) = .{},
|
||||
binary_operations: BlockList(BinaryOperation) = .{},
|
||||
unary_operations: BlockList(UnaryOperation) = .{},
|
||||
branches: BlockList(Branch) = .{},
|
||||
field_accesses: BlockList(FieldAccess) = .{},
|
||||
slices: BlockList(Slice) = .{},
|
||||
slice_accesses: BlockList(Slice.Access) = .{},
|
||||
indexed_accesses: BlockList(IndexedAccess) = .{},
|
||||
optional_checks: BlockList(OptionalCheck) = .{},
|
||||
optional_unwraps: BlockList(OptionalUnwrap) = .{},
|
||||
assembly_blocks: BlockList(Assembly.Block) = .{},
|
||||
assembly_instructions: BlockList(Assembly.Instruction) = .{},
|
||||
non_primitive_integer_types: data_structures.AutoArrayHashMap(Type.Integer, Type.Index) = .{},
|
||||
string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{},
|
||||
slice_types: data_structures.AutoArrayHashMap(Type.Slice, Type.Index) = .{},
|
||||
pointer_types: data_structures.AutoArrayHashMap(Type.Pointer, Type.Index) = .{},
|
||||
optional_types: data_structures.AutoArrayHashMap(Type.Index, Type.Index) = .{},
|
||||
array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{},
|
||||
entry_point: Function.Index = Function.Index.invalid,
|
||||
descriptor: Descriptor,
|
||||
@ -1074,8 +1225,27 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
|
||||
for (extra_common_type_data) |type_data| {
|
||||
_ = try module.types.append(compilation.base_allocator, type_data);
|
||||
}
|
||||
semantic_analyzer.pointer_to_any_type = (try module.types.append(compilation.base_allocator, .{
|
||||
.pointer = .{
|
||||
.element_type = Type.any,
|
||||
.many = false,
|
||||
.@"const" = true,
|
||||
},
|
||||
})).index;
|
||||
semantic_analyzer.optional_pointer_to_any_type = (try module.types.append(compilation.base_allocator, .{
|
||||
.optional = .{
|
||||
.element_type = semantic_analyzer.pointer_to_any_type,
|
||||
},
|
||||
})).index;
|
||||
semantic_analyzer.optional_any = (try module.types.append(compilation.base_allocator, .{
|
||||
.optional = .{
|
||||
.element_type = Type.any,
|
||||
},
|
||||
})).index;
|
||||
|
||||
semantic_analyzer.unreachable_index = (try module.values.append(compilation.base_allocator, .@"unreachable")).index;
|
||||
semantic_analyzer.pointer_null_index = (try module.values.append(compilation.base_allocator, .pointer_null_literal)).index;
|
||||
semantic_analyzer.optional_null_index = (try module.values.append(compilation.base_allocator, .optional_null_literal)).index;
|
||||
|
||||
const value_allocation = try module.values.append(compilation.base_allocator, .{
|
||||
.unresolved = .{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,10 @@ pub const Token = packed struct(u64) {
|
||||
fixed_keyword_enum = 0x17,
|
||||
fixed_keyword_union = 0x18,
|
||||
fixed_keyword_extern = 0x19,
|
||||
fixed_keyword_null = 0x1a,
|
||||
fixed_keyword_align = 0x1b,
|
||||
fixed_keyword_export = 0x1c,
|
||||
fixed_keyword_cc = 0x1d,
|
||||
keyword_unsigned_integer = 0x1f,
|
||||
keyword_signed_integer = 0x20,
|
||||
bang = '!', // 0x21
|
||||
@ -106,6 +110,10 @@ pub const FixedKeyword = enum {
|
||||
@"enum",
|
||||
@"union",
|
||||
@"extern",
|
||||
null,
|
||||
@"align",
|
||||
@"export",
|
||||
cc,
|
||||
};
|
||||
|
||||
pub const Result = struct {
|
||||
@ -169,10 +177,6 @@ pub fn analyze(allocator: Allocator, text: []const u8, file_index: File.Index) !
|
||||
inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)),
|
||||
} else .identifier;
|
||||
},
|
||||
'(', ')', '{', '}', '[', ']', '=', ';', '#', '@', ',', '.', ':', '>', '<', '!', '+', '-', '*', '\\', '/', '&', '|', '^' => |operator| blk: {
|
||||
index += 1;
|
||||
break :blk @enumFromInt(operator);
|
||||
},
|
||||
'0'...'9' => blk: {
|
||||
// Detect other non-decimal literals
|
||||
if (text[index] == '0' and index + 1 < text.len) {
|
||||
@ -217,6 +221,10 @@ pub fn analyze(allocator: Allocator, text: []const u8, file_index: File.Index) !
|
||||
index += 1;
|
||||
continue;
|
||||
},
|
||||
'(', ')', '{', '}', '[', ']', '=', ';', '#', '@', ',', '.', ':', '>', '<', '!', '+', '-', '*', '\\', '/', '&', '|', '^', '?', '$' => |operator| blk: {
|
||||
index += 1;
|
||||
break :blk @enumFromInt(operator);
|
||||
},
|
||||
else => |ch| {
|
||||
std.debug.panic("NI: '{c}'", .{ch});
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -5,9 +5,10 @@ const Compilation = @import("Compilation.zig");
|
||||
pub const panic = Compilation.panic;
|
||||
|
||||
pub fn main() !void {
|
||||
const allocator = std.heap.page_allocator;
|
||||
const GPA = std.heap.GeneralPurposeAllocator(.{});
|
||||
var gpa = GPA{};
|
||||
|
||||
try Compilation.init(allocator);
|
||||
try Compilation.init(gpa.allocator());
|
||||
}
|
||||
|
||||
test {
|
||||
|
@ -29,6 +29,7 @@ pub fn build(b: *std.Build) !void {
|
||||
const debug_command = switch (@import("builtin").os.tag) {
|
||||
.linux => blk: {
|
||||
const result = b.addSystemCommand(&.{"gf2"});
|
||||
result.addArgs(&.{ "-ex", "set disassembly-flavor intel" });
|
||||
result.addArg("-ex=r");
|
||||
result.addArgs(&.{ "-ex", "up" });
|
||||
result.addArg("--args");
|
||||
|
@ -14,3 +14,8 @@ const Abi = enum{
|
||||
gnu,
|
||||
msvc,
|
||||
};
|
||||
|
||||
const CallingConvention = enum{
|
||||
system_v,
|
||||
naked,
|
||||
};
|
||||
|
@ -5,13 +5,13 @@ const system = switch (current) {
|
||||
.windows => windows,
|
||||
};
|
||||
|
||||
const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [@]const u8, bytes_len: usize) ssize {
|
||||
const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [&]const u8, bytes_len: usize) ssize {
|
||||
switch (current) {
|
||||
.linux => return #syscall(1, file_descriptor, #cast(bytes_ptr), bytes_len),
|
||||
.macos => return macos.write(file_descriptor, #cast(bytes_ptr), bytes_len),
|
||||
.windows => {
|
||||
var written_bytes: u32 = 0;
|
||||
if (windows.WriteFile(file_descriptor, bytes_ptr, bytes_len, @written_bytes, false) != 0) {
|
||||
if (windows.WriteFile(file_descriptor, bytes_ptr, bytes_len, written_bytes.&, false) != 0) {
|
||||
return written_bytes;
|
||||
} else {
|
||||
unreachable;
|
||||
@ -22,7 +22,7 @@ const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [@]const u8, bytes
|
||||
|
||||
const FileDescriptor = system.FileDescriptor;
|
||||
|
||||
const print = fn(bytes_ptr: [@]const u8, bytes_len: usize) void {
|
||||
const print = fn(bytes_ptr: [&]const u8, bytes_len: usize) void {
|
||||
const file_descriptor = switch (current) {
|
||||
.linux, .macos => 2,
|
||||
.windows => windows.GetStdHandle(windows.STD_OUTPUT_HANDLE),
|
||||
@ -41,6 +41,38 @@ const exit = fn(exit_code: s32) noreturn {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const ProtectionFlags = struct(u32){
|
||||
read: bool,
|
||||
write: bool,
|
||||
execute: bool,
|
||||
};
|
||||
|
||||
const MapFlags = struct(u32){
|
||||
reserve: bool,
|
||||
commit: bool,
|
||||
};
|
||||
|
||||
const allocate_virtual_memory = fn(address: ?[&]u8, length: usize, general_protection_flags: ProtectionFlags, general_map_flags: MapFlags) ?[&]u8 {
|
||||
const protection_flags = system.get_protection_flags(flags = general_protection_flags);
|
||||
const map_flags = system.get_map_flags(flags = general_map_flags);
|
||||
|
||||
const result = switch (#import("builtin").os) {
|
||||
.linux => linux.mmap(address, length, protection_flags, map_flags, fd = -1, offset = 0),
|
||||
else => #error("OS not supported"),
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const free_virtual_memory = fn(bytes_ptr: [&]const u8, bytes_len: usize) bool {
|
||||
const result = switch (#import("builtin").os) {
|
||||
.linux => linux.munmap(bytes_ptr, bytes_len),
|
||||
else => #error("OS not supported"),
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const linux = #import("os/linux.nat");
|
||||
const macos = #import("os/macos.nat");
|
||||
const windows = #import("os/windows.nat");
|
||||
|
@ -1 +1,44 @@
|
||||
const std = #import("std");
|
||||
|
||||
const FileDescriptor = s32;
|
||||
|
||||
const ProtectionFlags = struct(u32) {
|
||||
read: bool,
|
||||
write: bool,
|
||||
execute: bool,
|
||||
};
|
||||
|
||||
const MapFlags = struct(u32){
|
||||
shared: bool,
|
||||
private: bool,
|
||||
reserved: u2 = 0,
|
||||
fixed: bool,
|
||||
anonymous: bool,
|
||||
};
|
||||
|
||||
const get_protection_flags = fn(flags: std.os.ProtectionFlags) ProtectionFlags {
|
||||
return ProtectionFlags{
|
||||
.read = flags.read,
|
||||
.write = flags.write,
|
||||
.execute = flags.execute,
|
||||
};
|
||||
}
|
||||
|
||||
const get_map_flags = fn(flags: std.os.MapFlags) MapFlags{
|
||||
return MapFlags{
|
||||
.shared = false,
|
||||
.private = true,
|
||||
.fixed = false,
|
||||
.anonymous = true,
|
||||
};
|
||||
}
|
||||
|
||||
const mmap = fn(address: ?[&]u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, fd: s32, offset: u64) ?[&]u8 {
|
||||
const result = #syscall(9, #cast(address), length, #cast(protection_flags), #cast(map_flags), fd, offset);
|
||||
return #cast(result);
|
||||
}
|
||||
|
||||
const munmap = fn(bytes_ptr: [&]const u8, bytes_len: usize) bool {
|
||||
const result: ssize = #syscall(11, #cast(bytes_ptr), bytes_len);
|
||||
return result == 0;
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
const FileDescriptor = s32;
|
||||
const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [@]const u8, bytes_len: usize) ssize extern;
|
||||
const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [&]const u8, bytes_len: usize) ssize extern;
|
||||
const exit = fn (exit_code: u32) noreturn extern;
|
||||
|
@ -3,7 +3,15 @@ comptime {
|
||||
_ = _start;
|
||||
}
|
||||
|
||||
const _start = fn () noreturn {
|
||||
const _start = fn () noreturn export cc(.naked) {
|
||||
#asm({
|
||||
xor ebp, ebp;
|
||||
and rsp, 0xfffffffffffffff0;
|
||||
call {start};
|
||||
});
|
||||
}
|
||||
|
||||
const start = fn() noreturn export {
|
||||
const result = #import("main").main();
|
||||
std.os.exit(exit_code = result);
|
||||
}
|
||||
|
@ -6,3 +6,83 @@ const builtin = #import("builtin.nat");
|
||||
const os = #import("os.nat");
|
||||
const print = os.print;
|
||||
const start = #import("start.nat");
|
||||
|
||||
const assert = fn(ok: bool) void {
|
||||
if (!ok) {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
const Allocator = struct {
|
||||
handler: &const fn(allocator: &Allocator, old_ptr: ?[&]const u8, old_size: usize, new_size: usize, alignment: u16) ?[&]u8,
|
||||
|
||||
const allocate = fn (allocator: &Allocator, size: usize, alignment: u16) ?[]u8 {
|
||||
if (allocator.handler(allocator, old_ptr = null, old_size = 0, new_size = size, alignment)) |result| {
|
||||
return result[0..size];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const free = fn (allocator: &Allocator, bytes_ptr: [&]const u8, bytes_len: usize) bool {
|
||||
if (allocator.handler(allocator, old_ptr = bytes_ptr, old_size = bytes_len, new_size = 0, alignment = 0)) |_| {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const PageAllocator = struct{
|
||||
allocator: Allocator = .{
|
||||
.handler = handler.&,
|
||||
},
|
||||
|
||||
const allocate = fn (a: &PageAllocator, size: usize, alignment: u16) ?[]u8 {
|
||||
const result = a.allocator.allocate(size, alignment);
|
||||
return result;
|
||||
}
|
||||
|
||||
const free = fn (a: &PageAllocator, bytes_ptr: [&]const u8, bytes_len: usize) bool {
|
||||
const result = a.allocator.free(bytes_ptr, bytes_len);
|
||||
return result;
|
||||
}
|
||||
|
||||
const handler = fn (allocator: &Allocator, maybe_old_ptr: ?[&]const u8, old_size: usize, new_size: usize, alignment: u16) ?[&]u8{
|
||||
var maybe_new_ptr: ?[&]u8 = null;
|
||||
if (new_size > 0) {
|
||||
const general_protection_flags = os.ProtectionFlags{
|
||||
.read = true,
|
||||
.write = true,
|
||||
.execute = false,
|
||||
};
|
||||
const general_map_flags = os.MapFlags{
|
||||
.reserve = true,
|
||||
.commit = true,
|
||||
};
|
||||
|
||||
maybe_new_ptr = os.allocate_virtual_memory(address = null, length = new_size, general_protection_flags, general_map_flags);
|
||||
}
|
||||
|
||||
if (maybe_old_ptr) |old_ptr| {
|
||||
if (maybe_new_ptr) |new_ptr| {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const result = os.free_virtual_memory(bytes_ptr = old_ptr, bytes_len = old_size);
|
||||
if (result) {
|
||||
return #cast(old_ptr);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return maybe_new_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
const getAllocator = fn(page_allocator: &PageAllocator) &Allocator {
|
||||
return page_allocator.allocator.&;
|
||||
}
|
||||
};
|
||||
|
||||
var page_allocator = PageAllocator{};
|
||||
|
19
src/main.nat
Normal file
19
src/main.nat
Normal file
@ -0,0 +1,19 @@
|
||||
const std = #import("std");
|
||||
|
||||
const main = fn() s32 {
|
||||
const size = 0x1000;
|
||||
if (std.page_allocator.allocate(size, alignment = 12)) |result| {
|
||||
result[0] = 0;
|
||||
std.print(bytes_ptr = "Allocation succeeded. Freeing...\n", bytes_len = 33);
|
||||
if (std.page_allocator.free(bytes_ptr = result.ptr, bytes_len = result.len)) {
|
||||
std.print(bytes_ptr = "Memory freed successfully\n", bytes_len = 26);
|
||||
return 0;
|
||||
} else {
|
||||
std.print(bytes_ptr = "Memory freed with errors\n", bytes_len = 25);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
std.print(bytes_ptr = "Allocation failed!\n", bytes_len = 19);
|
||||
return 1;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user