refactor: remove enum-map lookup wrappers
This commit is contained in:
@@ -241,9 +241,11 @@ AST `NodeId` values are compilation-wide, so declarations in different files can
|
|||||||
|
|
||||||
AST nodes retain a compact 16-byte layout on the bootstrap target: a kind, three 32-bit payload words and one packed flag byte in existing padding. file-local, foreign-callable, variadic and opaque metadata therefore need neither per-node boolean fields nor another allocation. spans and variable-length payloads remain in parallel storage; cloning and replacement copy the flags with the node.
|
AST nodes retain a compact 16-byte layout on the bootstrap target: a kind, three 32-bit payload words and one packed flag byte in existing padding. file-local, foreign-callable, variadic and opaque metadata therefore need neither per-node boolean fields nor another allocation. spans and variable-length payloads remain in parallel storage; cloning and replacement copy the flags with the node.
|
||||||
|
|
||||||
|
the AST renderer displays nested blocks, cleanup bodies/captures and compound operators alongside ordinary statements.
|
||||||
|
|
||||||
identifier interning is compilation-owned: initialize a `strpool.StringPool`, pass it to `lexer.scan` and `sema.resolve` before the diagnostic store, and release it after lowering. scanning interns identifier spellings once per pool; parsing preserves their IDs in compact AST storage, exposed through `ast.name_id`, while retaining source spans for diagnostics. name resolution keys scopes by those IDs and interns builtin names into the same pool, so identifier hashing and equality do not scan spelling bytes. IDs are meaningful only within their originating pool; independent compilations may assign the same numeric ID to different names. interning failures propagate as infrastructure errors rather than producing an invalid identifier ID. IR retains no pool dependency.
|
identifier interning is compilation-owned: initialize a `strpool.StringPool`, pass it to `lexer.scan` and `sema.resolve` before the diagnostic store, and release it after lowering. scanning interns identifier spellings once per pool; parsing preserves their IDs in compact AST storage, exposed through `ast.name_id`, while retaining source spans for diagnostics. name resolution keys scopes by those IDs and interns builtin names into the same pool, so identifier hashing and equality do not scan spelling bytes. IDs are meaningful only within their originating pool; independent compilations may assign the same numeric ID to different names. interning failures propagate as infrastructure errors rather than producing an invalid identifier ID. IR retains no pool dependency.
|
||||||
|
|
||||||
compiler token/operator mappings, scalar type metadata and backend spellings use compile-time `@std/enummap` tables; state-dependent layout and control-flow dispatch remain ordinary code. `enummap.init_filled(E, V, storage_default, fields)` initializes unused value slots so sparse maps can become owned compile-time constants without changing which keys are present. `enummap.get` still performs runtime linear lookup; static construction is not a constant-time lookup guarantee.
|
compiler token/operator mappings, scalar type metadata and backend spellings use compile-time `@std/enummap` tables directly; state-dependent layout and control-flow dispatch remain ordinary code. IR exports `integer_minima` and `integer_maxima` maps for shared integer bounds, with absent entries for noninteger types. `enummap.init_default(E, V, storage_default, fields)` initializes unused value slots so sparse maps can become owned compile-time constants without changing which keys are present. `enummap.get` still performs runtime linear lookup; static construction is not a constant-time lookup guarantee.
|
||||||
|
|
||||||
resolver scopes use `@std/hashmap.AutoHashMap(K, V)`, which shares `init`, `put`, `get` and `deinit` with custom `HashMap` and `StringHashMap`. automatic keys support integers, booleans, enums, distinct types, fixed arrays and records recursively; record padding is ignored. slices, floating-point values, pointers, optionals and unions require explicit hashing and equality through `HashMap` (`StringHashMap` supplies content-based byte-string operations). keys and values are stored by value; referenced resources remain caller-owned. the builtin declaration count is inferred, and builtin ID storage derives its fixed stack-array length from that declaration array rather than repeating a count or initializer.
|
resolver scopes use `@std/hashmap.AutoHashMap(K, V)`, which shares `init`, `put`, `get` and `deinit` with custom `HashMap` and `StringHashMap`. automatic keys support integers, booleans, enums, distinct types, fixed arrays and records recursively; record padding is ignored. slices, floating-point values, pointers, optionals and unions require explicit hashing and equality through `HashMap` (`StringHashMap` supplies content-based byte-string operations). keys and values are stored by value; referenced resources remain caller-owned. the builtin declaration count is inferred, and builtin ID storage derives its fixed stack-array length from that declaration array rather than repeating a count or initializer.
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ render_function proc(
|
|||||||
render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, depth usize) void ! mem.AllocError {
|
render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, depth usize) void ! mem.AllocError {
|
||||||
for 0..ast.block_statement_count(tree, block) |index| {
|
for 0..ast.block_statement_count(tree, block) |index| {
|
||||||
statement :: ast.block_statement(tree, block, index)
|
statement :: ast.block_statement(tree, block, index)
|
||||||
for 0..depth |_| try append_text(renderer, " ")
|
for (0..depth) |_| try append_text(renderer, " ")
|
||||||
match ast.stmt_kind(tree, statement) {
|
match ast.stmt_kind(tree, statement) {
|
||||||
.declaration: try render_statement(renderer, tree, statement, "")
|
.declaration: try render_statement(renderer, tree, statement, "")
|
||||||
.expression: {
|
.expression: {
|
||||||
@@ -65,6 +65,22 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
|||||||
}
|
}
|
||||||
.break_stmt: try append_text(renderer, "break\n")
|
.break_stmt: try append_text(renderer, "break\n")
|
||||||
.continue_stmt: try append_text(renderer, "continue\n")
|
.continue_stmt: try append_text(renderer, "continue\n")
|
||||||
|
.block_stmt: {
|
||||||
|
try append_text(renderer, "block\n")
|
||||||
|
try render_block(renderer, tree, ast.statement_block(tree, statement), depth + 1)
|
||||||
|
}
|
||||||
|
.defer_stmt, .errdefer_stmt: {
|
||||||
|
cleanup :: ast.cleanup_stmt(tree, statement)
|
||||||
|
keyword :: if (ast.stmt_kind(tree, statement) == .errdefer_stmt) "errdefer" else "defer"
|
||||||
|
try append_text(renderer, keyword)
|
||||||
|
if (cleanup.capture) |capture| {
|
||||||
|
try append_text(renderer, " |")
|
||||||
|
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, capture)))
|
||||||
|
try append_text(renderer, "|")
|
||||||
|
}
|
||||||
|
try append_text(renderer, "\n")
|
||||||
|
try render_block(renderer, tree, cleanup.body, depth + 1)
|
||||||
|
}
|
||||||
.return_stmt: {
|
.return_stmt: {
|
||||||
try append_text(renderer, "return")
|
try append_text(renderer, "return")
|
||||||
if (ast.return_stmt(tree, statement).value) |value| {
|
if (ast.return_stmt(tree, statement).value) |value| {
|
||||||
@@ -86,7 +102,7 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
|||||||
try append_text(renderer, "\n")
|
try append_text(renderer, "\n")
|
||||||
try render_block(renderer, tree, conditional.then_block, depth + 1)
|
try render_block(renderer, tree, conditional.then_block, depth + 1)
|
||||||
if (conditional.else_block) |alternative| {
|
if (conditional.else_block) |alternative| {
|
||||||
for 0..depth |_| try append_text(renderer, " ")
|
for (0..depth) |_| try append_text(renderer, " ")
|
||||||
try append_text(renderer, "else\n")
|
try append_text(renderer, "else\n")
|
||||||
try render_block(renderer, tree, alternative, depth + 1)
|
try render_block(renderer, tree, alternative, depth + 1)
|
||||||
}
|
}
|
||||||
@@ -99,7 +115,7 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
|||||||
if (assignment.compound) {
|
if (assignment.compound) {
|
||||||
binary :: ast.binary_expr(tree, value)
|
binary :: ast.binary_expr(tree, value)
|
||||||
try append_text(renderer, " ")
|
try append_text(renderer, " ")
|
||||||
try append_text(renderer, assignment_operator_text(binary.operator))
|
try append_text(renderer, enummap.get(&assignment_operator_texts, binary.operator)?)
|
||||||
try append_text(renderer, "= ")
|
try append_text(renderer, "= ")
|
||||||
value = binary.right
|
value = binary.right
|
||||||
} else {
|
} else {
|
||||||
@@ -126,7 +142,7 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
|||||||
else try append_text(renderer, "_")
|
else try append_text(renderer, "_")
|
||||||
if (loop.index_capture) {
|
if (loop.index_capture) {
|
||||||
try append_text(renderer, ", ")
|
try append_text(renderer, ", ")
|
||||||
if (loop.index) |index| try append_text(renderer, ast.source_text(tree, ast.node_span(tree, index)))
|
if (loop.index) |index_capture| try append_text(renderer, ast.source_text(tree, ast.node_span(tree, index_capture)))
|
||||||
else try append_text(renderer, "_")
|
else try append_text(renderer, "_")
|
||||||
}
|
}
|
||||||
try append_text(renderer, "|\n")
|
try append_text(renderer, "|\n")
|
||||||
@@ -152,7 +168,8 @@ render_statement proc(
|
|||||||
try append_text(renderer, " ")
|
try append_text(renderer, " ")
|
||||||
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, type)))
|
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, type)))
|
||||||
}
|
}
|
||||||
try append_text(renderer, if (statement.mutability == .constant) " :: " else " := ")
|
binding :: if (statement.mutability == .constant) " :: " else " := "
|
||||||
|
try append_text(renderer, binding)
|
||||||
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, statement.initializer)))
|
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, statement.initializer)))
|
||||||
try append_text(renderer, "\n")
|
try append_text(renderer, "\n")
|
||||||
}
|
}
|
||||||
@@ -163,7 +180,7 @@ append_text proc(renderer @mut Renderer, text []u8) void ! mem.AllocError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
assignment_operator_texts :: $enummap.init_filled(
|
assignment_operator_texts :: $enummap.init_default(
|
||||||
ast.BinaryOperator, []u8, "", {
|
ast.BinaryOperator, []u8, "", {
|
||||||
add = "+",
|
add = "+",
|
||||||
subtract = "-",
|
subtract = "-",
|
||||||
@@ -178,7 +195,3 @@ assignment_operator_texts :: $enummap.init_filled(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@hide:file
|
|
||||||
assignment_operator_text proc(operator ast.BinaryOperator) []u8 {
|
|
||||||
return enummap.get(&assignment_operator_texts, operator)?
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "@std/mem"
|
import "@std/mem"
|
||||||
import "@std/arraylist"
|
import "@std/arraylist"
|
||||||
|
import "@std/enummap"
|
||||||
import "@source/ir"
|
import "@source/ir"
|
||||||
import "@source/diagnostics"
|
import "@source/diagnostics"
|
||||||
|
|
||||||
@@ -579,7 +580,7 @@ execute proc(
|
|||||||
if instruction.type == .float {
|
if instruction.type == .float {
|
||||||
value = ir.Value{ type = .float, data = ir.ValueData{ real = -operand.data.real } }
|
value = ir.Value{ type = .float, data = ir.ValueData{ real = -operand.data.real } }
|
||||||
} else {
|
} else {
|
||||||
if (operand.data.integer == ir.integer_min(instruction.type)) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
if (operand.data.integer == enummap.get(&ir.integer_minima, instruction.type)?) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||||
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = -operand.data.integer } }
|
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = -operand.data.integer } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -628,7 +629,7 @@ execute proc(
|
|||||||
} else if ir.unsigned_integer(instruction.type) {
|
} else if ir.unsigned_integer(instruction.type) {
|
||||||
result :: checked_unsigned(instruction.op, left.data.unsigned, right.data.unsigned)
|
result :: checked_unsigned(instruction.op, left.data.unsigned, right.data.unsigned)
|
||||||
if result |unsigned| {
|
if result |unsigned| {
|
||||||
if (unsigned > ir.integer_max(instruction.type)) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
if (unsigned > enummap.get(&ir.integer_maxima, instruction.type)?) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||||
value = ir.Value{ type = instruction.type, data = ir.ValueData{ unsigned = unsigned } }
|
value = ir.Value{ type = instruction.type, data = ir.ValueData{ unsigned = unsigned } }
|
||||||
} else {
|
} else {
|
||||||
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||||
@@ -636,7 +637,7 @@ execute proc(
|
|||||||
} else {
|
} else {
|
||||||
result :: checked_integer(instruction.op, left.data.integer, right.data.integer)
|
result :: checked_integer(instruction.op, left.data.integer, right.data.integer)
|
||||||
if result |result_integer| {
|
if result |result_integer| {
|
||||||
if (result_integer < ir.integer_min(instruction.type) or result_integer > i64(ir.integer_max(instruction.type))) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
if (result_integer < enummap.get(&ir.integer_minima, instruction.type)? or result_integer > i64(enummap.get(&ir.integer_maxima, instruction.type)?)) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||||
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = result_integer } }
|
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = result_integer } }
|
||||||
} else {
|
} else {
|
||||||
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||||
@@ -950,7 +951,7 @@ shift_integer proc(op ir.Op, left ir.Value, count u64) ir.Value {
|
|||||||
negative :: signed and left.data.integer < 0
|
negative :: signed and left.data.integer < 0
|
||||||
if (op == .shift_left_saturating) {
|
if (op == .shift_left_saturating) {
|
||||||
if (bits == 0) return left
|
if (bits == 0) return left
|
||||||
limit :: if (negative) 1 + (mask >> 1) else ir.integer_max(left.type)
|
limit :: if (negative) 1 + (mask >> 1) else enummap.get(&ir.integer_maxima, left.type)?
|
||||||
magnitude :: if (negative) 1 + (bits xor mask) else bits
|
magnitude :: if (negative) 1 + (bits xor mask) else bits
|
||||||
if (count >= width) return integer_from_bits(left.type, limit)
|
if (count >= width) return integer_from_bits(left.type, limit)
|
||||||
if (magnitude > (limit >> count)) return integer_from_bits(left.type, limit)
|
if (magnitude > (limit >> count)) return integer_from_bits(left.type, limit)
|
||||||
@@ -1054,28 +1055,28 @@ convert proc(source ir.Value, target ir.Type) ?ir.Value {
|
|||||||
if (ir.unsigned_integer(target)) {
|
if (ir.unsigned_integer(target)) {
|
||||||
if (!(real > -1.0 and real < 18446744073709551616.0)) return null
|
if (!(real > -1.0 and real < 18446744073709551616.0)) return null
|
||||||
integer :: u64(real)
|
integer :: u64(real)
|
||||||
if (integer > ir.integer_max(target)) return null
|
if (integer > enummap.get(&ir.integer_maxima, target)?) return null
|
||||||
result.data = ir.ValueData{ unsigned = integer }
|
result.data = ir.ValueData{ unsigned = integer }
|
||||||
} else {
|
} else {
|
||||||
if (!(real >= -9223372036854775808.0 and real < 9223372036854775808.0)) return null
|
if (!(real >= -9223372036854775808.0 and real < 9223372036854775808.0)) return null
|
||||||
integer :: i64(real)
|
integer :: i64(real)
|
||||||
if (integer < ir.integer_min(target) or integer > i64(ir.integer_max(target))) return null
|
if (integer < enummap.get(&ir.integer_minima, target)? or integer > i64(enummap.get(&ir.integer_maxima, target)?)) return null
|
||||||
result.data = ir.ValueData{ integer = integer }
|
result.data = ir.ValueData{ integer = integer }
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if (ir.unsigned_integer(source.type)) {
|
if (ir.unsigned_integer(source.type)) {
|
||||||
integer :: source.data.unsigned
|
integer :: source.data.unsigned
|
||||||
if (integer > ir.integer_max(target)) return null
|
if (integer > enummap.get(&ir.integer_maxima, target)?) return null
|
||||||
if (ir.unsigned_integer(target)) result.data = ir.ValueData{ unsigned = integer }
|
if (ir.unsigned_integer(target)) result.data = ir.ValueData{ unsigned = integer }
|
||||||
else result.data = ir.ValueData{ integer = i64(integer) }
|
else result.data = ir.ValueData{ integer = i64(integer) }
|
||||||
} else {
|
} else {
|
||||||
integer :: source.data.integer
|
integer :: source.data.integer
|
||||||
if (ir.unsigned_integer(target)) {
|
if (ir.unsigned_integer(target)) {
|
||||||
if (integer < 0 or u64(integer) > ir.integer_max(target)) return null
|
if (integer < 0 or u64(integer) > enummap.get(&ir.integer_maxima, target)?) return null
|
||||||
result.data = ir.ValueData{ unsigned = u64(integer) }
|
result.data = ir.ValueData{ unsigned = u64(integer) }
|
||||||
} else {
|
} else {
|
||||||
if (integer < ir.integer_min(target) or integer > i64(ir.integer_max(target))) return null
|
if (integer < enummap.get(&ir.integer_minima, target)? or integer > i64(enummap.get(&ir.integer_maxima, target)?)) return null
|
||||||
result.data = ir.ValueData{ integer = integer }
|
result.data = ir.ValueData{ integer = integer }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-15
@@ -35,7 +35,7 @@ aggregate proc(type Type) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
integer_widths :: $enummap.init_filled(Type, usize, usize(0), {
|
integer_widths :: $enummap.init_default(Type, usize, usize(0), {
|
||||||
i8 = 8, u8 = 8,
|
i8 = 8, u8 = 8,
|
||||||
i16 = 16, u16 = 16,
|
i16 = 16, u16 = 16,
|
||||||
i32 = 32, u32 = 32,
|
i32 = 32, u32 = 32,
|
||||||
@@ -52,8 +52,8 @@ unsigned_integer proc(type Type) bool {
|
|||||||
return type == .u8 or type == .u16 or type == .u32 or type == .u64 or type == .usize
|
return type == .u8 or type == .u16 or type == .u32 or type == .u64 or type == .usize
|
||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
#! maxima for supported integer storage types; noninteger keys are absent.
|
||||||
integer_maxima :: $enummap.init_filled(Type, u64, u64(0), {
|
integer_maxima :: $enummap.init_default(Type, u64, u64(0), {
|
||||||
i8 = 127,
|
i8 = 127,
|
||||||
i16 = 32767,
|
i16 = 32767,
|
||||||
i32 = 2147483647,
|
i32 = 2147483647,
|
||||||
@@ -64,12 +64,8 @@ integer_maxima :: $enummap.init_filled(Type, u64, u64(0), {
|
|||||||
u64 = maxval!(u64), usize = maxval!(u64),
|
u64 = maxval!(u64), usize = maxval!(u64),
|
||||||
})
|
})
|
||||||
|
|
||||||
integer_max proc(type Type) u64 {
|
#! minima for supported integer storage types; noninteger keys are absent.
|
||||||
return enummap.get(&integer_maxima, type)?
|
integer_minima :: $enummap.init_default(Type, i64, i64(0), {
|
||||||
}
|
|
||||||
|
|
||||||
@hide:file
|
|
||||||
integer_minima :: $enummap.init_filled(Type, i64, i64(0), {
|
|
||||||
i8 = i64(-128),
|
i8 = i64(-128),
|
||||||
i16 = i64(-32768),
|
i16 = i64(-32768),
|
||||||
i32 = i64(-2147483648),
|
i32 = i64(-2147483648),
|
||||||
@@ -77,10 +73,6 @@ integer_minima :: $enummap.init_filled(Type, i64, i64(0), {
|
|||||||
u8 = 0, u16 = 0, u32 = 0, u64 = 0, usize = 0,
|
u8 = 0, u16 = 0, u32 = 0, u64 = 0, usize = 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
integer_min proc(type Type) i64 {
|
|
||||||
return enummap.get(&integer_minima, type)?
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueData :: union {
|
ValueData :: union {
|
||||||
integer i64
|
integer i64
|
||||||
# source pointers use run-wide handles; internal addresses use frame-local IDs below.
|
# source pointers use run-wide handles; internal addresses use frame-local IDs below.
|
||||||
@@ -421,12 +413,12 @@ aligned_size proc(size, alignment usize) usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
scalar_alignments :: $enummap.init_filled(Type, usize, usize(1), {
|
scalar_alignments :: $enummap.init_default(Type, usize, usize(1), {
|
||||||
float = 8, string = 8, function = 8, pointer = 8,
|
float = 8, string = 8, function = 8, pointer = 8,
|
||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
scalar_sizes :: $enummap.init_filled(Type, usize, usize(0), {
|
scalar_sizes :: $enummap.init_default(Type, usize, usize(0), {
|
||||||
float = 8, function = 8, pointer = 8,
|
float = 8, function = 8, pointer = 8,
|
||||||
string = 16,
|
string = 16,
|
||||||
bool = 1,
|
bool = 1,
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import "@source/ir"
|
|||||||
import "@source/diagnostics"
|
import "@source/diagnostics"
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
bitwise_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
bitwise_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
|
||||||
bit_and = "and",
|
bit_and = "and",
|
||||||
bit_or = "or",
|
bit_or = "or",
|
||||||
bit_xor = "xor",
|
bit_xor = "xor",
|
||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
float_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
float_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
|
||||||
add = "fadd",
|
add = "fadd",
|
||||||
subtract = "fsub",
|
subtract = "fsub",
|
||||||
multiply = "fmul",
|
multiply = "fmul",
|
||||||
@@ -19,14 +19,14 @@ float_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
integer_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
integer_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
|
||||||
add = "add",
|
add = "add",
|
||||||
subtract = "sub",
|
subtract = "sub",
|
||||||
multiply = "mul",
|
multiply = "mul",
|
||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
float_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
float_predicates :: $enummap.init_default(ir.Op, []u8, "", {
|
||||||
equal = "oeq",
|
equal = "oeq",
|
||||||
not_equal = "une",
|
not_equal = "une",
|
||||||
less = "olt",
|
less = "olt",
|
||||||
@@ -36,7 +36,7 @@ float_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
integer_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
integer_predicates :: $enummap.init_default(ir.Op, []u8, "", {
|
||||||
equal = "eq",
|
equal = "eq",
|
||||||
not_equal = "ne",
|
not_equal = "ne",
|
||||||
less = "lt",
|
less = "lt",
|
||||||
@@ -46,7 +46,7 @@ integer_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
conversion_upper_bounds :: $enummap.init_filled(ir.Type, []u8, "", {
|
conversion_upper_bounds :: $enummap.init_default(ir.Type, []u8, "", {
|
||||||
i8 = "1.280000e+02",
|
i8 = "1.280000e+02",
|
||||||
i16 = "3.276800e+04",
|
i16 = "3.276800e+04",
|
||||||
i32 = "2.147483648e+09",
|
i32 = "2.147483648e+09",
|
||||||
@@ -387,7 +387,7 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
|||||||
minimum :: fresh(state)
|
minimum :: fresh(state)
|
||||||
negative_one :: fresh(state)
|
negative_one :: fresh(state)
|
||||||
overflow :: fresh(state)
|
overflow :: fresh(state)
|
||||||
try io.print(" %r{} = icmp eq i{} %r{}, {}\n", output, {minimum, bits, left, ir.integer_min(instruction.type)})
|
try io.print(" %r{} = icmp eq i{} %r{}, {}\n", output, {minimum, bits, left, enummap.get(&ir.integer_minima, instruction.type)?})
|
||||||
try io.print(" %r{} = icmp eq i{} %r{}, -1\n", output, {negative_one, bits, right})
|
try io.print(" %r{} = icmp eq i{} %r{}, -1\n", output, {negative_one, bits, right})
|
||||||
try io.print(" %r{} = and i1 %r{}, %r{}\n", output, {overflow, minimum, negative_one})
|
try io.print(" %r{} = and i1 %r{}, %r{}\n", output, {overflow, minimum, negative_one})
|
||||||
try check_failure(state, overflow, .integer_overflow, instruction.span)
|
try check_failure(state, overflow, .integer_overflow, instruction.span)
|
||||||
@@ -472,9 +472,9 @@ emit_shift proc(state @mut State, instruction ir.Instruction) usize ! io.WriteEr
|
|||||||
if (signed) {
|
if (signed) {
|
||||||
negative :: fresh(state)
|
negative :: fresh(state)
|
||||||
try io.print(" %r{} = icmp slt i{} %r{}, 0\n", output, {negative, bits, left})
|
try io.print(" %r{} = icmp slt i{} %r{}, 0\n", output, {negative, bits, left})
|
||||||
try io.print(" %r{} = select i1 %r{}, i{} {}, i{} {}\n", output, {limit, negative, bits, ir.integer_min(instruction.type), bits, ir.integer_max(instruction.type)})
|
try io.print(" %r{} = select i1 %r{}, i{} {}, i{} {}\n", output, {limit, negative, bits, enummap.get(&ir.integer_minima, instruction.type)?, bits, enummap.get(&ir.integer_maxima, instruction.type)?})
|
||||||
} else {
|
} else {
|
||||||
try io.print(" %r{} = or i{} 0, {}\n", output, {limit, bits, ir.integer_max(instruction.type)})
|
try io.print(" %r{} = or i{} 0, {}\n", output, {limit, bits, enummap.get(&ir.integer_maxima, instruction.type)?})
|
||||||
}
|
}
|
||||||
result :: fresh(state)
|
result :: fresh(state)
|
||||||
try io.print(" %r{} = select i1 %r{}, i{} %r{}, i{} %r{}\n", output, {result, saturate, bits, limit, bits, shifted})
|
try io.print(" %r{} = select i1 %r{}, i{} %r{}, i{} %r{}\n", output, {result, saturate, bits, limit, bits, shifted})
|
||||||
@@ -684,13 +684,13 @@ emit_conversion proc(state @mut State, instruction ir.Instruction) usize ! io.Wr
|
|||||||
}
|
}
|
||||||
if (!source_unsigned and (destination_unsigned or destination_bits < source_bits)) {
|
if (!source_unsigned and (destination_unsigned or destination_bits < source_bits)) {
|
||||||
failed :: fresh(state)
|
failed :: fresh(state)
|
||||||
try io.print(" %r{} = icmp slt i{} %r{}, {}\n", output, {failed, source_bits, operand, ir.integer_min(destination)})
|
try io.print(" %r{} = icmp slt i{} %r{}, {}\n", output, {failed, source_bits, operand, enummap.get(&ir.integer_minima, destination)?})
|
||||||
try check_failure(state, failed, .integer_overflow, instruction.span)
|
try check_failure(state, failed, .integer_overflow, instruction.span)
|
||||||
}
|
}
|
||||||
if (destination_bits < source_bits or (source_unsigned and !destination_unsigned and destination_bits == source_bits)) {
|
if (destination_bits < source_bits or (source_unsigned and !destination_unsigned and destination_bits == source_bits)) {
|
||||||
failed :: fresh(state)
|
failed :: fresh(state)
|
||||||
predicate []u8 :: if (source_unsigned) "ugt" else "sgt"
|
predicate []u8 :: if (source_unsigned) "ugt" else "sgt"
|
||||||
try io.print(" %r{} = icmp {s} i{} %r{}, {}\n", output, {failed, predicate, source_bits, operand, ir.integer_max(destination)})
|
try io.print(" %r{} = icmp {s} i{} %r{}, {}\n", output, {failed, predicate, source_bits, operand, enummap.get(&ir.integer_maxima, destination)?})
|
||||||
try check_failure(state, failed, .integer_overflow, instruction.span)
|
try check_failure(state, failed, .integer_overflow, instruction.span)
|
||||||
}
|
}
|
||||||
if (source_bits == destination_bits) return operand
|
if (source_bits == destination_bits) return operand
|
||||||
|
|||||||
+5
-10
@@ -71,7 +71,7 @@ emit proc(
|
|||||||
.invalid_shift_count,
|
.invalid_shift_count,
|
||||||
]
|
]
|
||||||
for (memory_codes) |code| {
|
for (memory_codes) |code| {
|
||||||
try io.print("@memory_{s} = private constant ", output, {memory_symbol(code)})
|
try io.print("@memory_{s} = private constant ", output, {enummap.get(&memory_symbols, code)?})
|
||||||
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?, false)
|
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?, false)
|
||||||
try io.write_all(output, "\n")
|
try io.write_all(output, "\n")
|
||||||
}
|
}
|
||||||
@@ -212,7 +212,7 @@ write_type proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteE
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
abi_extensions :: $enummap.init_filled(ir.Type, []u8, "", {
|
abi_extensions :: $enummap.init_default(ir.Type, []u8, "", {
|
||||||
i8 = "signext",
|
i8 = "signext",
|
||||||
i16 = "signext",
|
i16 = "signext",
|
||||||
u8 = "zeroext",
|
u8 = "zeroext",
|
||||||
@@ -502,7 +502,7 @@ check_local proc(state @State, slot u32, read bool, span diagnostics.Span) void
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
memory_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
|
memory_symbols :: $enummap.init_default(diagnostics.Code, []u8, "", {
|
||||||
null_unwrap = "null_unwrap",
|
null_unwrap = "null_unwrap",
|
||||||
invalid_slice_bounds = "invalid_slice_bounds",
|
invalid_slice_bounds = "invalid_slice_bounds",
|
||||||
sentinel_mismatch = "sentinel_mismatch",
|
sentinel_mismatch = "sentinel_mismatch",
|
||||||
@@ -514,17 +514,12 @@ memory_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
trap_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
|
trap_symbols :: $enummap.init_default(diagnostics.Code, []u8, "", {
|
||||||
integer_overflow = "overflow",
|
integer_overflow = "overflow",
|
||||||
index_out_of_bounds = "bounds",
|
index_out_of_bounds = "bounds",
|
||||||
inactive_union_field = "inactive_union",
|
inactive_union_field = "inactive_union",
|
||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
|
||||||
memory_symbol proc(code diagnostics.Code) []u8 {
|
|
||||||
return enummap.get(&memory_symbols, code)?
|
|
||||||
}
|
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.WriteError {
|
trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.WriteError {
|
||||||
position :: location(state.line_starts, span)
|
position :: location(state.line_starts, span)
|
||||||
@@ -533,7 +528,7 @@ trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.
|
|||||||
length :: message.len
|
length :: message.len
|
||||||
try io.print(" call void @trap(i64 -1, i64 {}, i64 {}, i64 {}, ptr @", state.output, {usize(span.source), position.line, position.column})
|
try io.print(" call void @trap(i64 -1, i64 {}, i64 {}, i64 {}, ptr @", state.output, {usize(span.source), position.line, position.column})
|
||||||
if (symbol) |name| try io.write_all(state.output, name)
|
if (symbol) |name| try io.write_all(state.output, name)
|
||||||
else try io.print("memory_{s}", state.output, {memory_symbol(code)})
|
else try io.print("memory_{s}", state.output, {enummap.get(&memory_symbols, code)?})
|
||||||
try io.print(", i64 {})\n unreachable\n", state.output, {length})
|
try io.print(", i64 {})\n unreachable\n", state.output, {length})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-16
@@ -183,7 +183,7 @@ lower_module proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
base_types :: $enummap.init_filled(
|
base_types :: $enummap.init_default(
|
||||||
typecheck.TypeKind, ir.Type, ir.Type.invalid, {
|
typecheck.TypeKind, ir.Type, ir.Type.invalid, {
|
||||||
int = .int,
|
int = .int,
|
||||||
usize = .usize,
|
usize = .usize,
|
||||||
@@ -957,7 +957,7 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
|
|||||||
array_type = try array_shape(state, pointer.element), span = ast.node_span(tree, id),
|
array_type = try array_shape(state, pointer.element), span = ast.node_span(tree, id),
|
||||||
})
|
})
|
||||||
} else try emit(state, ir.Instruction{
|
} else try emit(state, ir.Instruction{
|
||||||
op = binary_op(expression.operator), type = type, left = old, right = right,
|
op = enummap.get(&binary_ops, expression.operator)?, type = type, left = old, right = right,
|
||||||
span = ast.node_span(tree, id),
|
span = ast.node_span(tree, id),
|
||||||
})
|
})
|
||||||
_ = try emit(state, ir.Instruction{
|
_ = try emit(state, ir.Instruction{
|
||||||
@@ -1056,7 +1056,7 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
binary_ops :: $enummap.init_filled(
|
binary_ops :: $enummap.init_default(
|
||||||
ast.BinaryOperator, ir.Op, ir.Op.constant, {
|
ast.BinaryOperator, ir.Op, ir.Op.constant, {
|
||||||
add = .add,
|
add = .add,
|
||||||
subtract = .subtract,
|
subtract = .subtract,
|
||||||
@@ -1078,7 +1078,7 @@ binary_ops :: $enummap.init_filled(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
unary_ops :: $enummap.init_filled(
|
unary_ops :: $enummap.init_default(
|
||||||
ast.UnaryOperator, ir.Op, ir.Op.constant, {
|
ast.UnaryOperator, ir.Op, ir.Op.constant, {
|
||||||
negate = .negate,
|
negate = .negate,
|
||||||
logical_not = .logical_not,
|
logical_not = .logical_not,
|
||||||
@@ -1086,11 +1086,6 @@ unary_ops :: $enummap.init_filled(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@hide:file
|
|
||||||
binary_op proc(operator ast.BinaryOperator) ir.Op {
|
|
||||||
return enummap.get(&binary_ops, operator)?
|
|
||||||
}
|
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
lower_logical proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError | diagnostics.CapacityError) {
|
lower_logical proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError | diagnostics.CapacityError) {
|
||||||
expression :: ast.binary_expr(state.tree, id)
|
expression :: ast.binary_expr(state.tree, id)
|
||||||
@@ -1892,8 +1887,8 @@ lower_builtin proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError
|
|||||||
if (mem.eql(name, "maxval") or mem.eql(name, "minval")) {
|
if (mem.eql(name, "maxval") or mem.eql(name, "minval")) {
|
||||||
type :: lower_type(state, typecheck.type_of(state.checked, id))
|
type :: lower_type(state, typecheck.type_of(state.checked, id))
|
||||||
value := ir.Value{ type = type }
|
value := ir.Value{ type = type }
|
||||||
if (mem.eql(name, "maxval")) value.data = ir.ValueData{ unsigned = ir.integer_max(type) }
|
if (mem.eql(name, "maxval")) value.data = ir.ValueData{ unsigned = enummap.get(&ir.integer_maxima, type)? }
|
||||||
else value.data = ir.ValueData{ integer = ir.integer_min(type) }
|
else value.data = ir.ValueData{ integer = enummap.get(&ir.integer_minima, type)? }
|
||||||
return try constant(state, value, span)
|
return try constant(state, value, span)
|
||||||
}
|
}
|
||||||
if (mem.eql(name, "divtrunc")) {
|
if (mem.eql(name, "divtrunc")) {
|
||||||
@@ -2264,9 +2259,9 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
|||||||
}
|
}
|
||||||
if (expression.operator == .negate and ir.integer_bits(type) != 0 and !ir.unsigned_integer(type) and ast.expr_kind(tree, expression.operand) == .literal_int) {
|
if (expression.operator == .negate and ir.integer_bits(type) != 0 and !ir.unsigned_integer(type) and ast.expr_kind(tree, expression.operand) == .literal_int) {
|
||||||
text :: ast.literal_text(tree, expression.operand)
|
text :: ast.literal_text(tree, expression.operand)
|
||||||
magnitude_limit :: ir.integer_max(type) + 1
|
magnitude_limit :: enummap.get(&ir.integer_maxima, type)? + 1
|
||||||
if (integer_magnitude(text, magnitude_limit)) |magnitude| {
|
if (integer_magnitude(text, magnitude_limit)) |magnitude| {
|
||||||
if (magnitude == magnitude_limit) return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = ir.integer_min(type) } }, span)
|
if (magnitude == magnitude_limit) return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = enummap.get(&ir.integer_minima, type)? } }, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
operand :: try lower_expression(state, expression.operand)
|
operand :: try lower_expression(state, expression.operand)
|
||||||
@@ -2302,7 +2297,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
|||||||
array_type = try array_shape(state, pointer.element), span = span,
|
array_type = try array_shape(state, pointer.element), span = span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
op :: binary_op(expression.operator)
|
op :: enummap.get(&binary_ops, expression.operator)?
|
||||||
return try emit(state, ir.Instruction{ op = op, type = type, left = left, right = right, span = span })
|
return try emit(state, ir.Instruction{ op = op, type = type, left = left, right = right, span = span })
|
||||||
}
|
}
|
||||||
.call: {
|
.call: {
|
||||||
@@ -2392,9 +2387,9 @@ lower_number proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError
|
|||||||
text :: ast.literal_text(state.tree, id)
|
text :: ast.literal_text(state.tree, id)
|
||||||
type :: lower_type(state, typecheck.type_of(state.checked, id))
|
type :: lower_type(state, typecheck.type_of(state.checked, id))
|
||||||
if (ir.integer_bits(type) != 0 and !ir.unsigned_integer(type)) {
|
if (ir.integer_bits(type) != 0 and !ir.unsigned_integer(type)) {
|
||||||
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = i64(magnitude) } }, span)
|
if (integer_magnitude(text, enummap.get(&ir.integer_maxima, type)?)) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = i64(magnitude) } }, span)
|
||||||
} else if (ir.unsigned_integer(type)) {
|
} else if (ir.unsigned_integer(type)) {
|
||||||
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ unsigned = magnitude } }, span)
|
if (integer_magnitude(text, enummap.get(&ir.integer_maxima, type)?)) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ unsigned = magnitude } }, span)
|
||||||
} else {
|
} else {
|
||||||
value :: strconv.parse_float(state.module.allocator, text) catch |err| {
|
value :: strconv.parse_float(state.module.allocator, text) catch |err| {
|
||||||
match err {
|
match err {
|
||||||
|
|||||||
+12
-31
@@ -432,7 +432,7 @@ parse_loop_control proc(state @mut State, allow_else bool) ast.NodeId ! ParseErr
|
|||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
parse_assignment proc(state @mut State, target ast.NodeId, allow_else bool) ast.NodeId ! ParseError {
|
parse_assignment proc(state @mut State, target ast.NodeId, allow_else bool) ast.NodeId ! ParseError {
|
||||||
operator :: assignment_operator(current(state).kind)
|
operator :: enummap.get(&assignment_operators, current(state).kind)
|
||||||
state.next_token += 1
|
state.next_token += 1
|
||||||
value := try parse_expr(state)
|
value := try parse_expr(state)
|
||||||
if (
|
if (
|
||||||
@@ -939,7 +939,7 @@ parse_function_type proc(state @mut State, start ast.Span, invalid bool) ast.Nod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
type_starters :: $enummap.init_filled(lexer.TokenKind, bool, false, {
|
type_starters :: $enummap.init_default(lexer.TokenKind, bool, false, {
|
||||||
ident = true, open_bracket = true, struct = true, c_struct = true, opaque = true,
|
ident = true, open_bracket = true, struct = true, c_struct = true, opaque = true,
|
||||||
enum = true, union = true, distinct = true, alias = true, match = true, if = true,
|
enum = true, union = true, distinct = true, alias = true, match = true, if = true,
|
||||||
at = true, star = true, question = true, open_paren = true, proc = true, c_func = true,
|
at = true, star = true, question = true, open_paren = true, proc = true, c_func = true,
|
||||||
@@ -951,7 +951,7 @@ starts_type proc(kind lexer.TokenKind) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
member_names :: $enummap.init_filled(lexer.TokenKind, bool, false, {
|
member_names :: $enummap.init_default(lexer.TokenKind, bool, false, {
|
||||||
ident = true, if = true, else = true, for = true, inline = true, while = true,
|
ident = true, if = true, else = true, for = true, inline = true, while = true,
|
||||||
proc = true, return = true, yield = true, break = true, continue = true,
|
proc = true, return = true, yield = true, break = true, continue = true,
|
||||||
true = true, false = true, and = true, or = true, xor = true, mut = true,
|
true = true, false = true, and = true, or = true, xor = true, mut = true,
|
||||||
@@ -1388,7 +1388,7 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! Par
|
|||||||
skip_operand_newlines(state)
|
skip_operand_newlines(state)
|
||||||
token := current(state)
|
token := current(state)
|
||||||
expression_start :: state.next_token
|
expression_start :: state.next_token
|
||||||
left := if (prefix_binding_power(token.kind)) |right_bp| {
|
left := if (enummap.get(&prefix_binding_powers, token.kind)) |right_bp| {
|
||||||
state.next_token += 1
|
state.next_token += 1
|
||||||
operand :: try parse_expr_bp(state, right_bp)
|
operand :: try parse_expr_bp(state, right_bp)
|
||||||
operand_span :: ast.builder_span(state.builder, operand)
|
operand_span :: ast.builder_span(state.builder, operand)
|
||||||
@@ -1518,7 +1518,7 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! Par
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (token.kind == .pipe and !state.allow_bit_or) break
|
if (token.kind == .pipe and !state.allow_bit_or) break
|
||||||
if (infix_binding_power(token.kind)) |power| {
|
if (enummap.get(&infix_binding_powers, token.kind)) |power| {
|
||||||
if (power.left < min_bp) break
|
if (power.left < min_bp) break
|
||||||
state.next_token += 1
|
state.next_token += 1
|
||||||
right :: try parse_expr_bp(state, power.right)
|
right :: try parse_expr_bp(state, power.right)
|
||||||
@@ -1531,7 +1531,7 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! Par
|
|||||||
end = right_span.end,
|
end = right_span.end,
|
||||||
source = left_span.source,
|
source = left_span.source,
|
||||||
},
|
},
|
||||||
binary_operator(token.kind),
|
enummap.get(&binary_operators, token.kind)?,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
)
|
)
|
||||||
@@ -1975,7 +1975,7 @@ skip_continuation_newlines proc(state @mut State) void {
|
|||||||
and kind != .close_bracket and kind != .open_bracket
|
and kind != .close_bracket and kind != .open_bracket
|
||||||
and kind != .range and kind != .range_inclusive
|
and kind != .range and kind != .range_inclusive
|
||||||
and kind != .pipe and kind != .bang and kind != .catch
|
and kind != .pipe and kind != .bang and kind != .catch
|
||||||
and infix_binding_power(kind) == null
|
and enummap.get(&infix_binding_powers, kind) == null
|
||||||
) state.next_token = start
|
) state.next_token = start
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2122,19 +2122,14 @@ consume_either proc(state @mut State, kinds []lexer.TokenKind) lexer.Token ! Syn
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
prefix_binding_powers :: $enummap.init_filled(
|
prefix_binding_powers :: $enummap.init_default(
|
||||||
lexer.TokenKind, BindingPowerScalar, BindingPowerScalar(0), {
|
lexer.TokenKind, BindingPowerScalar, BindingPowerScalar(0), {
|
||||||
minus = BindingPowerScalar(50), bang = BindingPowerScalar(50), tilde = BindingPowerScalar(50),
|
minus = BindingPowerScalar(50), bang = BindingPowerScalar(50), tilde = BindingPowerScalar(50),
|
||||||
ampersand = BindingPowerScalar(50), try = BindingPowerScalar(50),
|
ampersand = BindingPowerScalar(50), try = BindingPowerScalar(50),
|
||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
prefix_binding_power proc(kind lexer.TokenKind) ?BindingPowerScalar {
|
infix_binding_powers :: $enummap.init_default(lexer.TokenKind, BindingPower,
|
||||||
return enummap.get(&prefix_binding_powers, kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
@hide:file
|
|
||||||
infix_binding_powers :: $enummap.init_filled(lexer.TokenKind, BindingPower,
|
|
||||||
BindingPower{ left = BindingPowerScalar(0), right = BindingPowerScalar(0) },
|
BindingPower{ left = BindingPowerScalar(0), right = BindingPowerScalar(0) },
|
||||||
{
|
{
|
||||||
or = BindingPower{ left = BindingPowerScalar(2), right = BindingPowerScalar(3) },
|
or = BindingPower{ left = BindingPowerScalar(2), right = BindingPowerScalar(3) },
|
||||||
@@ -2159,12 +2154,7 @@ infix_binding_powers :: $enummap.init_filled(lexer.TokenKind, BindingPower,
|
|||||||
)
|
)
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
infix_binding_power proc(kind lexer.TokenKind) ?BindingPower {
|
binary_operators :: $enummap.init_default(
|
||||||
return enummap.get(&infix_binding_powers, kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
@hide:file
|
|
||||||
binary_operators :: $enummap.init_filled(
|
|
||||||
lexer.TokenKind,
|
lexer.TokenKind,
|
||||||
ast.BinaryOperator,
|
ast.BinaryOperator,
|
||||||
ast.BinaryOperator.add, {
|
ast.BinaryOperator.add, {
|
||||||
@@ -2189,18 +2179,13 @@ binary_operators :: $enummap.init_filled(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@hide:file
|
|
||||||
binary_operator proc(kind lexer.TokenKind) ast.BinaryOperator {
|
|
||||||
return enummap.get(&binary_operators, kind)?
|
|
||||||
}
|
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
is_assignment_operator proc(kind lexer.TokenKind) bool {
|
is_assignment_operator proc(kind lexer.TokenKind) bool {
|
||||||
return kind == .equal or assignment_operator(kind) != null
|
return kind == .equal or enummap.get(&assignment_operators, kind) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
assignment_operators :: $enummap.init_filled(lexer.TokenKind, ast.BinaryOperator, ast.BinaryOperator.add, {
|
assignment_operators :: $enummap.init_default(lexer.TokenKind, ast.BinaryOperator, ast.BinaryOperator.add, {
|
||||||
plus_equal = .add,
|
plus_equal = .add,
|
||||||
minus_equal = .subtract,
|
minus_equal = .subtract,
|
||||||
star_equal = .multiply,
|
star_equal = .multiply,
|
||||||
@@ -2213,7 +2198,3 @@ assignment_operators :: $enummap.init_filled(lexer.TokenKind, ast.BinaryOperator
|
|||||||
shift_left_saturating_equal = .shift_left_saturating,
|
shift_left_saturating_equal = .shift_left_saturating,
|
||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
|
||||||
assignment_operator proc(kind lexer.TokenKind) ?ast.BinaryOperator {
|
|
||||||
return enummap.get(&assignment_operators, kind)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import "@source/diagnostics"
|
|||||||
import "@source/strpool"
|
import "@source/strpool"
|
||||||
import "@source/lexer"
|
import "@source/lexer"
|
||||||
import "@source/ast"
|
import "@source/ast"
|
||||||
|
import "@source/ast/renderer"
|
||||||
|
|
||||||
parses_expression_precedence_through_tree_views test {
|
parses_expression_precedence_through_tree_views test {
|
||||||
strings := strpool.init(mem.c_allocator)
|
strings := strpool.init(mem.c_allocator)
|
||||||
@@ -2375,3 +2376,29 @@ keeps_type_composition_and_capture_pipes_with_bitwise_or test {
|
|||||||
try testing.expect(ast.while_stmt(&tree, ast.block_statement(&tree, body, 4)).capture != null)
|
try testing.expect(ast.while_stmt(&tree, ast.block_statement(&tree, body, 4)).capture != null)
|
||||||
try testing.expect(ast.for_stmt(&tree, ast.block_statement(&tree, body, 5)).capture != null)
|
try testing.expect(ast.for_stmt(&tree, ast.block_statement(&tree, body, 5)).capture != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renders_compound_updates_in_scoped_statement_bodies test {
|
||||||
|
strings := strpool.init(mem.c_allocator)
|
||||||
|
defer strpool.deinit(&strings)
|
||||||
|
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||||
|
defer diagnostics.deinit(&diagnostic_store)
|
||||||
|
source ::
|
||||||
|
`run proc() void {
|
||||||
|
` value := 1
|
||||||
|
` { value += 2 }
|
||||||
|
` defer { value -= 3 }
|
||||||
|
` errdefer |err| { value xor= 4 }
|
||||||
|
` for (0..2) |item, index| value |= 5
|
||||||
|
`}
|
||||||
|
scan_state := lexer.init(mem.c_allocator)
|
||||||
|
defer lexer.deinit(&scan_state)
|
||||||
|
try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
|
||||||
|
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
|
||||||
|
defer ast.deinit(&tree)
|
||||||
|
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
|
||||||
|
output := renderer.init(mem.c_allocator)
|
||||||
|
defer renderer.deinit(&output)
|
||||||
|
try renderer.render(&output, &tree)
|
||||||
|
expected :: "package\n function run\n declaration value := 1\n block\n assignment value += 2\n defer\n assignment value -= 3\n errdefer |err|\n assignment value xor= 4\n for 0..2 |item, index|\n assignment value |= 5\n"
|
||||||
|
try testing.expect(mem.eql(output.bytes.items, expected))
|
||||||
|
}
|
||||||
|
|||||||
@@ -931,7 +931,7 @@ check_annotation proc(state @mut State, id ast.NodeId, allow_void bool) Type ! (
|
|||||||
match binding.kind {
|
match binding.kind {
|
||||||
.invalid: type = invalid_type(binding.data.diagnostic)
|
.invalid: type = invalid_type(binding.data.diagnostic)
|
||||||
.builtin: {
|
.builtin: {
|
||||||
type.kind = builtin_kind(binding.data.builtin)
|
type.kind = enummap.get(&builtin_kinds, binding.data.builtin)?
|
||||||
if (binding.data.builtin == .unavailable)
|
if (binding.data.builtin == .unavailable)
|
||||||
type = invalid_type(try diagnostics.add(state.diagnostics, .invalid_operand, ast.node_span(state.tree, id)))
|
type = invalid_type(try diagnostics.add(state.diagnostics, .invalid_operand, ast.node_span(state.tree, id)))
|
||||||
if (type.kind == .anyopaque and state.pointer_depth == 0) {
|
if (type.kind == .anyopaque and state.pointer_depth == 0) {
|
||||||
@@ -1551,17 +1551,12 @@ builtin_kinds :: $enummap.init(sema.BuiltinType, TypeKind, {
|
|||||||
unavailable = .none,
|
unavailable = .none,
|
||||||
})
|
})
|
||||||
|
|
||||||
@hide:file
|
|
||||||
builtin_kind proc(type sema.BuiltinType) TypeKind {
|
|
||||||
return enummap.get(&builtin_kinds, type)?
|
|
||||||
}
|
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
conversion_kind proc(state @State, id ast.NodeId) TypeKind {
|
conversion_kind proc(state @State, id ast.NodeId) TypeKind {
|
||||||
callee :: ast.call_expr(state.tree, id).callee
|
callee :: ast.call_expr(state.tree, id).callee
|
||||||
binding :: sema.binding(state.resolution, callee)
|
binding :: sema.binding(state.resolution, callee)
|
||||||
if (binding.kind != .builtin) return .none
|
if (binding.kind != .builtin) return .none
|
||||||
kind :: builtin_kind(binding.data.builtin)
|
kind :: enummap.get(&builtin_kinds, binding.data.builtin)?
|
||||||
return if (numeric_kind(kind)) kind else .none
|
return if (numeric_kind(kind)) kind else .none
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1589,7 +1584,7 @@ bitwise proc(operator ast.BinaryOperator) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
integer_widths :: $enummap.init_filled(
|
integer_widths :: $enummap.init_default(
|
||||||
TypeKind, usize, usize(0), {
|
TypeKind, usize, usize(0), {
|
||||||
i8 = 8, u8 = 8,
|
i8 = 8, u8 = 8,
|
||||||
i16 = 16, u16 = 16,
|
i16 = 16, u16 = 16,
|
||||||
@@ -2512,7 +2507,7 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
|
|||||||
type_binding :: sema.binding(state.resolution, annotation)
|
type_binding :: sema.binding(state.resolution, annotation)
|
||||||
if (
|
if (
|
||||||
!integer_kind(state.types[usize(annotation)].kind)
|
!integer_kind(state.types[usize(annotation)].kind)
|
||||||
and (type_binding.kind != .builtin or !integer_kind(builtin_kind(type_binding.data.builtin)))
|
and (type_binding.kind != .builtin or !integer_kind(enummap.get(&builtin_kinds, type_binding.data.builtin)?))
|
||||||
) return null
|
) return null
|
||||||
}
|
}
|
||||||
return evaluate_integer(state, declaration.initializer, allow_local)
|
return evaluate_integer(state, declaration.initializer, allow_local)
|
||||||
|
|||||||
@@ -5297,7 +5297,7 @@ replace proc(state @mut State, target, source ast.NodeId) void ! mem.AllocError
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
builtin_identities :: $enummap.init_filled(
|
builtin_identities :: $enummap.init_default(
|
||||||
sema.BuiltinType,
|
sema.BuiltinType,
|
||||||
sema.BuiltinType,
|
sema.BuiltinType,
|
||||||
sema.BuiltinType.type, {
|
sema.BuiltinType.type, {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ init proc(
|
|||||||
|
|
||||||
#! initializes absent value slots without making their keys present.
|
#! initializes absent value slots without making their keys present.
|
||||||
#! use fully initialized storage when a sparse map must be materialized at compile time.
|
#! use fully initialized storage when a sparse map must be materialized at compile time.
|
||||||
init_filled proc(
|
init_default proc(
|
||||||
$E, $V type,
|
$E, $V type,
|
||||||
storage_default V,
|
storage_default V,
|
||||||
values meta.EnumFieldStruct(E, ?V, some!(null)),
|
values meta.EnumFieldStruct(E, ?V, some!(null)),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ handles_sparse_enum_get test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
sparse_optional_values :: $init_filled(TestEnum, ?i16, null, {
|
sparse_optional_values :: $init_default(TestEnum, ?i16, null, {
|
||||||
ident = some!(null),
|
ident = some!(null),
|
||||||
int = some!(some!(i16(42))),
|
int = some!(some!(i16(42))),
|
||||||
})
|
})
|
||||||
|
|||||||
+2
-2
@@ -9045,11 +9045,11 @@ def check_typeinfo_packages(temporary, executable):
|
|||||||
import "@std/enummap"
|
import "@std/enummap"
|
||||||
Key :: enum(i16) { absent = -3, present_null = 8, present_value = 21 }
|
Key :: enum(i16) { absent = -3, present_null = 8, present_value = 21 }
|
||||||
main proc() i16 {
|
main proc() i16 {
|
||||||
values :: $(enummap.init_filled(Key, ?i16, null, {
|
values :: $(enummap.init_default(Key, ?i16, null, {
|
||||||
present_null = some!(null),
|
present_null = some!(null),
|
||||||
present_value = some!(some!(i16(42))),
|
present_value = some!(some!(i16(42))),
|
||||||
}))
|
}))
|
||||||
empty :: $(enummap.init_filled(Key, i16, i16(99), {}))
|
empty :: $(enummap.init_default(Key, i16, i16(99), {}))
|
||||||
if (enummap.get(&values, .absent) != null or enummap.get(&empty, .absent) != null) return -1
|
if (enummap.get(&values, .absent) != null or enummap.get(&empty, .absent) != null) return -1
|
||||||
payload :: enummap.get(&values, .present_null)
|
payload :: enummap.get(&values, .present_null)
|
||||||
if (payload == null or payload? != null) return -2
|
if (payload == null or payload? != null) return -2
|
||||||
|
|||||||
Reference in New Issue
Block a user