parser: assign.v, containers.v, for.v, if.v
parent
b53fb365a6
commit
59baef89a0
|
@ -0,0 +1,115 @@
|
||||||
|
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
module parser
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
import v.table
|
||||||
|
import v.token
|
||||||
|
|
||||||
|
fn (var p Parser) assign_stmt() ast.Stmt {
|
||||||
|
is_static := p.tok.kind == .key_static
|
||||||
|
if is_static {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
idents := p.parse_assign_lhs()
|
||||||
|
op := p.tok.kind
|
||||||
|
p.next() // :=, =
|
||||||
|
pos := p.tok.position()
|
||||||
|
exprs := p.parse_assign_rhs()
|
||||||
|
is_decl := op == .decl_assign
|
||||||
|
for i, ident in idents {
|
||||||
|
known_var := p.scope.known_var(ident.name)
|
||||||
|
if !is_decl && !known_var {
|
||||||
|
p.error('unknown variable `$ident.name`')
|
||||||
|
}
|
||||||
|
if is_decl && ident.kind != .blank_ident {
|
||||||
|
if p.scope.known_var(ident.name) {
|
||||||
|
p.error('redefinition of `$ident.name`')
|
||||||
|
}
|
||||||
|
if idents.len == exprs.len {
|
||||||
|
p.scope.register(ident.name, ast.Var{
|
||||||
|
name: ident.name
|
||||||
|
expr: exprs[i]
|
||||||
|
is_mut: ident.is_mut || p.inside_for
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
p.scope.register(ident.name, ast.Var{
|
||||||
|
name: ident.name
|
||||||
|
is_mut: ident.is_mut || p.inside_for
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.AssignStmt{
|
||||||
|
left: idents
|
||||||
|
right: exprs
|
||||||
|
op: op
|
||||||
|
pos: pos
|
||||||
|
is_static: is_static
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: is it possible to merge with AssignStmt?
|
||||||
|
pub fn (var p Parser) assign_expr(left ast.Expr) ast.AssignExpr {
|
||||||
|
op := p.tok.kind
|
||||||
|
p.next()
|
||||||
|
pos := p.tok.position()
|
||||||
|
val := p.expr(0)
|
||||||
|
match left {
|
||||||
|
ast.IndexExpr {
|
||||||
|
// it.mark_as_setter()
|
||||||
|
it.is_setter = true
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
node := ast.AssignExpr{
|
||||||
|
left: left
|
||||||
|
val: val
|
||||||
|
op: op
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (var p Parser) parse_assign_lhs() []ast.Ident {
|
||||||
|
var idents := []ast.Ident
|
||||||
|
for {
|
||||||
|
is_mut := p.tok.kind == .key_mut || p.tok.kind == .key_var
|
||||||
|
if is_mut {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
is_static := p.tok.kind == .key_static
|
||||||
|
if is_static {
|
||||||
|
p.check(.key_static)
|
||||||
|
}
|
||||||
|
var ident := p.parse_ident(false, false)
|
||||||
|
ident.is_mut = is_mut
|
||||||
|
ident.info = ast.IdentVar{
|
||||||
|
is_mut: is_mut
|
||||||
|
is_static: is_static
|
||||||
|
}
|
||||||
|
idents << ident
|
||||||
|
if p.tok.kind == .comma {
|
||||||
|
p.check(.comma)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idents
|
||||||
|
}
|
||||||
|
|
||||||
|
// right hand side of `=` or `:=` in `a,b,c := 1,2,3`
|
||||||
|
fn (var p Parser) parse_assign_rhs() []ast.Expr {
|
||||||
|
var exprs := []ast.Expr
|
||||||
|
for {
|
||||||
|
expr := p.expr(0)
|
||||||
|
exprs << expr
|
||||||
|
if p.tok.kind == .comma {
|
||||||
|
p.check(.comma)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exprs
|
||||||
|
}
|
|
@ -2,3 +2,114 @@
|
||||||
// Use of this source code is governed by an MIT license
|
// Use of this source code is governed by an MIT license
|
||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module parser
|
module parser
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
import v.table
|
||||||
|
import v.token
|
||||||
|
|
||||||
|
fn (var p Parser) array_init() ast.ArrayInit {
|
||||||
|
first_pos := p.tok.position()
|
||||||
|
var last_pos := token.Position{}
|
||||||
|
p.check(.lsbr)
|
||||||
|
// p.warn('array_init() exp=$p.expected_type')
|
||||||
|
var array_type := table.void_type
|
||||||
|
var elem_type := table.void_type
|
||||||
|
var exprs := []ast.Expr
|
||||||
|
var is_fixed := false
|
||||||
|
if p.tok.kind == .rsbr {
|
||||||
|
// []typ => `[]` and `typ` must be on the same line
|
||||||
|
line_nr := p.tok.line_nr
|
||||||
|
p.check(.rsbr)
|
||||||
|
// []string
|
||||||
|
if p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
|
||||||
|
elem_type = p.parse_type()
|
||||||
|
// this is set here becasue its a known type, others could be the
|
||||||
|
// result of expr so we do those in checker
|
||||||
|
idx := p.table.find_or_register_array(elem_type, 1)
|
||||||
|
array_type = table.new_type(idx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// [1,2,3] or [const]byte
|
||||||
|
for i := 0; p.tok.kind != .rsbr; i++ {
|
||||||
|
expr := p.expr(0)
|
||||||
|
exprs << expr
|
||||||
|
if p.tok.kind == .comma {
|
||||||
|
p.check(.comma)
|
||||||
|
}
|
||||||
|
// p.check_comment()
|
||||||
|
}
|
||||||
|
line_nr := p.tok.line_nr
|
||||||
|
$if tinyc {
|
||||||
|
// NB: do not remove the next line without testing
|
||||||
|
// v selfcompilation with tcc first
|
||||||
|
tcc_stack_bug := 12345
|
||||||
|
}
|
||||||
|
last_pos = p.tok.position()
|
||||||
|
p.check(.rsbr)
|
||||||
|
// [100]byte
|
||||||
|
if exprs.len == 1 && p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
|
||||||
|
elem_type = p.parse_type()
|
||||||
|
is_fixed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// !
|
||||||
|
if p.tok.kind == .not {
|
||||||
|
last_pos = p.tok.position()
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
if p.tok.kind == .not {
|
||||||
|
last_pos = p.tok.position()
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
if p.tok.kind == .lcbr && exprs.len == 0 {
|
||||||
|
// `[]int{ len: 10, cap: 100}` syntax
|
||||||
|
p.next()
|
||||||
|
for p.tok.kind != .rcbr {
|
||||||
|
key := p.check_name()
|
||||||
|
p.check(.colon)
|
||||||
|
if !(key in ['len', 'cap', 'init']) {
|
||||||
|
p.error('wrong field `$key`, expecting `len`, `cap`, or `init`')
|
||||||
|
}
|
||||||
|
p.expr(0)
|
||||||
|
if p.tok.kind != .rcbr {
|
||||||
|
p.check(.comma)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.check(.rcbr)
|
||||||
|
}
|
||||||
|
pos := token.Position{
|
||||||
|
line_nr: first_pos.line_nr
|
||||||
|
pos: first_pos.pos
|
||||||
|
len: last_pos.pos - first_pos.pos + last_pos.len
|
||||||
|
}
|
||||||
|
return ast.ArrayInit{
|
||||||
|
is_fixed: is_fixed
|
||||||
|
mod: p.mod
|
||||||
|
elem_type: elem_type
|
||||||
|
typ: array_type
|
||||||
|
exprs: exprs
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (var p Parser) map_init() ast.MapInit {
|
||||||
|
pos := p.tok.position()
|
||||||
|
var keys := []ast.Expr
|
||||||
|
var vals := []ast.Expr
|
||||||
|
for p.tok.kind != .rcbr && p.tok.kind != .eof {
|
||||||
|
// p.check(.str)
|
||||||
|
key := p.expr(0)
|
||||||
|
keys << key
|
||||||
|
p.check(.colon)
|
||||||
|
val := p.expr(0)
|
||||||
|
vals << val
|
||||||
|
if p.tok.kind == .comma {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.MapInit{
|
||||||
|
keys: keys
|
||||||
|
vals: vals
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
module parser
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
import v.table
|
||||||
|
import v.token
|
||||||
|
|
||||||
|
fn (var p Parser) for_stmt() ast.Stmt {
|
||||||
|
p.check(.key_for)
|
||||||
|
pos := p.tok.position()
|
||||||
|
p.open_scope()
|
||||||
|
p.inside_for = true
|
||||||
|
// defer { p.close_scope() }
|
||||||
|
// Infinite loop
|
||||||
|
if p.tok.kind == .lcbr {
|
||||||
|
p.inside_for = false
|
||||||
|
stmts := p.parse_block()
|
||||||
|
p.close_scope()
|
||||||
|
return ast.ForStmt{
|
||||||
|
stmts: stmts
|
||||||
|
pos: pos
|
||||||
|
is_inf: true
|
||||||
|
}
|
||||||
|
} else if p.tok.kind in [.key_mut, .key_var] {
|
||||||
|
p.error('`mut` is not needed in for loops')
|
||||||
|
} else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon] || p.tok.kind == .semicolon {
|
||||||
|
// `for i := 0; i < 10; i++ {`
|
||||||
|
var init := ast.Stmt{}
|
||||||
|
var cond := p.new_true_expr()
|
||||||
|
// mut inc := ast.Stmt{}
|
||||||
|
var inc := ast.Expr{}
|
||||||
|
var has_init := false
|
||||||
|
var has_cond := false
|
||||||
|
var has_inc := false
|
||||||
|
if p.peek_tok.kind in [.assign, .decl_assign] {
|
||||||
|
init = p.assign_stmt()
|
||||||
|
has_init = true
|
||||||
|
} else if p.tok.kind != .semicolon {
|
||||||
|
}
|
||||||
|
// allow `for ;; i++ {`
|
||||||
|
// Allow `for i = 0; i < ...`
|
||||||
|
p.check(.semicolon)
|
||||||
|
if p.tok.kind != .semicolon {
|
||||||
|
var typ := table.void_type
|
||||||
|
cond = p.expr(0)
|
||||||
|
has_cond = true
|
||||||
|
}
|
||||||
|
p.check(.semicolon)
|
||||||
|
if p.tok.kind != .lcbr {
|
||||||
|
// inc = p.stmt()
|
||||||
|
inc = p.expr(0)
|
||||||
|
has_inc = true
|
||||||
|
}
|
||||||
|
p.inside_for = false
|
||||||
|
stmts := p.parse_block()
|
||||||
|
p.close_scope()
|
||||||
|
return ast.ForCStmt{
|
||||||
|
stmts: stmts
|
||||||
|
has_init: has_init
|
||||||
|
has_cond: has_cond
|
||||||
|
has_inc: has_inc
|
||||||
|
init: init
|
||||||
|
cond: cond
|
||||||
|
inc: inc
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
} else if p.peek_tok.kind in [.key_in, .comma] {
|
||||||
|
// `for i in vals`, `for i in start .. end`
|
||||||
|
var key_var_name := ''
|
||||||
|
var val_var_name := p.check_name()
|
||||||
|
if p.tok.kind == .comma {
|
||||||
|
p.check(.comma)
|
||||||
|
key_var_name = val_var_name
|
||||||
|
val_var_name = p.check_name()
|
||||||
|
p.scope.register(key_var_name, ast.Var{
|
||||||
|
name: key_var_name
|
||||||
|
typ: table.int_type
|
||||||
|
})
|
||||||
|
}
|
||||||
|
p.check(.key_in)
|
||||||
|
// arr_expr
|
||||||
|
cond := p.expr(0)
|
||||||
|
// 0 .. 10
|
||||||
|
// start := p.tok.lit.int()
|
||||||
|
// TODO use RangeExpr
|
||||||
|
var high_expr := ast.Expr{}
|
||||||
|
var is_range := false
|
||||||
|
if p.tok.kind == .dotdot {
|
||||||
|
is_range = true
|
||||||
|
p.check(.dotdot)
|
||||||
|
high_expr = p.expr(0)
|
||||||
|
p.scope.register(val_var_name, ast.Var{
|
||||||
|
name: val_var_name
|
||||||
|
typ: table.int_type
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// this type will be set in checker
|
||||||
|
p.scope.register(val_var_name, ast.Var{
|
||||||
|
name: val_var_name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
p.inside_for = false
|
||||||
|
stmts := p.parse_block()
|
||||||
|
// println('nr stmts=$stmts.len')
|
||||||
|
p.close_scope()
|
||||||
|
return ast.ForInStmt{
|
||||||
|
stmts: stmts
|
||||||
|
cond: cond
|
||||||
|
key_var: key_var_name
|
||||||
|
val_var: val_var_name
|
||||||
|
high: high_expr
|
||||||
|
is_range: is_range
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `for cond {`
|
||||||
|
cond := p.expr(0)
|
||||||
|
p.inside_for = false
|
||||||
|
stmts := p.parse_block()
|
||||||
|
p.close_scope()
|
||||||
|
return ast.ForStmt{
|
||||||
|
cond: cond
|
||||||
|
stmts: stmts
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
module parser
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
import v.table
|
||||||
|
import v.token
|
||||||
|
|
||||||
|
fn (var p Parser) if_expr() ast.IfExpr {
|
||||||
|
pos := p.tok.position()
|
||||||
|
var branches := []ast.IfBranch
|
||||||
|
var has_else := false
|
||||||
|
for p.tok.kind in [.key_if, .key_else] {
|
||||||
|
p.inside_if = true
|
||||||
|
branch_pos := p.tok.position()
|
||||||
|
var comment := ast.Comment{}
|
||||||
|
if p.tok.kind == .key_if {
|
||||||
|
p.check(.key_if)
|
||||||
|
} else {
|
||||||
|
// if p.tok.kind == .comment {
|
||||||
|
// p.error('place comments inside {}')
|
||||||
|
// }
|
||||||
|
// comment = p.check_comment()
|
||||||
|
p.check(.key_else)
|
||||||
|
if p.tok.kind == .key_if {
|
||||||
|
p.check(.key_if)
|
||||||
|
} else {
|
||||||
|
has_else = true
|
||||||
|
p.inside_if = false
|
||||||
|
branches << ast.IfBranch{
|
||||||
|
stmts: p.parse_block()
|
||||||
|
pos: branch_pos
|
||||||
|
comment: comment
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var cond := ast.Expr{}
|
||||||
|
var is_or := false
|
||||||
|
// `if x := opt() {`
|
||||||
|
if p.peek_tok.kind == .decl_assign {
|
||||||
|
is_or = true
|
||||||
|
p.open_scope()
|
||||||
|
var_name := p.check_name()
|
||||||
|
p.check(.decl_assign)
|
||||||
|
expr := p.expr(0)
|
||||||
|
p.scope.register(var_name, ast.Var{
|
||||||
|
name: var_name
|
||||||
|
expr: expr
|
||||||
|
})
|
||||||
|
cond = ast.IfGuardExpr{
|
||||||
|
var_name: var_name
|
||||||
|
expr: expr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cond = p.expr(0)
|
||||||
|
}
|
||||||
|
p.inside_if = false
|
||||||
|
stmts := p.parse_block()
|
||||||
|
if is_or {
|
||||||
|
p.close_scope()
|
||||||
|
}
|
||||||
|
branches << ast.IfBranch{
|
||||||
|
cond: cond
|
||||||
|
stmts: stmts
|
||||||
|
pos: branch_pos
|
||||||
|
comment: ast.Comment{}
|
||||||
|
}
|
||||||
|
if p.tok.kind != .key_else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.IfExpr{
|
||||||
|
branches: branches
|
||||||
|
pos: pos
|
||||||
|
has_else: has_else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (var p Parser) match_expr() ast.MatchExpr {
|
||||||
|
match_first_pos := p.tok.position()
|
||||||
|
p.inside_match = true
|
||||||
|
p.check(.key_match)
|
||||||
|
is_mut := p.tok.kind in [.key_mut, .key_var]
|
||||||
|
var is_sum_type := false
|
||||||
|
if is_mut {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
cond := p.expr(0)
|
||||||
|
p.inside_match = false
|
||||||
|
p.check(.lcbr)
|
||||||
|
var branches := []ast.MatchBranch
|
||||||
|
for {
|
||||||
|
branch_first_pos := p.tok.position()
|
||||||
|
comment := p.check_comment() // comment before {}
|
||||||
|
var exprs := []ast.Expr
|
||||||
|
p.open_scope()
|
||||||
|
// final else
|
||||||
|
var is_else := false
|
||||||
|
if p.tok.kind == .key_else {
|
||||||
|
is_else = true
|
||||||
|
p.next()
|
||||||
|
} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names || (p.tok.lit[0].is_capital() &&
|
||||||
|
!p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) {
|
||||||
|
// Sum type match
|
||||||
|
// if sym.kind == .sum_type {
|
||||||
|
// p.warn('is sum')
|
||||||
|
// TODO `exprs << ast.Type{...}
|
||||||
|
typ := p.parse_type()
|
||||||
|
x := ast.Type{
|
||||||
|
typ: typ
|
||||||
|
}
|
||||||
|
var expr := ast.Expr{}
|
||||||
|
expr = x
|
||||||
|
exprs << expr
|
||||||
|
p.scope.register('it', ast.Var{
|
||||||
|
name: 'it'
|
||||||
|
typ: table.type_to_ptr(typ)
|
||||||
|
})
|
||||||
|
// TODO
|
||||||
|
if p.tok.kind == .comma {
|
||||||
|
p.next()
|
||||||
|
p.parse_type()
|
||||||
|
}
|
||||||
|
is_sum_type = true
|
||||||
|
} else {
|
||||||
|
// Expression match
|
||||||
|
for {
|
||||||
|
p.inside_match_case = true
|
||||||
|
expr := p.expr(0)
|
||||||
|
p.inside_match_case = false
|
||||||
|
exprs << expr
|
||||||
|
if p.tok.kind != .comma {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.check(.comma)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
branch_last_pos := p.tok.position()
|
||||||
|
// p.warn('match block')
|
||||||
|
stmts := p.parse_block()
|
||||||
|
pos := token.Position{
|
||||||
|
line_nr: branch_first_pos.line_nr
|
||||||
|
pos: branch_first_pos.pos
|
||||||
|
len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len
|
||||||
|
}
|
||||||
|
branches << ast.MatchBranch{
|
||||||
|
exprs: exprs
|
||||||
|
stmts: stmts
|
||||||
|
pos: pos
|
||||||
|
comment: comment
|
||||||
|
is_else: is_else
|
||||||
|
}
|
||||||
|
p.close_scope()
|
||||||
|
if p.tok.kind == .rcbr {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match_last_pos := p.tok.position()
|
||||||
|
pos := token.Position{
|
||||||
|
line_nr: match_first_pos.line_nr
|
||||||
|
pos: match_first_pos.pos
|
||||||
|
len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len
|
||||||
|
}
|
||||||
|
p.check(.rcbr)
|
||||||
|
return ast.MatchExpr{
|
||||||
|
branches: branches
|
||||||
|
cond: cond
|
||||||
|
is_sum_type: is_sum_type
|
||||||
|
pos: pos
|
||||||
|
is_mut: is_mut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -455,28 +455,6 @@ pub fn (var p Parser) stmt() ast.Stmt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is it possible to merge with AssignStmt?
|
|
||||||
pub fn (var p Parser) assign_expr(left ast.Expr) ast.AssignExpr {
|
|
||||||
op := p.tok.kind
|
|
||||||
p.next()
|
|
||||||
pos := p.tok.position()
|
|
||||||
val := p.expr(0)
|
|
||||||
match left {
|
|
||||||
ast.IndexExpr {
|
|
||||||
// it.mark_as_setter()
|
|
||||||
it.is_setter = true
|
|
||||||
}
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
node := ast.AssignExpr{
|
|
||||||
left: left
|
|
||||||
val: val
|
|
||||||
op: op
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) attribute() ast.Attr {
|
fn (var p Parser) attribute() ast.Attr {
|
||||||
p.check(.lsbr)
|
p.check(.lsbr)
|
||||||
if p.tok.kind == .key_if {
|
if p.tok.kind == .key_if {
|
||||||
|
@ -826,197 +804,6 @@ fn (var p Parser) enum_val() ast.EnumVal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (var p Parser) for_stmt() ast.Stmt {
|
|
||||||
p.check(.key_for)
|
|
||||||
pos := p.tok.position()
|
|
||||||
p.open_scope()
|
|
||||||
p.inside_for = true
|
|
||||||
// defer { p.close_scope() }
|
|
||||||
// Infinite loop
|
|
||||||
if p.tok.kind == .lcbr {
|
|
||||||
p.inside_for = false
|
|
||||||
stmts := p.parse_block()
|
|
||||||
p.close_scope()
|
|
||||||
return ast.ForStmt{
|
|
||||||
stmts: stmts
|
|
||||||
pos: pos
|
|
||||||
is_inf: true
|
|
||||||
}
|
|
||||||
} else if p.tok.kind in [.key_mut, .key_var] {
|
|
||||||
p.error('`mut` is not needed in for loops')
|
|
||||||
} else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon] || p.tok.kind == .semicolon {
|
|
||||||
// `for i := 0; i < 10; i++ {`
|
|
||||||
var init := ast.Stmt{}
|
|
||||||
var cond := p.new_true_expr()
|
|
||||||
// mut inc := ast.Stmt{}
|
|
||||||
var inc := ast.Expr{}
|
|
||||||
var has_init := false
|
|
||||||
var has_cond := false
|
|
||||||
var has_inc := false
|
|
||||||
if p.peek_tok.kind in [.assign, .decl_assign] {
|
|
||||||
init = p.assign_stmt()
|
|
||||||
has_init = true
|
|
||||||
} else if p.tok.kind != .semicolon {
|
|
||||||
}
|
|
||||||
// allow `for ;; i++ {`
|
|
||||||
// Allow `for i = 0; i < ...`
|
|
||||||
p.check(.semicolon)
|
|
||||||
if p.tok.kind != .semicolon {
|
|
||||||
var typ := table.void_type
|
|
||||||
cond = p.expr(0)
|
|
||||||
has_cond = true
|
|
||||||
}
|
|
||||||
p.check(.semicolon)
|
|
||||||
if p.tok.kind != .lcbr {
|
|
||||||
// inc = p.stmt()
|
|
||||||
inc = p.expr(0)
|
|
||||||
has_inc = true
|
|
||||||
}
|
|
||||||
p.inside_for = false
|
|
||||||
stmts := p.parse_block()
|
|
||||||
p.close_scope()
|
|
||||||
return ast.ForCStmt{
|
|
||||||
stmts: stmts
|
|
||||||
has_init: has_init
|
|
||||||
has_cond: has_cond
|
|
||||||
has_inc: has_inc
|
|
||||||
init: init
|
|
||||||
cond: cond
|
|
||||||
inc: inc
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
} else if p.peek_tok.kind in [.key_in, .comma] {
|
|
||||||
// `for i in vals`, `for i in start .. end`
|
|
||||||
var key_var_name := ''
|
|
||||||
var val_var_name := p.check_name()
|
|
||||||
if p.tok.kind == .comma {
|
|
||||||
p.check(.comma)
|
|
||||||
key_var_name = val_var_name
|
|
||||||
val_var_name = p.check_name()
|
|
||||||
p.scope.register(key_var_name, ast.Var{
|
|
||||||
name: key_var_name
|
|
||||||
typ: table.int_type
|
|
||||||
})
|
|
||||||
}
|
|
||||||
p.check(.key_in)
|
|
||||||
// arr_expr
|
|
||||||
cond := p.expr(0)
|
|
||||||
// 0 .. 10
|
|
||||||
// start := p.tok.lit.int()
|
|
||||||
// TODO use RangeExpr
|
|
||||||
var high_expr := ast.Expr{}
|
|
||||||
var is_range := false
|
|
||||||
if p.tok.kind == .dotdot {
|
|
||||||
is_range = true
|
|
||||||
p.check(.dotdot)
|
|
||||||
high_expr = p.expr(0)
|
|
||||||
p.scope.register(val_var_name, ast.Var{
|
|
||||||
name: val_var_name
|
|
||||||
typ: table.int_type
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// this type will be set in checker
|
|
||||||
p.scope.register(val_var_name, ast.Var{
|
|
||||||
name: val_var_name
|
|
||||||
})
|
|
||||||
}
|
|
||||||
p.inside_for = false
|
|
||||||
stmts := p.parse_block()
|
|
||||||
// println('nr stmts=$stmts.len')
|
|
||||||
p.close_scope()
|
|
||||||
return ast.ForInStmt{
|
|
||||||
stmts: stmts
|
|
||||||
cond: cond
|
|
||||||
key_var: key_var_name
|
|
||||||
val_var: val_var_name
|
|
||||||
high: high_expr
|
|
||||||
is_range: is_range
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// `for cond {`
|
|
||||||
cond := p.expr(0)
|
|
||||||
p.inside_for = false
|
|
||||||
stmts := p.parse_block()
|
|
||||||
p.close_scope()
|
|
||||||
return ast.ForStmt{
|
|
||||||
cond: cond
|
|
||||||
stmts: stmts
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) if_expr() ast.IfExpr {
|
|
||||||
pos := p.tok.position()
|
|
||||||
var branches := []ast.IfBranch
|
|
||||||
var has_else := false
|
|
||||||
for p.tok.kind in [.key_if, .key_else] {
|
|
||||||
p.inside_if = true
|
|
||||||
branch_pos := p.tok.position()
|
|
||||||
var comment := ast.Comment{}
|
|
||||||
if p.tok.kind == .key_if {
|
|
||||||
p.check(.key_if)
|
|
||||||
} else {
|
|
||||||
// if p.tok.kind == .comment {
|
|
||||||
// p.error('place comments inside {}')
|
|
||||||
// }
|
|
||||||
// comment = p.check_comment()
|
|
||||||
p.check(.key_else)
|
|
||||||
if p.tok.kind == .key_if {
|
|
||||||
p.check(.key_if)
|
|
||||||
} else {
|
|
||||||
has_else = true
|
|
||||||
p.inside_if = false
|
|
||||||
branches << ast.IfBranch{
|
|
||||||
stmts: p.parse_block()
|
|
||||||
pos: branch_pos
|
|
||||||
comment: comment
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var cond := ast.Expr{}
|
|
||||||
var is_or := false
|
|
||||||
// `if x := opt() {`
|
|
||||||
if p.peek_tok.kind == .decl_assign {
|
|
||||||
is_or = true
|
|
||||||
p.open_scope()
|
|
||||||
var_name := p.check_name()
|
|
||||||
p.check(.decl_assign)
|
|
||||||
expr := p.expr(0)
|
|
||||||
p.scope.register(var_name, ast.Var{
|
|
||||||
name: var_name
|
|
||||||
expr: expr
|
|
||||||
})
|
|
||||||
cond = ast.IfGuardExpr{
|
|
||||||
var_name: var_name
|
|
||||||
expr: expr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cond = p.expr(0)
|
|
||||||
}
|
|
||||||
p.inside_if = false
|
|
||||||
stmts := p.parse_block()
|
|
||||||
if is_or {
|
|
||||||
p.close_scope()
|
|
||||||
}
|
|
||||||
branches << ast.IfBranch{
|
|
||||||
cond: cond
|
|
||||||
stmts: stmts
|
|
||||||
pos: branch_pos
|
|
||||||
comment: ast.Comment{}
|
|
||||||
}
|
|
||||||
if p.tok.kind != .key_else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ast.IfExpr{
|
|
||||||
branches: branches
|
|
||||||
pos: pos
|
|
||||||
has_else: has_else
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) string_expr() ast.Expr {
|
fn (var p Parser) string_expr() ast.Expr {
|
||||||
is_raw := p.tok.kind == .name && p.tok.lit == 'r'
|
is_raw := p.tok.kind == .name && p.tok.lit == 'r'
|
||||||
is_cstr := p.tok.kind == .name && p.tok.lit == 'c'
|
is_cstr := p.tok.kind == .name && p.tok.lit == 'c'
|
||||||
|
@ -1078,113 +865,6 @@ fn (var p Parser) string_expr() ast.Expr {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (var p Parser) array_init() ast.ArrayInit {
|
|
||||||
first_pos := p.tok.position()
|
|
||||||
var last_pos := token.Position{}
|
|
||||||
p.check(.lsbr)
|
|
||||||
// p.warn('array_init() exp=$p.expected_type')
|
|
||||||
var array_type := table.void_type
|
|
||||||
var elem_type := table.void_type
|
|
||||||
var exprs := []ast.Expr
|
|
||||||
var is_fixed := false
|
|
||||||
if p.tok.kind == .rsbr {
|
|
||||||
// []typ => `[]` and `typ` must be on the same line
|
|
||||||
line_nr := p.tok.line_nr
|
|
||||||
p.check(.rsbr)
|
|
||||||
// []string
|
|
||||||
if p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
|
|
||||||
elem_type = p.parse_type()
|
|
||||||
// this is set here becasue its a known type, others could be the
|
|
||||||
// result of expr so we do those in checker
|
|
||||||
idx := p.table.find_or_register_array(elem_type, 1)
|
|
||||||
array_type = table.new_type(idx)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// [1,2,3] or [const]byte
|
|
||||||
for i := 0; p.tok.kind != .rsbr; i++ {
|
|
||||||
expr := p.expr(0)
|
|
||||||
exprs << expr
|
|
||||||
if p.tok.kind == .comma {
|
|
||||||
p.check(.comma)
|
|
||||||
}
|
|
||||||
// p.check_comment()
|
|
||||||
}
|
|
||||||
line_nr := p.tok.line_nr
|
|
||||||
$if tinyc {
|
|
||||||
// NB: do not remove the next line without testing
|
|
||||||
// v selfcompilation with tcc first
|
|
||||||
tcc_stack_bug := 12345
|
|
||||||
}
|
|
||||||
last_pos = p.tok.position()
|
|
||||||
p.check(.rsbr)
|
|
||||||
// [100]byte
|
|
||||||
if exprs.len == 1 && p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
|
|
||||||
elem_type = p.parse_type()
|
|
||||||
is_fixed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// !
|
|
||||||
if p.tok.kind == .not {
|
|
||||||
last_pos = p.tok.position()
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
if p.tok.kind == .not {
|
|
||||||
last_pos = p.tok.position()
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
if p.tok.kind == .lcbr && exprs.len == 0 {
|
|
||||||
// `[]int{ len: 10, cap: 100}` syntax
|
|
||||||
p.next()
|
|
||||||
for p.tok.kind != .rcbr {
|
|
||||||
key := p.check_name()
|
|
||||||
p.check(.colon)
|
|
||||||
if !(key in ['len', 'cap', 'init']) {
|
|
||||||
p.error('wrong field `$key`, expecting `len`, `cap`, or `init`')
|
|
||||||
}
|
|
||||||
p.expr(0)
|
|
||||||
if p.tok.kind != .rcbr {
|
|
||||||
p.check(.comma)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.check(.rcbr)
|
|
||||||
}
|
|
||||||
pos := token.Position{
|
|
||||||
line_nr: first_pos.line_nr
|
|
||||||
pos: first_pos.pos
|
|
||||||
len: last_pos.pos - first_pos.pos + last_pos.len
|
|
||||||
}
|
|
||||||
return ast.ArrayInit{
|
|
||||||
is_fixed: is_fixed
|
|
||||||
mod: p.mod
|
|
||||||
elem_type: elem_type
|
|
||||||
typ: array_type
|
|
||||||
exprs: exprs
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) map_init() ast.MapInit {
|
|
||||||
pos := p.tok.position()
|
|
||||||
var keys := []ast.Expr
|
|
||||||
var vals := []ast.Expr
|
|
||||||
for p.tok.kind != .rcbr && p.tok.kind != .eof {
|
|
||||||
// p.check(.str)
|
|
||||||
key := p.expr(0)
|
|
||||||
keys << key
|
|
||||||
p.check(.colon)
|
|
||||||
val := p.expr(0)
|
|
||||||
vals << val
|
|
||||||
if p.tok.kind == .comma {
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ast.MapInit{
|
|
||||||
keys: keys
|
|
||||||
vals: vals
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) parse_number_literal() ast.Expr {
|
fn (var p Parser) parse_number_literal() ast.Expr {
|
||||||
lit := p.tok.lit
|
lit := p.tok.lit
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
|
@ -1329,91 +1009,6 @@ fn (var p Parser) return_stmt() ast.Return {
|
||||||
}
|
}
|
||||||
|
|
||||||
// left hand side of `=` or `:=` in `a,b,c := 1,2,3`
|
// left hand side of `=` or `:=` in `a,b,c := 1,2,3`
|
||||||
fn (var p Parser) parse_assign_lhs() []ast.Ident {
|
|
||||||
var idents := []ast.Ident
|
|
||||||
for {
|
|
||||||
is_mut := p.tok.kind == .key_mut || p.tok.kind == .key_var
|
|
||||||
if is_mut {
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
is_static := p.tok.kind == .key_static
|
|
||||||
if is_static {
|
|
||||||
p.check(.key_static)
|
|
||||||
}
|
|
||||||
var ident := p.parse_ident(false, false)
|
|
||||||
ident.is_mut = is_mut
|
|
||||||
ident.info = ast.IdentVar{
|
|
||||||
is_mut: is_mut
|
|
||||||
is_static: is_static
|
|
||||||
}
|
|
||||||
idents << ident
|
|
||||||
if p.tok.kind == .comma {
|
|
||||||
p.check(.comma)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return idents
|
|
||||||
}
|
|
||||||
|
|
||||||
// right hand side of `=` or `:=` in `a,b,c := 1,2,3`
|
|
||||||
fn (var p Parser) parse_assign_rhs() []ast.Expr {
|
|
||||||
var exprs := []ast.Expr
|
|
||||||
for {
|
|
||||||
expr := p.expr(0)
|
|
||||||
exprs << expr
|
|
||||||
if p.tok.kind == .comma {
|
|
||||||
p.check(.comma)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exprs
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) assign_stmt() ast.Stmt {
|
|
||||||
is_static := p.tok.kind == .key_static
|
|
||||||
if is_static {
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
idents := p.parse_assign_lhs()
|
|
||||||
op := p.tok.kind
|
|
||||||
p.next() // :=, =
|
|
||||||
pos := p.tok.position()
|
|
||||||
exprs := p.parse_assign_rhs()
|
|
||||||
is_decl := op == .decl_assign
|
|
||||||
for i, ident in idents {
|
|
||||||
known_var := p.scope.known_var(ident.name)
|
|
||||||
if !is_decl && !known_var {
|
|
||||||
p.error('unknown variable `$ident.name`')
|
|
||||||
}
|
|
||||||
if is_decl && ident.kind != .blank_ident {
|
|
||||||
if p.scope.known_var(ident.name) {
|
|
||||||
p.error('redefinition of `$ident.name`')
|
|
||||||
}
|
|
||||||
if idents.len == exprs.len {
|
|
||||||
p.scope.register(ident.name, ast.Var{
|
|
||||||
name: ident.name
|
|
||||||
expr: exprs[i]
|
|
||||||
is_mut: ident.is_mut || p.inside_for
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
p.scope.register(ident.name, ast.Var{
|
|
||||||
name: ident.name
|
|
||||||
is_mut: ident.is_mut || p.inside_for
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ast.AssignStmt{
|
|
||||||
left: idents
|
|
||||||
right: exprs
|
|
||||||
op: op
|
|
||||||
pos: pos
|
|
||||||
is_static: is_static
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) global_decl() ast.GlobalDecl {
|
fn (var p Parser) global_decl() ast.GlobalDecl {
|
||||||
if !p.pref.translated && !p.pref.is_live && !p.builtin_mod && !p.pref.building_v && p.mod !=
|
if !p.pref.translated && !p.pref.is_live && !p.builtin_mod && !p.pref.building_v && p.mod !=
|
||||||
'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals {
|
'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals {
|
||||||
|
@ -1454,101 +1049,6 @@ fn (var p Parser) global_decl() ast.GlobalDecl {
|
||||||
return glob
|
return glob
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (var p Parser) match_expr() ast.MatchExpr {
|
|
||||||
match_first_pos := p.tok.position()
|
|
||||||
p.inside_match = true
|
|
||||||
p.check(.key_match)
|
|
||||||
is_mut := p.tok.kind in [.key_mut, .key_var]
|
|
||||||
var is_sum_type := false
|
|
||||||
if is_mut {
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
cond := p.expr(0)
|
|
||||||
p.inside_match = false
|
|
||||||
p.check(.lcbr)
|
|
||||||
var branches := []ast.MatchBranch
|
|
||||||
for {
|
|
||||||
branch_first_pos := p.tok.position()
|
|
||||||
comment := p.check_comment() // comment before {}
|
|
||||||
var exprs := []ast.Expr
|
|
||||||
p.open_scope()
|
|
||||||
// final else
|
|
||||||
var is_else := false
|
|
||||||
if p.tok.kind == .key_else {
|
|
||||||
is_else = true
|
|
||||||
p.next()
|
|
||||||
} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names || (p.tok.lit[0].is_capital() &&
|
|
||||||
!p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) {
|
|
||||||
// Sum type match
|
|
||||||
// if sym.kind == .sum_type {
|
|
||||||
// p.warn('is sum')
|
|
||||||
// TODO `exprs << ast.Type{...}
|
|
||||||
typ := p.parse_type()
|
|
||||||
x := ast.Type{
|
|
||||||
typ: typ
|
|
||||||
}
|
|
||||||
var expr := ast.Expr{}
|
|
||||||
expr = x
|
|
||||||
exprs << expr
|
|
||||||
p.scope.register('it', ast.Var{
|
|
||||||
name: 'it'
|
|
||||||
typ: table.type_to_ptr(typ)
|
|
||||||
})
|
|
||||||
// TODO
|
|
||||||
if p.tok.kind == .comma {
|
|
||||||
p.next()
|
|
||||||
p.parse_type()
|
|
||||||
}
|
|
||||||
is_sum_type = true
|
|
||||||
} else {
|
|
||||||
// Expression match
|
|
||||||
for {
|
|
||||||
p.inside_match_case = true
|
|
||||||
expr := p.expr(0)
|
|
||||||
p.inside_match_case = false
|
|
||||||
exprs << expr
|
|
||||||
if p.tok.kind != .comma {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.check(.comma)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
branch_last_pos := p.tok.position()
|
|
||||||
// p.warn('match block')
|
|
||||||
stmts := p.parse_block()
|
|
||||||
pos := token.Position{
|
|
||||||
line_nr: branch_first_pos.line_nr
|
|
||||||
pos: branch_first_pos.pos
|
|
||||||
len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len
|
|
||||||
}
|
|
||||||
branches << ast.MatchBranch{
|
|
||||||
exprs: exprs
|
|
||||||
stmts: stmts
|
|
||||||
pos: pos
|
|
||||||
comment: comment
|
|
||||||
is_else: is_else
|
|
||||||
}
|
|
||||||
p.close_scope()
|
|
||||||
if p.tok.kind == .rcbr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match_last_pos := p.tok.position()
|
|
||||||
pos := token.Position{
|
|
||||||
line_nr: match_first_pos.line_nr
|
|
||||||
pos: match_first_pos.pos
|
|
||||||
len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len
|
|
||||||
}
|
|
||||||
p.check(.rcbr)
|
|
||||||
return ast.MatchExpr{
|
|
||||||
branches: branches
|
|
||||||
cond: cond
|
|
||||||
is_sum_type: is_sum_type
|
|
||||||
pos: pos
|
|
||||||
is_mut: is_mut
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (var p Parser) enum_decl() ast.EnumDecl {
|
fn (var p Parser) enum_decl() ast.EnumDecl {
|
||||||
is_pub := p.tok.kind == .key_pub
|
is_pub := p.tok.kind == .key_pub
|
||||||
if is_pub {
|
if is_pub {
|
||||||
|
|
Loading…
Reference in New Issue