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