feat: support fallible results and scoped cleanup
This commit is contained in:
+124
-6
@@ -1,17 +1,17 @@
|
||||
#! grammar:
|
||||
#! * type ..... -> identifier {'.' member} | '?' type | ('@' | '*') ['mut'] type | '@proc' '(' parameters ')' type | '[' (expr | '_') ']' ['mut'] type | '[' [';' expr] ']' ['mut'] type | 'struct' '{' fields '}' | 'enum' ['(' type ')'] '{' members '}' | 'union' ['(' (type | 'enum') ')'] '{' fields '}' | ('distinct' | 'alias') type
|
||||
#! * type ..... -> type_sum ['!' type]; type_sum -> type_atom {'|' type_atom}; type_atom includes '(' type ')', identifier {'.' member}, '?' type_atom, ('@' | '*') ['mut'] type_atom, '@proc' '(' parameters ')' type, '[' (expr | '_') ']' ['mut'] type_atom, '[' [';' expr] ']' ['mut'] type_atom, record/enum/union declarations, and ('distinct' | 'alias') type.
|
||||
#! * binding ... -> :: | :=
|
||||
#! * literal .. -> int | float | string | 'true' | 'false' | 'null'
|
||||
#! * op ....... -> '+' | '-' | '*' | '/' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'and' | 'or'
|
||||
#! * expr ..... -> identifier | literal | type | '.' member | '[' [expr {',' expr} [',']] ']' | [type] '{' [initializer {',' initializer} [',']] '}' | '(' expr ')' | ('-' | '!' | '&') expr | expr ('^' | '?') | expr op expr | expr '(' [expr {',' expr} [',']] ')' | identifier '!' '(' [expr {',' expr} [',']] ')' | expr '[' expr ']' | expr '[' [expr] '..' [expr] [';' expr] ']' | expr '.' (member | int) | expr ('..' | '..=') expr | 'match' expr '{' match_arms '}'
|
||||
#! * expr ..... -> identifier | literal | type | '.' member | '[' [expr {',' expr} [',']] ']' | [type] '{' [initializer {',' initializer} [',']] '}' | '(' expr ')' | ('-' | '!' | '&' | 'try') expr | expr ('^' | '?') | expr op expr | expr '(' [expr {',' expr} [',']] ')' | identifier '!' '(' [expr {',' expr} [',']] ')' | expr '[' expr ']' | expr '[' [expr] '..' [expr] [';' expr] ']' | expr '.' (member | int) | expr ('..' | '..=') expr | 'match' expr '{' match_arms '}' | expr 'catch' ['|' identifier '|'] (expr | block)
|
||||
#! * decl ..... -> identifier [type] binding expr | identifier 'c_func' '(' parameters ')' type | ['test'] [identifier '::'] 'import' string
|
||||
#! * block .... -> '{' {stmt} '}'
|
||||
#! * arm ...... -> block | stmt (single statements become canonical blocks)
|
||||
#! * match_arm -> (expr {',' expr} ['|' identifier '|'] | 'else') ':' (block | stmt | expr); arms are comma/newline separated.
|
||||
#! enum members and tagged-union variants accept keyword names; member names do not enter lexical scopes.
|
||||
#! * stmt ..... -> decl | expr | expr '=' expr | 'return' [expr] | 'break' | 'continue' | 'if' expr ['|' identifier '|'] arm ['else' arm] | 'while' expr ['|' identifier '|'] arm | 'for' expr '|' ['@'] identifier [',' identifier] '|' arm
|
||||
#! * stmt ..... -> decl | expr | block | ('defer' | 'errdefer' ['|' identifier '|']) arm | expr '=' expr | 'return' [expr] | 'break' | 'continue' | 'if' expr ['|' identifier '|'] arm ['else' arm] | 'while' expr ['|' identifier '|'] arm | 'for' expr '|' ['@'] identifier [',' identifier] '|' arm
|
||||
#! braceless guards/iterables require parentheses unless they are calls or optional captures; else binds to the nearest if.
|
||||
#! precedence (tightest first): calls/indices/selections/dereference/unwrap, unary -/!/& higher than multiplication/division, addition/subtraction, relational comparisons, equality, and, or, ranges. compound range endpoints require their own parentheses.
|
||||
#! precedence (tightest first): calls/indices/selections/dereference/unwrap, unary -/!/&/try higher than multiplication/division, addition/subtraction, relational comparisons, equality, and, or, ranges/catch. type composition | binds tighter than fallible !; pointer/optional/array prefixes bind to type atoms. compound range endpoints require their own parentheses.
|
||||
#! binary operators associate left; unary operators associate right.
|
||||
|
||||
import "@std/debug"
|
||||
@@ -63,6 +63,10 @@ ExprKind :: enum {
|
||||
slice
|
||||
builtin_call
|
||||
literal_null
|
||||
fallible_type
|
||||
sum_type
|
||||
try_expr
|
||||
catch_expr
|
||||
}
|
||||
|
||||
DeclKind :: enum {
|
||||
@@ -82,6 +86,9 @@ StmtKind :: enum {
|
||||
if_stmt
|
||||
while_stmt
|
||||
for_stmt
|
||||
defer_stmt
|
||||
errdefer_stmt
|
||||
block_stmt
|
||||
invalid
|
||||
}
|
||||
|
||||
@@ -155,6 +162,13 @@ NodeKind :: enum {
|
||||
expr_slice
|
||||
expr_builtin_call
|
||||
literal_null
|
||||
expr_fallible_type
|
||||
expr_sum_type
|
||||
expr_try
|
||||
expr_catch
|
||||
stmt_defer
|
||||
stmt_errdefer
|
||||
stmt_block
|
||||
}
|
||||
|
||||
@hide:file
|
||||
@@ -239,6 +253,20 @@ BinaryExprView :: struct {
|
||||
right NodeId
|
||||
}
|
||||
|
||||
CatchExprView :: struct {
|
||||
span Span
|
||||
operand NodeId
|
||||
capture ?NodeId
|
||||
handler NodeId
|
||||
handler_is_block bool
|
||||
}
|
||||
|
||||
CleanupView :: struct {
|
||||
span Span
|
||||
body NodeId
|
||||
capture ?NodeId
|
||||
}
|
||||
|
||||
CallExprView :: struct {
|
||||
span Span
|
||||
callee NodeId
|
||||
@@ -485,6 +513,41 @@ add_binary proc(
|
||||
}, span)
|
||||
}
|
||||
|
||||
add_binary_type proc(
|
||||
builder @mut Builder, span Span, left, right NodeId, fallible bool,
|
||||
) NodeId ! mem.AllocError {
|
||||
kind NodeKind :: if (fallible) .expr_fallible_type else .expr_sum_type
|
||||
return try add_node(builder, Node{
|
||||
kind = kind,
|
||||
main = NodeData{ binary_operator = .add },
|
||||
data0 = NodeData{ node_id = left },
|
||||
data1 = NodeData{ node_id = right },
|
||||
}, span)
|
||||
}
|
||||
|
||||
add_try_expr proc(builder @mut Builder, span Span, operand NodeId) NodeId ! mem.AllocError {
|
||||
return try add_node(builder, Node{
|
||||
kind = .expr_try,
|
||||
main = NodeData{ node_id = operand },
|
||||
}, span)
|
||||
}
|
||||
|
||||
add_catch_expr proc(
|
||||
builder @mut Builder, span Span, operand NodeId, capture ?NodeId,
|
||||
handler NodeId, handler_is_block bool,
|
||||
) NodeId ! mem.AllocError {
|
||||
capture_id :: if (capture) |id| u32(id) else u32(no_node)
|
||||
block_flag :: if (handler_is_block) u32(1) else u32(0)
|
||||
extra :: try add_extra(builder, capture_id)
|
||||
_ = try add_extra(builder, block_flag)
|
||||
return try add_node(builder, Node{
|
||||
kind = .expr_catch,
|
||||
main = NodeData{ extra = extra },
|
||||
data0 = NodeData{ node_id = operand },
|
||||
data1 = NodeData{ node_id = handler },
|
||||
}, span)
|
||||
}
|
||||
|
||||
add_range proc(
|
||||
builder @mut Builder, span Span, start NodeId, end NodeId, inclusive bool,
|
||||
) NodeId ! mem.AllocError {
|
||||
@@ -735,6 +798,25 @@ add_expression_stmt proc(builder @mut Builder, expression NodeId) NodeId ! mem.A
|
||||
}, builder_span(builder, expression))
|
||||
}
|
||||
|
||||
add_cleanup_stmt proc(
|
||||
builder @mut Builder, span Span, body NodeId, capture ?NodeId, error_only bool,
|
||||
) NodeId ! mem.AllocError {
|
||||
kind NodeKind :: if (error_only) .stmt_errdefer else .stmt_defer
|
||||
capture_id :: if (capture) |id| id else no_node
|
||||
return try add_node(builder, Node{
|
||||
kind = kind,
|
||||
main = NodeData{ node_id = body },
|
||||
data0 = NodeData{ node_id = capture_id },
|
||||
}, span)
|
||||
}
|
||||
|
||||
add_block_stmt proc(builder @mut Builder, body NodeId) NodeId ! mem.AllocError {
|
||||
return try add_node(builder, Node{
|
||||
kind = .stmt_block,
|
||||
main = NodeData{ node_id = body },
|
||||
}, builder_span(builder, body))
|
||||
}
|
||||
|
||||
add_return_stmt proc(builder @mut Builder, span Span, value ?NodeId) NodeId ! mem.AllocError {
|
||||
raw_value :: if (value) |id| id else no_node
|
||||
return try add_node(builder, Node{
|
||||
@@ -1041,6 +1123,9 @@ stmt_kind proc(tree @Tree, id NodeId) StmtKind {
|
||||
.stmt_assignment: .assignment_stmt
|
||||
.stmt_while: .while_stmt
|
||||
.stmt_for: .for_stmt
|
||||
.stmt_defer: .defer_stmt
|
||||
.stmt_errdefer: .errdefer_stmt
|
||||
.stmt_block: .block_stmt
|
||||
.invalid_stmt: .invalid
|
||||
else: unreachable
|
||||
}
|
||||
@@ -1052,6 +1137,22 @@ statement_expr proc(tree @Tree, id NodeId) NodeId {
|
||||
return node.main.node_id
|
||||
}
|
||||
|
||||
cleanup_stmt proc(tree @Tree, id NodeId) CleanupView {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .stmt_defer or node.kind == .stmt_errdefer)
|
||||
return CleanupView{
|
||||
span = get_span(&tree.storage, id),
|
||||
body = node.main.node_id,
|
||||
capture = maybe_node_id(u32(node.data0.node_id)),
|
||||
}
|
||||
}
|
||||
|
||||
statement_block proc(tree @Tree, id NodeId) NodeId {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .stmt_block)
|
||||
return node.main.node_id
|
||||
}
|
||||
|
||||
return_stmt proc(tree @Tree, id NodeId) ReturnView {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .stmt_return)
|
||||
@@ -1234,6 +1335,10 @@ expression_kind proc(kind NodeKind) ExprKind {
|
||||
.expr_slice: .slice
|
||||
.expr_builtin_call: .builtin_call
|
||||
.literal_null: .literal_null
|
||||
.expr_fallible_type: .fallible_type
|
||||
.expr_sum_type: .sum_type
|
||||
.expr_try: .try_expr
|
||||
.expr_catch: .catch_expr
|
||||
else: unreachable
|
||||
}
|
||||
}
|
||||
@@ -1250,7 +1355,7 @@ unary_expr proc(tree @Tree, id NodeId) UnaryExprView {
|
||||
|
||||
binary_expr proc(tree @Tree, id NodeId) BinaryExprView {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .expr_binary)
|
||||
debug.assert(node.kind == .expr_binary or node.kind == .expr_fallible_type or node.kind == .expr_sum_type)
|
||||
return BinaryExprView{
|
||||
span = get_span(&tree.storage, id),
|
||||
operator = node.main.binary_operator,
|
||||
@@ -1259,6 +1364,19 @@ binary_expr proc(tree @Tree, id NodeId) BinaryExprView {
|
||||
}
|
||||
}
|
||||
|
||||
catch_expr proc(tree @Tree, id NodeId) CatchExprView {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .expr_catch)
|
||||
extra :: usize(node.main.extra)
|
||||
return CatchExprView{
|
||||
span = get_span(&tree.storage, id),
|
||||
operand = node.data0.node_id,
|
||||
capture = maybe_node_id(tree.storage.extra.items[extra]),
|
||||
handler = node.data1.node_id,
|
||||
handler_is_block = tree.storage.extra.items[extra + 1] != 0,
|
||||
}
|
||||
}
|
||||
|
||||
range_expr proc(tree @Tree, id NodeId) RangeView {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .expr_range)
|
||||
@@ -1403,7 +1521,7 @@ union_type proc(tree @Tree, id NodeId) UnionTypeView {
|
||||
|
||||
type_operand proc(tree @Tree, id NodeId) NodeId {
|
||||
node :: get_node(&tree.storage, id)
|
||||
debug.assert(node.kind == .expr_distinct_type or node.kind == .expr_alias or node.kind == .expr_optional_type)
|
||||
debug.assert(node.kind == .expr_distinct_type or node.kind == .expr_alias or node.kind == .expr_optional_type or node.kind == .expr_try)
|
||||
return node.main.node_id
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user