booleans, comparisons, and if/else

This commit is contained in:
2026-06-21 20:20:55 +02:00
parent 19e9fbdd4b
commit c90ada608e
13 changed files with 1064 additions and 518 deletions
+124 -30
View File
@@ -20,6 +20,11 @@ Parser :: struct {
file: ast.File_Id,
cursor: int,
delimiter_depth: int,
// Suppresses `Name { ... }` struct-literal parsing at delimiter depth 0 so a
// control-flow condition like `if foo { ... }` does not swallow the block as a
// struct literal. Nested `(`/`[`/call-arg contexts (delimiter_depth > 0) still
// allow struct literals.
no_struct_literal: bool,
}
MAX_EXPRESSION_NESTING :: 256
@@ -90,7 +95,7 @@ is_type_token :: proc(kind: token.Kind) -> bool {
.Keyword_C_Short, .Keyword_C_Ushort, .Keyword_C_Int, .Keyword_C_Uint,
.Keyword_C_Long, .Keyword_C_Ulong, .Keyword_C_Longlong, .Keyword_C_Ulonglong,
.Keyword_C_Float, .Keyword_C_Double, .Keyword_C_Longdouble,
.Keyword_Void, .Keyword_C_Func, .Identifier, .Question, .At, .Star, .Left_Bracket:
.Keyword_Void, .Keyword_Bool, .Keyword_C_Func, .Identifier, .Question, .At, .Star, .Left_Bracket:
return true
}
return false
@@ -291,6 +296,9 @@ parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
case .Keyword_Void:
advance(parser)
return types.VOID
case .Keyword_Bool:
advance(parser)
return types.BOOL
case .Keyword_C_Func:
advance(parser)
if _, ok := allow(parser, .Left_Paren); !ok {
@@ -567,6 +575,16 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Keyword_True, .Keyword_False:
advance(parser)
return add_expr(parser, ast.Expr{
kind=.Bool,
span=tok.span,
integer=1 if tok.kind == .Keyword_True else 0,
left=ast.INVALID_EXPR,
right=ast.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Left_Bracket:
return parse_array_literal(parser, nesting)
case .Identifier:
@@ -583,7 +601,7 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
if current(parser).kind == .Left_Paren {
return parse_call(parser, qualifier, first, name, nesting)
}
if current(parser).kind == .Left_Brace {
if current(parser).kind == .Left_Brace && !(parser.no_struct_literal && parser.delimiter_depth == 0) {
return parse_struct_literal(parser, qualifier, first, name, nesting)
}
return add_expr(parser, ast.Expr{
@@ -633,15 +651,36 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
#partial switch kind {
case .Keyword_Orelse:
return 2, 3, true
case .Keyword_Or:
return 4, 5, true
case .Keyword_And:
return 6, 7, true
case .Equal_Equal, .Bang_Equal, .Less, .Less_Equal, .Greater, .Greater_Equal:
return 8, 9, true
case .Plus:
return 10, 11, true
}
return 0, 0, false
}
infix_expr_kind :: proc(kind: token.Kind) -> ast.Expr_Kind {
#partial switch kind {
case .Keyword_Orelse: return .Orelse
case .Keyword_Or: return .Or
case .Keyword_And: return .And
case .Equal_Equal: return .Eq
case .Bang_Equal: return .Ne
case .Less: return .Lt
case .Less_Equal: return .Le
case .Greater: return .Gt
case .Greater_Equal: return .Ge
case: return .Add
}
}
prefix_binding_power :: proc(kind: token.Kind) -> (right: int, ok: bool) {
#partial switch kind {
case .Minus, .Ampersand:
case .Minus, .Ampersand, .Bang:
return 20, true
}
return 0, false
@@ -663,8 +702,13 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
}
operand := parse_expression_bp(parser, right_power, nesting+1)
operand_expr := parser.module.exprs[operand]
prefix_kind := ast.Expr_Kind.Negate
#partial switch operator.kind {
case .Ampersand: prefix_kind = .Address
case .Bang: prefix_kind = .Not
}
left = add_expr(parser, ast.Expr{
kind=.Address if operator.kind == .Ampersand else .Negate,
kind=prefix_kind,
span=span_from(operator.span, operand_expr.span),
left=operand,
right=ast.INVALID_EXPR,
@@ -787,7 +831,7 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
left_expr := parser.module.exprs[left]
right_expr := parser.module.exprs[right]
left = add_expr(parser, ast.Expr{
kind=.Orelse if operator.kind == .Keyword_Orelse else .Add,
kind=infix_expr_kind(operator.kind),
span=span_from(left_expr.span, right_expr.span),
left=left,
right=right,
@@ -881,6 +925,9 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
if current(parser).kind == .Keyword_Return {
return parse_return(parser)
}
if current(parser).kind == .Keyword_If {
return parse_if(parser)
}
if current(parser).kind == .Identifier || current(parser).kind == .Underscore {
start_cursor := parser.cursor
@@ -995,8 +1042,76 @@ parse_params :: proc(parser: ^Parser) -> ([]ast.Param, bool) {
return params[:], variadic
}
// parse_block parses a brace-delimited statement sequence `{ ... }`, consuming
// both braces, and returns the contained statement ids. Shared by function
// bodies and control-flow blocks.
parse_block :: proc(parser: ^Parser) -> []ast.Stmt_Id {
body: [dynamic]ast.Stmt_Id
body.allocator = parser.module.allocator
if _, ok := allow(parser, .Left_Brace); !ok {
source.add(parser.diagnostics, current(parser).span, "expected '{' to open a block")
return body[:]
}
skip_newlines(parser)
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
append(&body, parse_statement(parser))
if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC {
statement_id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Invalid,
span=current(parser).span,
expr=ast.INVALID_EXPR,
diagnostic=diagnostic,
})
append(&body, statement_id)
}
}
if _, ok := allow(parser, .Right_Brace); !ok {
source.add(parser.diagnostics, current(parser).span, "expected '}' to close a block")
}
return body[:]
}
parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id {
start := advance(parser) // consume 'if'
skip_newlines(parser)
saved := parser.no_struct_literal
parser.no_struct_literal = true
condition := parse_expression(parser)
parser.no_struct_literal = saved
skip_newlines(parser)
then_body := parse_block(parser)
else_body: []ast.Stmt_Id = nil
saved_cursor := parser.cursor
skip_newlines(parser)
if current(parser).kind == .Keyword_Else {
advance(parser)
skip_newlines(parser)
if current(parser).kind == .Keyword_If {
nested := parse_if(parser)
single := make([]ast.Stmt_Id, 1, parser.module.allocator)
single[0] = nested
else_body = single
} else {
else_body = parse_block(parser)
}
} else {
parser.cursor = saved_cursor
}
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.If,
span=span_from(start.span, previous(parser).span),
expr=condition,
body=then_body,
else_body=else_body,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return id
}
parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
func_token := advance(parser)
advance(parser)
if _, ok := allow(parser, .Left_Paren); !ok {
source.add(parser.diagnostics, current(parser).span, "expected '(' after 'func'")
}
@@ -1030,29 +1145,8 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
})
return
}
advance(parser)
body: [dynamic]ast.Stmt_Id
body.allocator = parser.module.allocator
skip_newlines(parser)
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
append(&body, parse_statement(parser))
if diagnostic := finish_statement(parser, true); diagnostic != source.INVALID_DIAGNOSTIC {
statement_id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Invalid,
span=current(parser).span,
expr=ast.INVALID_EXPR,
diagnostic=diagnostic,
})
append(&body, statement_id)
}
}
end = current(parser)
if _, ok := allow(parser, .Right_Brace); !ok {
source.add(parser.diagnostics, current(parser).span, "expected '}' after function body")
end = func_token
}
body := parse_block(parser)
end = previous(parser)
_ = ast.function_id(len(parser.module.functions))
append(&parser.module.functions, ast.Function{
span=span_from(name.span, end.span),
@@ -1064,7 +1158,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool) {
variadic=variadic,
params=params,
result=result,
body=body[:],
body=body,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}