booleans, comparisons, and if/else
This commit is contained in:
+124
-30
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user