refactor: remove enum-map lookup wrappers

This commit is contained in:
2026-10-30 11:17:11 +01:00
parent 30a98db379
commit 31a1bf1995
14 changed files with 118 additions and 117 deletions
+3 -1
View File
@@ -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.
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.
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.
+23 -10
View File
@@ -54,7 +54,7 @@ render_function proc(
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| {
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) {
.declaration: try render_statement(renderer, tree, statement, "")
.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")
.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: {
try append_text(renderer, "return")
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 render_block(renderer, tree, conditional.then_block, depth + 1)
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 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) {
binary :: ast.binary_expr(tree, value)
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, "= ")
value = binary.right
} else {
@@ -126,7 +142,7 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
else try append_text(renderer, "_")
if (loop.index_capture) {
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, "_")
}
try append_text(renderer, "|\n")
@@ -152,7 +168,8 @@ render_statement proc(
try append_text(renderer, " ")
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, "\n")
}
@@ -163,7 +180,7 @@ append_text proc(renderer @mut Renderer, text []u8) void ! mem.AllocError {
}
@hide:file
assignment_operator_texts :: $enummap.init_filled(
assignment_operator_texts :: $enummap.init_default(
ast.BinaryOperator, []u8, "", {
add = "+",
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)?
}
+10 -9
View File
@@ -1,5 +1,6 @@
import "@std/mem"
import "@std/arraylist"
import "@std/enummap"
import "@source/ir"
import "@source/diagnostics"
@@ -579,7 +580,7 @@ execute proc(
if instruction.type == .float {
value = ir.Value{ type = .float, data = ir.ValueData{ real = -operand.data.real } }
} 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 } }
}
}
@@ -628,7 +629,7 @@ execute proc(
} else if ir.unsigned_integer(instruction.type) {
result :: checked_unsigned(instruction.op, left.data.unsigned, right.data.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 } }
} else {
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
@@ -636,7 +637,7 @@ execute proc(
} else {
result :: checked_integer(instruction.op, left.data.integer, right.data.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 } }
} else {
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
if (op == .shift_left_saturating) {
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
if (count >= width) 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 (!(real > -1.0 and real < 18446744073709551616.0)) return null
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 }
} else {
if (!(real >= -9223372036854775808.0 and real < 9223372036854775808.0)) return null
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 }
}
return result
}
if (ir.unsigned_integer(source.type)) {
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 }
else result.data = ir.ValueData{ integer = i64(integer) }
} else {
integer :: source.data.integer
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) }
} 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 }
}
}
+7 -15
View File
@@ -35,7 +35,7 @@ aggregate proc(type Type) bool {
}
@hide:file
integer_widths :: $enummap.init_filled(Type, usize, usize(0), {
integer_widths :: $enummap.init_default(Type, usize, usize(0), {
i8 = 8, u8 = 8,
i16 = 16, u16 = 16,
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
}
@hide:file
integer_maxima :: $enummap.init_filled(Type, u64, u64(0), {
#! maxima for supported integer storage types; noninteger keys are absent.
integer_maxima :: $enummap.init_default(Type, u64, u64(0), {
i8 = 127,
i16 = 32767,
i32 = 2147483647,
@@ -64,12 +64,8 @@ integer_maxima :: $enummap.init_filled(Type, u64, u64(0), {
u64 = maxval!(u64), usize = maxval!(u64),
})
integer_max proc(type Type) u64 {
return enummap.get(&integer_maxima, type)?
}
@hide:file
integer_minima :: $enummap.init_filled(Type, i64, i64(0), {
#! minima for supported integer storage types; noninteger keys are absent.
integer_minima :: $enummap.init_default(Type, i64, i64(0), {
i8 = i64(-128),
i16 = i64(-32768),
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,
})
integer_min proc(type Type) i64 {
return enummap.get(&integer_minima, type)?
}
ValueData :: union {
integer i64
# 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
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,
})
@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,
string = 16,
bool = 1,
+11 -11
View File
@@ -4,14 +4,14 @@ import "@source/ir"
import "@source/diagnostics"
@hide:file
bitwise_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
bitwise_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
bit_and = "and",
bit_or = "or",
bit_xor = "xor",
})
@hide:file
float_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
float_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
add = "fadd",
subtract = "fsub",
multiply = "fmul",
@@ -19,14 +19,14 @@ float_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
})
@hide:file
integer_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
integer_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
add = "add",
subtract = "sub",
multiply = "mul",
})
@hide:file
float_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
float_predicates :: $enummap.init_default(ir.Op, []u8, "", {
equal = "oeq",
not_equal = "une",
less = "olt",
@@ -36,7 +36,7 @@ float_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
})
@hide:file
integer_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
integer_predicates :: $enummap.init_default(ir.Op, []u8, "", {
equal = "eq",
not_equal = "ne",
less = "lt",
@@ -46,7 +46,7 @@ integer_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
})
@hide:file
conversion_upper_bounds :: $enummap.init_filled(ir.Type, []u8, "", {
conversion_upper_bounds :: $enummap.init_default(ir.Type, []u8, "", {
i8 = "1.280000e+02",
i16 = "3.276800e+04",
i32 = "2.147483648e+09",
@@ -387,7 +387,7 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
minimum :: fresh(state)
negative_one :: 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{} = and i1 %r{}, %r{}\n", output, {overflow, minimum, negative_one})
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) {
negative :: fresh(state)
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 {
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)
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)) {
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)
}
if (destination_bits < source_bits or (source_unsigned and !destination_unsigned and destination_bits == source_bits)) {
failed :: fresh(state)
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)
}
if (source_bits == destination_bits) return operand
+5 -10
View File
@@ -71,7 +71,7 @@ emit proc(
.invalid_shift_count,
]
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 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
abi_extensions :: $enummap.init_filled(ir.Type, []u8, "", {
abi_extensions :: $enummap.init_default(ir.Type, []u8, "", {
i8 = "signext",
i16 = "signext",
u8 = "zeroext",
@@ -502,7 +502,7 @@ check_local proc(state @State, slot u32, read bool, span diagnostics.Span) void
}
@hide:file
memory_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
memory_symbols :: $enummap.init_default(diagnostics.Code, []u8, "", {
null_unwrap = "null_unwrap",
invalid_slice_bounds = "invalid_slice_bounds",
sentinel_mismatch = "sentinel_mismatch",
@@ -514,17 +514,12 @@ memory_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
})
@hide:file
trap_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
trap_symbols :: $enummap.init_default(diagnostics.Code, []u8, "", {
integer_overflow = "overflow",
index_out_of_bounds = "bounds",
inactive_union_field = "inactive_union",
})
@hide:file
memory_symbol proc(code diagnostics.Code) []u8 {
return enummap.get(&memory_symbols, code)?
}
@hide:file
trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.WriteError {
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
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)
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})
}
+11 -16
View File
@@ -183,7 +183,7 @@ lower_module proc(
}
@hide:file
base_types :: $enummap.init_filled(
base_types :: $enummap.init_default(
typecheck.TypeKind, ir.Type, ir.Type.invalid, {
int = .int,
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),
})
} 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),
})
_ = try emit(state, ir.Instruction{
@@ -1056,7 +1056,7 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
}
@hide:file
binary_ops :: $enummap.init_filled(
binary_ops :: $enummap.init_default(
ast.BinaryOperator, ir.Op, ir.Op.constant, {
add = .add,
subtract = .subtract,
@@ -1078,7 +1078,7 @@ binary_ops :: $enummap.init_filled(
)
@hide:file
unary_ops :: $enummap.init_filled(
unary_ops :: $enummap.init_default(
ast.UnaryOperator, ir.Op, ir.Op.constant, {
negate = .negate,
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
lower_logical proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError | diagnostics.CapacityError) {
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")) {
type :: lower_type(state, typecheck.type_of(state.checked, id))
value := ir.Value{ type = type }
if (mem.eql(name, "maxval")) value.data = ir.ValueData{ unsigned = ir.integer_max(type) }
else value.data = ir.ValueData{ integer = ir.integer_min(type) }
if (mem.eql(name, "maxval")) value.data = ir.ValueData{ unsigned = enummap.get(&ir.integer_maxima, type)? }
else value.data = ir.ValueData{ integer = enummap.get(&ir.integer_minima, type)? }
return try constant(state, value, span)
}
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) {
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 (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)
@@ -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,
})
}
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 })
}
.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)
type :: lower_type(state, typecheck.type_of(state.checked, id))
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)) {
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 {
value :: strconv.parse_float(state.module.allocator, text) catch |err| {
match err {
+12 -31
View File
@@ -432,7 +432,7 @@ parse_loop_control proc(state @mut State, allow_else bool) ast.NodeId ! ParseErr
@hide:file
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
value := try parse_expr(state)
if (
@@ -939,7 +939,7 @@ parse_function_type proc(state @mut State, start ast.Span, invalid bool) ast.Nod
}
@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,
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,
@@ -951,7 +951,7 @@ starts_type proc(kind lexer.TokenKind) bool {
}
@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,
proc = true, return = true, yield = true, break = true, continue = 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)
token := current(state)
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
operand :: try parse_expr_bp(state, right_bp)
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
}
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
state.next_token += 1
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,
source = left_span.source,
},
binary_operator(token.kind),
enummap.get(&binary_operators, token.kind)?,
left,
right,
)
@@ -1975,7 +1975,7 @@ skip_continuation_newlines proc(state @mut State) void {
and kind != .close_bracket and kind != .open_bracket
and kind != .range and kind != .range_inclusive
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
}
@@ -2122,19 +2122,14 @@ consume_either proc(state @mut State, kinds []lexer.TokenKind) lexer.Token ! Syn
}
@hide:file
prefix_binding_powers :: $enummap.init_filled(
prefix_binding_powers :: $enummap.init_default(
lexer.TokenKind, BindingPowerScalar, BindingPowerScalar(0), {
minus = BindingPowerScalar(50), bang = BindingPowerScalar(50), tilde = BindingPowerScalar(50),
ampersand = BindingPowerScalar(50), try = BindingPowerScalar(50),
})
@hide:file
prefix_binding_power proc(kind lexer.TokenKind) ?BindingPowerScalar {
return enummap.get(&prefix_binding_powers, kind)
}
@hide:file
infix_binding_powers :: $enummap.init_filled(lexer.TokenKind, BindingPower,
infix_binding_powers :: $enummap.init_default(lexer.TokenKind, BindingPower,
BindingPower{ left = BindingPowerScalar(0), right = BindingPowerScalar(0) },
{
or = BindingPower{ left = BindingPowerScalar(2), right = BindingPowerScalar(3) },
@@ -2159,12 +2154,7 @@ infix_binding_powers :: $enummap.init_filled(lexer.TokenKind, BindingPower,
)
@hide:file
infix_binding_power proc(kind lexer.TokenKind) ?BindingPower {
return enummap.get(&infix_binding_powers, kind)
}
@hide:file
binary_operators :: $enummap.init_filled(
binary_operators :: $enummap.init_default(
lexer.TokenKind,
ast.BinaryOperator,
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
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
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,
minus_equal = .subtract,
star_equal = .multiply,
@@ -2213,7 +2198,3 @@ assignment_operators :: $enummap.init_filled(lexer.TokenKind, ast.BinaryOperator
shift_left_saturating_equal = .shift_left_saturating,
})
@hide:file
assignment_operator proc(kind lexer.TokenKind) ?ast.BinaryOperator {
return enummap.get(&assignment_operators, kind)
}
+27
View File
@@ -6,6 +6,7 @@ import "@source/diagnostics"
import "@source/strpool"
import "@source/lexer"
import "@source/ast"
import "@source/ast/renderer"
parses_expression_precedence_through_tree_views test {
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.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))
}
+4 -9
View File
@@ -931,7 +931,7 @@ check_annotation proc(state @mut State, id ast.NodeId, allow_void bool) Type ! (
match binding.kind {
.invalid: type = invalid_type(binding.data.diagnostic)
.builtin: {
type.kind = builtin_kind(binding.data.builtin)
type.kind = enummap.get(&builtin_kinds, binding.data.builtin)?
if (binding.data.builtin == .unavailable)
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) {
@@ -1551,17 +1551,12 @@ builtin_kinds :: $enummap.init(sema.BuiltinType, TypeKind, {
unavailable = .none,
})
@hide:file
builtin_kind proc(type sema.BuiltinType) TypeKind {
return enummap.get(&builtin_kinds, type)?
}
@hide:file
conversion_kind proc(state @State, id ast.NodeId) TypeKind {
callee :: ast.call_expr(state.tree, id).callee
binding :: sema.binding(state.resolution, callee)
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
}
@@ -1589,7 +1584,7 @@ bitwise proc(operator ast.BinaryOperator) bool {
}
@hide:file
integer_widths :: $enummap.init_filled(
integer_widths :: $enummap.init_default(
TypeKind, usize, usize(0), {
i8 = 8, u8 = 8,
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)
if (
!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 evaluate_integer(state, declaration.initializer, allow_local)
+1 -1
View File
@@ -5297,7 +5297,7 @@ replace proc(state @mut State, target, source ast.NodeId) void ! mem.AllocError
}
@hide:file
builtin_identities :: $enummap.init_filled(
builtin_identities :: $enummap.init_default(
sema.BuiltinType,
sema.BuiltinType,
sema.BuiltinType.type, {
+1 -1
View File
@@ -21,7 +21,7 @@ init proc(
#! initializes absent value slots without making their keys present.
#! use fully initialized storage when a sparse map must be materialized at compile time.
init_filled proc(
init_default proc(
$E, $V type,
storage_default V,
values meta.EnumFieldStruct(E, ?V, some!(null)),
+1 -1
View File
@@ -26,7 +26,7 @@ handles_sparse_enum_get test {
}
@hide:file
sparse_optional_values :: $init_filled(TestEnum, ?i16, null, {
sparse_optional_values :: $init_default(TestEnum, ?i16, null, {
ident = some!(null),
int = some!(some!(i16(42))),
})
+2 -2
View File
@@ -9045,11 +9045,11 @@ def check_typeinfo_packages(temporary, executable):
import "@std/enummap"
Key :: enum(i16) { absent = -3, present_null = 8, present_value = 21 }
main proc() i16 {
values :: $(enummap.init_filled(Key, ?i16, null, {
values :: $(enummap.init_default(Key, ?i16, null, {
present_null = some!(null),
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
payload :: enummap.get(&values, .present_null)
if (payload == null or payload? != null) return -2