for loops
This commit is contained in:
+102
-1
@@ -20,6 +20,7 @@ Parser :: struct {
|
||||
file: ast.File_Id,
|
||||
cursor: int,
|
||||
delimiter_depth: int,
|
||||
range_disabled: 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
|
||||
@@ -630,6 +631,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
if _, ok := allow(parser, .Right_Paren); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')'")
|
||||
}
|
||||
if expr != ast.INVALID_EXPR && int(expr) < len(parser.module.exprs) {
|
||||
parser.module.exprs[expr].parenthesized = true
|
||||
}
|
||||
return expr
|
||||
case .Invalid:
|
||||
advance(parser)
|
||||
@@ -649,6 +653,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
|
||||
infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
|
||||
#partial switch kind {
|
||||
case .Range, .Range_Inclusive:
|
||||
return 0, 1, true
|
||||
case .Keyword_Orelse:
|
||||
return 2, 3, true
|
||||
case .Keyword_Or:
|
||||
@@ -665,6 +671,7 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
|
||||
|
||||
infix_expr_kind :: proc(kind: token.Kind) -> ast.Expr_Kind {
|
||||
#partial switch kind {
|
||||
case .Range, .Range_Inclusive: return .Range
|
||||
case .Keyword_Orelse: return .Orelse
|
||||
case .Keyword_Or: return .Or
|
||||
case .Keyword_And: return .And
|
||||
@@ -678,6 +685,18 @@ infix_expr_kind :: proc(kind: token.Kind) -> ast.Expr_Kind {
|
||||
}
|
||||
}
|
||||
|
||||
is_simple_range_bound :: proc(expr: ast.Expr) -> bool {
|
||||
if expr.parenthesized {
|
||||
return true
|
||||
}
|
||||
#partial switch expr.kind {
|
||||
case .Integer, .Float, .String, .Bool, .Name:
|
||||
return true
|
||||
case:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
prefix_binding_power :: proc(kind: token.Kind) -> (right: int, ok: bool) {
|
||||
#partial switch kind {
|
||||
case .Minus, .Ampersand, .Bang:
|
||||
@@ -762,7 +781,9 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
if _, ok := allow(parser, .Range); ok {
|
||||
slicing = true
|
||||
} else {
|
||||
parser.range_disabled += 1
|
||||
start_expr = parse_expression_bp(parser, 0, nesting+1)
|
||||
parser.range_disabled -= 1
|
||||
skip_newlines(parser)
|
||||
if _, ok := allow(parser, .Range); ok {
|
||||
slicing = true
|
||||
@@ -770,7 +791,9 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
}
|
||||
skip_newlines(parser)
|
||||
if slicing && current(parser).kind != .Right_Bracket {
|
||||
parser.range_disabled += 1
|
||||
end_expr = parse_expression_bp(parser, 0, nesting+1)
|
||||
parser.range_disabled -= 1
|
||||
skip_newlines(parser)
|
||||
}
|
||||
end_token, ok := allow(parser, .Right_Bracket)
|
||||
@@ -822,6 +845,10 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
continue
|
||||
}
|
||||
left_power, right_power, ok := infix_binding_power(current(parser).kind)
|
||||
if ok && (current(parser).kind == .Range || current(parser).kind == .Range_Inclusive) &&
|
||||
parser.range_disabled > 0 {
|
||||
ok = false
|
||||
}
|
||||
if !ok || left_power < minimum_binding_power {
|
||||
break
|
||||
}
|
||||
@@ -830,9 +857,26 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
right := parse_expression_bp(parser, right_power, nesting+1)
|
||||
left_expr := parser.module.exprs[left]
|
||||
right_expr := parser.module.exprs[right]
|
||||
if operator.kind == .Range || operator.kind == .Range_Inclusive {
|
||||
if !is_simple_range_bound(left_expr) {
|
||||
source.add(
|
||||
parser.diagnostics,
|
||||
left_expr.span,
|
||||
"range bounds with operators must be parenthesized",
|
||||
)
|
||||
}
|
||||
if !is_simple_range_bound(right_expr) {
|
||||
source.add(
|
||||
parser.diagnostics,
|
||||
right_expr.span,
|
||||
"range bounds with operators must be parenthesized",
|
||||
)
|
||||
}
|
||||
}
|
||||
left = add_expr(parser, ast.Expr{
|
||||
kind=infix_expr_kind(operator.kind),
|
||||
span=span_from(left_expr.span, right_expr.span),
|
||||
integer=1 if operator.kind == .Range_Inclusive else 0,
|
||||
left=left,
|
||||
right=right,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
@@ -931,6 +975,9 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
if current(parser).kind == .Keyword_While {
|
||||
return parse_while(parser)
|
||||
}
|
||||
if current(parser).kind == .Keyword_For {
|
||||
return parse_for(parser)
|
||||
}
|
||||
|
||||
if current(parser).kind == .Identifier || current(parser).kind == .Underscore {
|
||||
start_cursor := parser.cursor
|
||||
@@ -1159,7 +1206,7 @@ parse_while_update :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
statement := &parser.module.statements[update]
|
||||
switch statement.kind {
|
||||
case .Assignment, .Expression:
|
||||
case .Invalid, .Declaration, .Return, .If, .While:
|
||||
case .Invalid, .Declaration, .Return, .If, .While, .For:
|
||||
diagnostic := source.add(
|
||||
parser.diagnostics,
|
||||
statement.span,
|
||||
@@ -1187,6 +1234,60 @@ parse_while_update :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
return update
|
||||
}
|
||||
|
||||
parse_for :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
start := advance(parser) // consume 'for'
|
||||
skip_newlines(parser)
|
||||
saved := parser.no_struct_literal
|
||||
parser.no_struct_literal = true
|
||||
iterable := parse_expression(parser)
|
||||
parser.no_struct_literal = saved
|
||||
skip_newlines(parser)
|
||||
|
||||
pointer_capture := false
|
||||
item_name := symbol.INVALID
|
||||
index_name := symbol.INVALID
|
||||
if _, ok := allow(parser, .Pipe); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '|' before for-loop captures")
|
||||
} else {
|
||||
if _, ok := allow(parser, .At); ok {
|
||||
pointer_capture = true
|
||||
}
|
||||
item, item_ok := allow(parser, .Identifier)
|
||||
if item_ok {
|
||||
item_name = item.symbol
|
||||
} else {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected a for-loop item capture")
|
||||
}
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
index, index_ok := allow(parser, .Identifier)
|
||||
if index_ok {
|
||||
index_name = index.symbol
|
||||
} else {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected an index capture after ','")
|
||||
}
|
||||
}
|
||||
if _, ok := allow(parser, .Pipe); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '|' to close for-loop captures")
|
||||
}
|
||||
}
|
||||
skip_newlines(parser)
|
||||
body := parse_block(parser)
|
||||
|
||||
id := ast.stmt_id(len(parser.module.statements))
|
||||
append(&parser.module.statements, ast.Stmt{
|
||||
kind=.For,
|
||||
span=span_from(start.span, previous(parser).span),
|
||||
name=item_name,
|
||||
index_name=index_name,
|
||||
pointer_capture=pointer_capture,
|
||||
expr=iterable,
|
||||
body=body,
|
||||
update=ast.INVALID_STMT,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return id
|
||||
}
|
||||
|
||||
parse_while :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
||||
start := advance(parser) // consume 'while'
|
||||
skip_newlines(parser)
|
||||
|
||||
Reference in New Issue
Block a user