From dc90f4f4a68315ebcb87a191c210da58056e1289 Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Sat, 15 Feb 2020 23:37:48 +1100 Subject: [PATCH] v2: remove unresolved types; handle types in checker; add ast.scope --- vlib/v/ast/ast.v | 8 +- vlib/v/ast/scope.v | 143 ++++++++++++++++ vlib/v/builder/builder.v | 7 + vlib/v/checker/checker.v | 324 ++++++++++++++++++++++-------------- vlib/v/gen/tests/1.c | 6 +- vlib/v/gen/x64/gen.v | 1 + vlib/v/parser/fn.v | 27 +-- vlib/v/parser/parse_type.v | 10 +- vlib/v/parser/parser.v | 203 ++++++++++++++-------- vlib/v/parser/parser_test.v | 23 ++- vlib/v/scanner/scanner.v | 1 + vlib/v/table/table.v | 3 +- vlib/v/token/position.v | 5 +- vlib/v/token/token.v | 2 +- 14 files changed, 536 insertions(+), 227 deletions(-) create mode 100644 vlib/v/ast/scope.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 3c0799a44f..3171c426c3 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -70,6 +70,7 @@ pub struct Field { pub: name string // type_idx int +mut: typ table.Type } @@ -197,6 +198,7 @@ pub: mod Module imports []Import stmts []Stmt + scope Scope unresolved []Expr } @@ -292,9 +294,10 @@ pub: cond Expr stmts []Stmt else_stmts []Stmt - typ table.Type left Expr // `a` in `a := if ...` pos token.Position +mut: + typ table.Type } pub struct MatchExpr { @@ -303,8 +306,9 @@ pub: cond Expr blocks []StmtBlock match_exprs []Expr - typ table.Type pos token.Position +mut: + typ table.Type } pub struct CompIf { diff --git a/vlib/v/ast/scope.v b/vlib/v/ast/scope.v new file mode 100644 index 0000000000..a168652994 --- /dev/null +++ b/vlib/v/ast/scope.v @@ -0,0 +1,143 @@ +// 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 ast + +pub struct Scope { +mut: + parent &Scope + children []&Scope + start_pos int + end_pos int + //vars map[string]table.Var + vars map[string]VarDecl +} + +pub fn new_scope(parent &Scope, start_pos int) &Scope { + return &Scope{ + parent: parent + start_pos: start_pos + } +} + +[inline] +pub fn (s &Scope) find_scope_and_var(name string) ?(&Scope,VarDecl) { + if name in s.vars { + return s,s.vars[name] + } + for sc := s; !isnil(sc.parent); sc = sc.parent { + if name in sc.vars { + return sc,sc.vars[name] + } + } + return error('not found') +} + +[inline] +pub fn (s &Scope) find_var(name string) ?VarDecl { +//pub fn (s &Scope) find_var(name string) ?table.Var { + if name in s.vars { + return s.vars[name] + } + for sc := s; !isnil(sc.parent); sc = sc.parent { + if name in sc.vars { + return sc.vars[name] + } + } + return error('not found') +} + +/* +[inline] +pub fn (s &Scope) find_var2(name string, pos int) ?VarDecl { + return find_var_in_scope(name, pos, s) +} + +[inline] +fn find_var_in_scope(name string, pos int, scope &Scope) ?VarDecl { + if pos != 0 && (pos < scope.start_pos || pos > scope.end_pos) { + return none + } + if name in scope.vars { + return scope.vars[name] + } + for child in scope.children { + //if pos < child.start_pos || pos > child.end_pos { + // continue + //} + var := find_var_in_scope(name, pos, child) or { + continue + } + return var + //return find_var_in_scope(name, pos, child) + } + return none +} +*/ + +//pub fn (s mut Scope) register_var(var table.Var) { +[inline] +pub fn (s mut Scope) register_var(var VarDecl) { + if x := s.find_var(var.name) { + println('existing var: $var.name') + return + } + s.vars[var.name] = var +} + +pub fn (s &Scope) innermost(pos int) ?&Scope { + if s.contains(pos) { + /* + for s1 in s.children { + if s1.contains(pos) { + return s1.innermost(pos) + } + } + return s + */ + // binary search + mut first := 0 + mut last := s.children.len-1 + mut middle := last/2 + for first <= last { + //println('FIRST: $first, LAST: $last, LEN: $s.children.len-1') + s1 := s.children[middle] + if s1.end_pos < pos { + first = middle+1 + } + else if s1.contains(pos) { + return s1.innermost(pos) + } + else { + last = middle-1 + } + middle = (first+last)/2 + if first > last { + break + } + } + return s + } + return s + //return error('none') + //return none +} + +[inline] +fn (s &Scope) contains(pos int) bool { + return pos > s.start_pos && pos < s.end_pos +} + +pub fn print_scope_vars(sc &Scope, level int) { + mut indent := '' + for _ in 0..level*4 { + indent += ' ' + } + println('$indent# $sc.start_pos - $sc.end_pos') + for _, var in sc.vars { + println('$indent * $var.name - $var.typ') + } + for child in sc.children { + print_scope_vars(child, level+1) + } +} diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index c931236290..a2f96879c2 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -39,6 +39,10 @@ pub fn (b mut Builder) gen_c(v_files []string) string { b.parsed_files = parser.parse_files(v_files, b.table) b.parse_imports() b.checker.check_files(b.parsed_files) + //println('=======================') + //ast.print_scope_vars(&b.parsed_files[0].scope, 0) + //println('=======================') + //return gen.cgen(b.parsed_files, b.table) return gen.cgen(b.parsed_files, b.table) } @@ -54,6 +58,9 @@ pub fn (b mut Builder) build_x64(v_files []string, out_file string) { parse_time := t1 - t0 println('PARSE: ${parse_time}ms') b.checker.check_files(b.parsed_files) + //println('=======================') + //ast.print_scope_vars(&b.parsed_files[0].scope, 0) + //println('=======================') t2 := time.ticks() check_time := t2 - t1 println('CHECK: ${check_time}ms') diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 35b5c333f9..0269aad024 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -14,6 +14,7 @@ pub struct Checker { table &table.Table mut: file_name string + scope ast.Scope resolved []table.Type } @@ -25,109 +26,49 @@ pub fn new_checker(table &table.Table) Checker { pub fn (c mut Checker) check(ast_file ast.File) { c.file_name = ast_file.path - // if ast_file.unresolved.len != c.resolved.len { - // c.resolve_exprs(file) - // } - c.complete_types() + c.scope = ast_file.scope + for stmt in ast_file.stmts { c.stmt(stmt) } } pub fn (c mut Checker) check_files(ast_files []ast.File) { - // this cant be moved to check() for multiple - // files this muse be done first. TODO: optimize - for file in ast_files { - c.file_name = file.path - c.resolve_expr_types(file.unresolved) - } for file in ast_files { c.check(file) } } -// resolve type of unresolved expressions -fn (c mut Checker) resolve_expr_types(exprs []ast.Expr) { - for x in exprs { - c.resolved << c.expr(x) - } -} - -// update any types chich contain unresolved sub types -fn (c &Checker) complete_types() { - for idx, t in c.table.types { - // skip builtin types - if idx <= table.map_type_idx { - continue - } - // println('Resolve type: $t.name') - if t.kind == .array { - mut info := t.array_info() - if table.type_is_unresolved(info.elem_type) { - info.elem_type = c.resolve(info.elem_type) - elem_type_sym := c.table.get_type_symbol(info.elem_type) - mut t1 := &c.table.types[idx] - t1.name = table.array_name(elem_type_sym, info.nr_dims) - t1.info = info - } - } - else if t.kind == .map { - mut info := t.map_info() - mut updated := false - if table.type_is_unresolved(info.key_type) { - info.key_type = c.resolve(info.key_type) - updated = true - } - if table.type_is_unresolved(info.value_type) { - info.value_type = c.resolve(info.value_type) - updated = true - } - if updated { - mut t1 := &c.table.types[idx] - key_type_sym := c.table.get_type_symbol(info.key_type) - value_type_sym := c.table.get_type_symbol(info.value_type) - t1.name = table.map_name(key_type_sym, value_type_sym) - t1.info = info - } - } - else if t.kind == .multi_return { - mut info := t.mr_info() - mut types := info.types - mut updated := false - for i, ut in types { - if table.type_is_unresolved(ut) { - types[i] = c.resolve(ut) - updated = true - } - } - if updated { - mut t1 := &c.table.types[idx] - info.types = types - t1.info = info - } - } - } -} - -// return the resolved Type from unresovled Type -pub fn (c &Checker) resolve(unresolved table.Type) table.Type { - return c.resolved[-table.type_idx(unresolved) - 1] -} - -pub fn (c &Checker) check_struct_init(struct_init ast.StructInit) table.Type { +pub fn (c mut Checker) check_struct_init(struct_init ast.StructInit) table.Type { // typ := c.table.find_type(struct_init.typ.typ.name) or { // c.error('unknown struct: $struct_init.typ.typ.name', struct_init.pos) // panic('') // } - typ := c.table.get_type_symbol(struct_init.typ) - match typ.kind { + typ_sym := c.table.get_type_symbol(struct_init.typ) + match typ_sym.kind { .placeholder { - c.error('unknown struct: $typ.name', struct_init.pos) + c.error('unknown struct: $typ_sym.name', struct_init.pos) } .struct_ { - info := typ.info as table.Struct + info := typ_sym.info as table.Struct + if struct_init.exprs.len > info.fields.len { + c.error('too many fields', struct_init.pos) + } for i, expr in struct_init.exprs { - field := info.fields[i] + //struct_field info. + field_name := struct_init.fields[i] + mut field := info.fields[i] + mut found_field := false + for f in info.fields { + if f.name == field_name { + field = f + found_field = true + break + } + } + if !found_field { + c.error('struct init: no such field `$field_name` for struct `$typ_sym.name`', struct_init.pos) + } expr_type := c.expr(expr) expr_type_sym := c.table.get_type_symbol(expr_type) field_type_sym := c.table.get_type_symbol(field.typ) @@ -141,7 +82,7 @@ pub fn (c &Checker) check_struct_init(struct_init ast.StructInit) table.Type { return struct_init.typ } -pub fn (c &Checker) infix_expr(infix_expr ast.InfixExpr) table.Type { +pub fn (c mut Checker) infix_expr(infix_expr ast.InfixExpr) table.Type { left_type := c.expr(infix_expr.left) right_type := c.expr(infix_expr.right) if !c.table.check(right_type, left_type) { @@ -157,7 +98,7 @@ pub fn (c &Checker) infix_expr(infix_expr ast.InfixExpr) table.Type { return left_type } -fn (c &Checker) check_assign_expr(assign_expr ast.AssignExpr) { +fn (c mut Checker) check_assign_expr(assign_expr ast.AssignExpr) { left_type := c.expr(assign_expr.left) right_type := c.expr(assign_expr.val) if !c.table.check(right_type, left_type) { @@ -167,7 +108,7 @@ fn (c &Checker) check_assign_expr(assign_expr ast.AssignExpr) { } } -pub fn (c &Checker) call_expr(call_expr ast.CallExpr) table.Type { +pub fn (c mut Checker) call_expr(call_expr ast.CallExpr) table.Type { fn_name := call_expr.name if f := c.table.find_fn(fn_name) { // return_ti := f.return_ti @@ -195,7 +136,7 @@ pub fn (c &Checker) call_expr(call_expr ast.CallExpr) table.Type { exit(1) } -pub fn (c &Checker) check_method_call_expr(method_call_expr ast.MethodCallExpr) table.Type { +pub fn (c mut Checker) check_method_call_expr(method_call_expr ast.MethodCallExpr) table.Type { typ := c.expr(method_call_expr.expr) typ_sym := c.table.get_type_symbol(typ) if method := typ_sym.find_method(method_call_expr.name) { @@ -211,7 +152,7 @@ pub fn (c &Checker) check_method_call_expr(method_call_expr ast.MethodCallExpr) exit(1) } -pub fn (c &Checker) selector_expr(selector_expr ast.SelectorExpr) table.Type { +pub fn (c mut Checker) selector_expr(selector_expr ast.SelectorExpr) table.Type { typ := c.expr(selector_expr.expr) typ_sym := c.table.get_type_symbol(typ) field_name := selector_expr.field @@ -221,9 +162,6 @@ pub fn (c &Checker) selector_expr(selector_expr ast.SelectorExpr) table.Type { // check parent if !isnil(typ_sym.parent) { if field := typ_sym.parent.find_field(field_name) { - if table.type_is_unresolved(field.typ) { - return c.resolved[field.typ] - } return field.typ } } @@ -237,7 +175,7 @@ pub fn (c &Checker) selector_expr(selector_expr ast.SelectorExpr) table.Type { } // TODO: non deferred -pub fn (c &Checker) return_stmt(return_stmt ast.Return) { +pub fn (c mut Checker) return_stmt(return_stmt ast.Return) { mut got_types := []table.Type if return_stmt.exprs.len == 0 { return @@ -268,8 +206,14 @@ pub fn (c &Checker) return_stmt(return_stmt ast.Return) { pub fn (c &Checker) assign_stmt(assign_stmt ast.AssignStmt) {} -pub fn (c &Checker) array_init(array_init ast.ArrayInit) table.Type { +pub fn (c mut Checker) array_init(array_init mut ast.ArrayInit) table.Type { mut elem_type := table.void_type + + // a = [] + if array_init.exprs.len == 0 { + + } + for i, expr in array_init.exprs { c.expr(expr) typ := c.expr(expr) @@ -283,10 +227,21 @@ pub fn (c &Checker) array_init(array_init ast.ArrayInit) table.Type { c.error('expected array element with type `$elem_type_sym.name`', array_init.pos) } } + //idx := if is_fixed { p.table.find_or_register_array_fixed(val_type, fixed_size, 1) } else { p.table.find_or_register_array(val_type, 1) } + is_fixed := false + fixed_size := 1 + idx := if is_fixed { + c.table.find_or_register_array_fixed(elem_type, fixed_size, 1) + } + else { + c.table.find_or_register_array(elem_type, 1) + } + array_type := table.new_type(idx) + array_init.typ = array_type return array_init.typ } -fn (c &Checker) stmt(node ast.Stmt) { +fn (c mut Checker) stmt(node ast.Stmt) { match mut node { ast.FnDecl { for stmt in it.stmts { @@ -299,14 +254,29 @@ fn (c &Checker) stmt(node ast.Stmt) { ast.AssignStmt { c.assign_stmt(it) } - ast.VarDecl { - typ := c.expr(it.expr) - // println('checker: var decl $typ.name it.typ=$it.typ.name $it.pos.line_nr') - // if typ.typ.kind != .void { - if table.type_idx(typ) != table.void_type_idx { - it.typ = typ + ast.ConstDecl { + for i, expr in it.exprs { + mut field := it.fields[i] + typ := c.expr(expr) + mut xconst := c.table.consts[field.name] + //if xconst.typ == 0 { + xconst.typ = typ + c.table.consts[field.name] = xconst + //} + field.typ = typ + it.fields[i] = field } } + ast.VarDecl { + println('VARDECL') + typ := c.expr(it.expr) + typ_sym := c.table.get_type_symbol(typ) + //println('var $it.name - $typ - $it.typ') + //if it.typ == 0 { + // it.typ = typ + //} + it.typ = typ + } ast.ForStmt { typ := c.expr(it.cond) // typ_sym := c.table.get_type_symbol(typ) @@ -334,7 +304,7 @@ fn (c &Checker) stmt(node ast.Stmt) { } } -pub fn (c &Checker) expr(node ast.Expr) table.Type { +pub fn (c mut Checker) expr(node ast.Expr) table.Type { match mut node { ast.AssignExpr { c.check_assign_expr(it) @@ -342,7 +312,9 @@ pub fn (c &Checker) expr(node ast.Expr) table.Type { ast.IntegerLiteral { return table.int_type } - // ast.FloatLiteral {} + ast.FloatLiteral{ + return table.f64_type + } ast.PostfixExpr { return c.postfix_expr(it) } @@ -351,7 +323,6 @@ pub fn (c &Checker) expr(node ast.Expr) table.Type { c.expr(it.left) } */ - ast.StringLiteral { return table.string_type } @@ -371,26 +342,59 @@ pub fn (c &Checker) expr(node ast.Expr) table.Type { return c.check_method_call_expr(it) } ast.ArrayInit { - return c.array_init(it) + return c.array_init(mut it) } ast.Ident { + //println('IDENT: $it.name - $it.pos.pos') if it.kind == .variable { - mut info := it.info as ast.IdentVar - if table.type_is_unresolved(info.typ) { - typ := c.resolve(info.typ) - info.typ = typ - it.info = info - return typ + //println('===========================') + //ast.print_scope_vars(&c.scope, 0) + //println('===========================') + info := it.info as ast.IdentVar + if info.typ != 0 { + return info.typ + } + if inner := c.scope.innermost(it.pos.pos) { + mut found := true + mut varscope, var := inner.find_scope_and_var(it.name) or { + //ast.print_scope_vars(inner, 0) + found = false + c.error('not found: $it.name - POS: $it.pos.pos', it.pos) + panic('') + } + if found { + mut typ := var.typ + // set var type on first use + if var.typ == 0 { + typ = c.expr(var.expr) + mut v1 := var + v1.typ = typ + varscope.vars[var.name] = v1 + } + // update ident + it.kind = .variable + it.info = ast.IdentVar{ + typ: typ + } + return typ + } } - return info.typ } // Handle indents with unresolved types during the parsing step // (declared after first usage) - else if it.kind == .blank_ident { + else if it.kind == .constant { if constant := c.table.find_const(it.name) { return constant.typ } } + else if it.kind == .blank_ident { + println('CONST A: $it.name') + if constant := c.table.find_const(it.name) { + println('CONST: $it.name') + + return constant.typ + } + } return table.void_type } ast.BoolLiteral { @@ -403,20 +407,10 @@ pub fn (c &Checker) expr(node ast.Expr) table.Type { return c.index_expr(it) } ast.IfExpr { - typ := c.expr(it.cond) - typ_sym := c.table.get_type_symbol(typ) - // if typ_sym.kind != .bool { - if table.type_idx(typ) != table.bool_type_idx { - c.error('non-bool (`$typ_sym.name`) used as if condition', it.pos) - } - for i, stmt in it.stmts { - c.stmt(stmt) - } - if it.else_stmts.len > 0 { - for stmt in it.else_stmts { - c.stmt(stmt) - } - } + return c.if_expr(mut it) + } + ast.MatchExpr { + return c.match_expr(mut it) } ast.CastExpr { return it.typ @@ -426,7 +420,70 @@ pub fn (c &Checker) expr(node ast.Expr) table.Type { return table.void_type } -pub fn (c &Checker) postfix_expr(node ast.PostfixExpr) table.Type { +pub fn (c mut Checker) match_expr(node mut ast.MatchExpr) table.Type { + t := c.expr(node.cond) + for i, block in node.blocks { + match_expr := node.match_exprs[i] + c.expr(match_expr) + for stmt in block.stmts { + c.stmt(stmt) + } + // If the last statement is an expression, return its type + if block.stmts.len > 0 { + match block.stmts[block.stmts.len - 1] { + ast.ExprStmt { + // TODO: ask alex about this + //typ := c.expr(it.expr) + //type_sym := c.table.get_type_symbol(typ) + //p.warn('match expr ret $type_sym.name') + //node.typ = typ + //return typ + } + else {} + } + } + } + node.typ = t + return t +} + +pub fn (c mut Checker) if_expr(node mut ast.IfExpr) table.Type { + typ := c.expr(node.cond) + node.typ = typ + typ_sym := c.table.get_type_symbol(typ) + // if typ_sym.kind != .bool { + if table.type_idx(typ) != table.bool_type_idx { + c.error('non-bool (`$typ_sym.name`) used as if condition', node.pos) + } + for i, stmt in node.stmts { + c.stmt(stmt) + } + if node.else_stmts.len > 0 { + for stmt in node.else_stmts { + c.stmt(stmt) + } + } + if node.stmts.len > 0 { + match node.stmts[node.stmts.len - 1] { + ast.ExprStmt { + //type_sym := p.table.get_type_symbol(it.typ) + //p.warn('if expr ret $type_sym.name') + //typ = it.typ + //return it.typ + t := c.expr(it.expr) + node.typ = t + return t + // return node,it.ti + // left = + } + else {} + } + } + return typ + //return table.void_type +} + +pub fn (c mut Checker) postfix_expr(node ast.PostfixExpr) table.Type { /* match node.expr { ast.IdentVar { @@ -444,7 +501,16 @@ pub fn (c &Checker) postfix_expr(node ast.PostfixExpr) table.Type { return typ } -pub fn (c &Checker) index_expr(node ast.IndexExpr) table.Type { +pub fn (c mut Checker) index_expr(node ast.IndexExpr) table.Type { +/* + mut typ := left_type + left_type_sym := p.table.get_type_symbol(left_type) + if left_type_sym.kind == .array { + info := left_type_sym.info as table.Array + typ = info.elem_type + } +*/ + mut typ := c.expr(node.left) mut is_range := false // TODO is_range := node.index is ast.RangeExpr match node.index { diff --git a/vlib/v/gen/tests/1.c b/vlib/v/gen/tests/1.c index 72c6dd4327..df604ebab7 100644 --- a/vlib/v/gen/tests/1.c +++ b/vlib/v/gen/tests/1.c @@ -112,12 +112,12 @@ void println(string s) { void matches() { int a = 100; - int tmp2 = a; - if tmp2 == 10{ + int tmp3 = a; + if tmp3 == 10{ println(tos3("10")); } - if tmp2 == 20{ + if tmp3 == 20{ int k = a + 1; } diff --git a/vlib/v/gen/x64/gen.v b/vlib/v/gen/x64/gen.v index 58617b9991..811dadc248 100644 --- a/vlib/v/gen/x64/gen.v +++ b/vlib/v/gen/x64/gen.v @@ -310,6 +310,7 @@ pub fn (g mut Gen) call_fn(name string) { fn (g mut Gen) stmt(node ast.Stmt) { match node { + ast.ConstDecl {} ast.FnDecl { is_main := it.name == 'main' if is_main { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index b718851767..a055bad4b6 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -8,17 +8,15 @@ import ( v.table ) -pub fn (p mut Parser) call_expr() (ast.CallExpr,table.Type) { +pub fn (p mut Parser) call_expr() ast.CallExpr { tok := p.tok fn_name := p.check_name() p.check(.lpar) - // mut return_ti := types.void_ti args := p.call_args() node := ast.CallExpr{ name: fn_name args: args // tok: tok - pos: tok.position() } if p.tok.kind == .key_orelse { @@ -26,10 +24,9 @@ pub fn (p mut Parser) call_expr() (ast.CallExpr,table.Type) { p.parse_block() } if f := p.table.find_fn(fn_name) { - return node,f.return_type + return node } - typ := p.add_unresolved('${fn_name}()', node) - return node,typ + return node } pub fn (p mut Parser) call_args() []ast.Expr { @@ -49,12 +46,13 @@ pub fn (p mut Parser) call_args() []ast.Expr { } fn (p mut Parser) fn_decl() ast.FnDecl { - p.table.clear_vars() + //p.table.clear_vars() + p.open_scope() is_pub := p.tok.kind == .key_pub if is_pub { p.next() } - p.table.clear_vars() + //p.table.clear_vars() p.check(.key_fn) // C. is_c := p.tok.kind == .name && p.tok.lit == 'C' @@ -74,7 +72,11 @@ fn (p mut Parser) fn_decl() ast.FnDecl { p.next() } rec_type = p.parse_type() - p.table.register_var(table.Var{ + //p.table.register_var(table.Var{ + // name: rec_name + // typ: rec_type + //}) + p.scope.register_var(ast.VarDecl{ name: rec_name typ: rec_type }) @@ -101,7 +103,11 @@ fn (p mut Parser) fn_decl() ast.FnDecl { typ: ast_arg.typ } args << var - p.table.register_var(var) + p.scope.register_var(ast.VarDecl{ + name: ast_arg.name + typ: ast_arg.typ + }) + //p.table.register_var(var) } // /* @@ -149,6 +155,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { if p.tok.kind == .lcbr { stmts = p.parse_block() } + p.close_scope() return ast.FnDecl{ name: name stmts: stmts diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 8873c2db47..88e66b308f 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -78,6 +78,13 @@ pub fn (p mut Parser) parse_fn_type() table.Type { } pub fn (p mut Parser) parse_type() table.Type { + // optional + mut is_optional := false + if p.tok.kind == .question { + p.next() + is_optional = true + } + // &Type mut nr_muls := 0 for p.tok.kind == .amp { p.check(.amp) @@ -87,9 +94,6 @@ pub fn (p mut Parser) parse_type() table.Type { p.next() p.check(.dot) } - if p.tok.kind == .question { - p.next() - } // `module.Type` if p.peek_tok.kind == .dot { // /if !(p.tok.lit in p.table.imports) { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index bca5ebe5a3..b906e1d8ca 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -43,18 +43,19 @@ mut: pref &pref.Preferences // Preferences shared from V struct builtin_mod bool mod string - unresolved []ast.Expr - unresolved_offset int expected_type table.Type + scope &ast.Scope } // for tests -pub fn parse_stmt(text string, table &table.Table) ast.Stmt { +pub fn parse_stmt(text string, table &table.Table, scope &ast.Scope) ast.Stmt { s := scanner.new_scanner(text) mut p := Parser{ scanner: s table: table pref: &pref.Preferences{} + scope: scope + //scope: &ast.Scope{start_pos: 0, parent: 0} } p.init_parse_fns() p.read_first_token() @@ -72,9 +73,10 @@ pub fn parse_file(path string, table &table.Table) ast.File { table: table file_name: path pref: &pref.Preferences{} - unresolved_offset: table.unresolved_idxs.size + scope: &ast.Scope{start_pos: 0, parent: 0} } p.read_first_token() + //p.scope = &ast.Scope{start_pos: p.tok.position(), parent: 0} // module decl module_decl := if p.tok.kind == .key_module { p.module_decl() } else { ast.Module{name: 'main' } } @@ -97,12 +99,15 @@ pub fn parse_file(path string, table &table.Table) ast.File { } // println('nr stmts = $stmts.len') // println(stmts[0]) + + p.scope.end_pos = p.tok.pos + return ast.File{ path: path mod: module_decl imports: imports stmts: stmts - unresolved: p.unresolved + scope: *p.scope } } @@ -126,7 +131,22 @@ pub fn (p mut Parser) read_first_token() { p.next() } + +pub fn (p mut Parser) open_scope() { + p.scope = &ast.Scope{ + parent: p.scope + start_pos: p.tok.pos + } +} + +pub fn (p mut Parser) close_scope() { + p.scope.end_pos = p.tok.pos + p.scope.parent.children << p.scope + p.scope = p.scope.parent +} + pub fn (p mut Parser) parse_block() []ast.Stmt { + p.open_scope() p.table.open_scope() p.check(.lcbr) mut stmts := []ast.Stmt @@ -140,6 +160,8 @@ pub fn (p mut Parser) parse_block() []ast.Stmt { } } p.check(.rcbr) + println('parse block') + p.close_scope() p.table.close_scope() // println('nr exprs in block = $exprs.len') return stmts @@ -266,10 +288,11 @@ pub fn (p mut Parser) stmt() ast.Stmt { if p.tok.kind == .name && p.peek_tok.kind in [.comma] { return p.assign_stmt() } - expr,typ := p.expr(0) + //expr,typ := p.expr(0) + expr,_ := p.expr(0) return ast.ExprStmt{ expr: expr - typ: typ + //typ: typ } } } @@ -412,21 +435,21 @@ pub fn (p mut Parser) parse_ident(is_c bool) (ast.Ident,table.Type) { mut ident := ast.Ident{ name: name is_c: is_c + pos: p.tok.position() } mut known_var := false - if var := p.table.find_var(name) { + if var := p.scope.find_var(name) { known_var = true - typ = var.typ + // typ = var.typ } // variable - if known_var || p.tok.kind in [.comma, .decl_assign, .assign] { + if known_var /* || p.tok.kind in [.comma, .decl_assign, .assign]*/ { // println('#### IDENT: $var.name: $var.typ.typ.name - $var.typ.idx') ident.kind = .variable ident.info = ast.IdentVar{ - typ: typ + //typ: typ // name: ident.name // expr: p.expr(0)// var.expr - } return ident,typ } @@ -457,6 +480,7 @@ pub fn (p mut Parser) parse_ident(is_c bool) (ast.Ident,table.Type) { node = ast.Ident{ kind: .blank_ident name: name + //pos: p.tok.position() } return node,typ // p.error('parse_ident: unknown identifier `$name`') @@ -559,9 +583,8 @@ pub fn (p mut Parser) name_expr() (ast.Expr,table.Type) { // fn call else { println('calling $p.tok.lit') - x,ti2 := p.call_expr() // TODO `node,typ :=` should work + x := p.call_expr() // TODO `node,typ :=` should work node = x - typ = ti2 } } else if p.peek_tok.kind == .lcbr && (p.tok.lit[0].is_capital() || is_c || p.tok.lit in ['array', 'string', 'ustring', 'mapnode', 'map']) && !p.tok.lit[p.tok.lit.len - 1].is_capital() { @@ -615,7 +638,8 @@ pub fn (p mut Parser) expr(precedence int) (ast.Expr,table.Type) { p.next() } .key_match { - node,typ = p.match_expr() + //node,typ = p.match_expr() + node = p.match_expr() } .number { node,typ = p.parse_number_literal() @@ -626,10 +650,11 @@ pub fn (p mut Parser) expr(precedence int) (ast.Expr,table.Type) { p.check(.rpar) } .key_if { - node,typ = p.if_expr() + node = p.if_expr() } .lsbr { - node,typ = p.array_init() + //node,typ = p.array_init() + node = p.array_init() } .key_none { p.next() @@ -669,13 +694,15 @@ pub fn (p mut Parser) expr(precedence int) (ast.Expr,table.Type) { node = p.assign_expr(node) } else if p.tok.kind == .dot { - node,typ = p.dot_expr(node, typ) + node = p.dot_expr(node, typ) } else if p.tok.kind == .lsbr { - // node = p.index_expr(node) // , typ) + node = p.index_expr(node) // , typ) + /* ie_node,ie_typ := p.index_expr(node, typ) node = ie_node typ = ie_typ + */ } else if p.tok.kind == .key_as { p.next() @@ -712,7 +739,7 @@ fn (p mut Parser) prefix_expr() (ast.Expr,table.Type) { return expr,typ } -fn (p mut Parser) index_expr(left ast.Expr, left_type table.Type) (ast.IndexExpr,table.Type) { +fn (p mut Parser) index_expr(left ast.Expr) ast.IndexExpr { // left == `a` in `a[0]` p.next() // [ if p.tok.kind == .dotdot { @@ -727,7 +754,7 @@ fn (p mut Parser) index_expr(left ast.Expr, left_type table.Type) (ast.IndexExpr low: ast.Expr{} high: high } - },left_type // TODO: return correct type + } } expr,_ := p.expr(0) // `[expr]` or `[expr..]` if p.tok.kind == .dotdot { @@ -745,35 +772,44 @@ fn (p mut Parser) index_expr(left ast.Expr, left_type table.Type) (ast.IndexExpr low: expr high: high } - },left_type // TODO: return correct type + } } // get the element type + /* mut typ := left_type left_type_sym := p.table.get_type_symbol(left_type) if left_type_sym.kind == .array { info := left_type_sym.info as table.Array typ = info.elem_type } + */ // [expr] p.check(.rsbr) return ast.IndexExpr{ left: left index: expr pos: p.tok.position() - },typ + } } fn (p mut Parser) filter(typ table.Type) { + /* p.table.register_var(table.Var{ name: 'it' typ: typ }) + */ + p.scope.register_var(ast.VarDecl{ + name: 'it' + typ: typ + }) } -fn (p mut Parser) dot_expr(left ast.Expr, left_type table.Type) (ast.Expr,table.Type) { +fn (p mut Parser) dot_expr(left ast.Expr, left_type table.Type) ast.Expr { p.next() field_name := p.check_name() if field_name == 'filter' { + p.open_scope() p.table.open_scope() p.filter(left_type) } @@ -794,8 +830,8 @@ fn (p mut Parser) dot_expr(left ast.Expr, left_type table.Type) (ast.Expr,table. mut node := ast.Expr{} node = mcall_expr // typ := p.add_unresolved('${left_type.typ.name}.${field_name}()', mcall_expr) - typ := p.add_unresolved('${table.type_idx(left_type)}.${field_name}()', mcall_expr) - return node,typ + // typ := p.add_unresolved('${table.type_idx(left_type)}.${field_name}()', mcall_expr) + return node } sel_expr := ast.SelectorExpr{ expr: left @@ -803,13 +839,14 @@ fn (p mut Parser) dot_expr(left ast.Expr, left_type table.Type) (ast.Expr,table. pos: p.tok.position() } // typ := p.add_unresolved('${left_type.typ.name}.$field_name', sel_expr) - typ := p.add_unresolved('${table.type_idx(left_type)}.$field_name', sel_expr) + // typ := p.add_unresolved('${table.type_idx(left_type)}.$field_name', sel_expr) mut node := ast.Expr{} node = sel_expr if field_name == 'filter' { + p.close_scope() p.table.close_scope() } - return node,typ + return node } fn (p mut Parser) infix_expr(left ast.Expr) (ast.Expr,table.Type) { @@ -853,11 +890,13 @@ fn (p mut Parser) enum_val() (ast.Expr,table.Type) { fn (p mut Parser) for_statement() ast.Stmt { p.check(.key_for) + p.open_scope() p.table.open_scope() // defer { p.table.close_scope() } // Infinite loop if p.tok.kind == .lcbr { stmts := p.parse_block() + p.close_scope() p.table.close_scope() return ast.ForStmt{ stmts: stmts @@ -896,6 +935,7 @@ fn (p mut Parser) for_statement() ast.Stmt { inc = p.stmt() } stmts := p.parse_block() + p.close_scope() p.table.close_scope() return ast.ForCStmt{ stmts: stmts @@ -910,7 +950,11 @@ fn (p mut Parser) for_statement() ast.Stmt { if p.tok.kind == .comma { p.check(.comma) val_name := p.check_name() - p.table.register_var(table.Var{ + //p.table.register_var(table.Var{ + // name: val_name + // typ: table.int_type + //}) + p.scope.register_var(ast.VarDecl{ name: val_name typ: table.int_type }) @@ -945,12 +989,17 @@ fn (p mut Parser) for_statement() ast.Stmt { p.check(.dotdot) p.expr(0) } - p.table.register_var(table.Var{ + //p.table.register_var(table.Var{ + // name: var_name + // typ: elem_type + //}) + p.scope.register_var(ast.VarDecl{ name: var_name typ: elem_type }) stmts := p.parse_block() // println('nr stmts=$stmts.len') + p.close_scope() p.table.close_scope() return ast.ForStmt{ stmts: stmts @@ -960,6 +1009,7 @@ fn (p mut Parser) for_statement() ast.Stmt { // `for cond {` cond,_ := p.expr(0) stmts := p.parse_block() + p.close_scope() p.table.close_scope() return ast.ForStmt{ cond: cond @@ -968,7 +1018,7 @@ fn (p mut Parser) for_statement() ast.Stmt { } } -fn (p mut Parser) if_expr() (ast.Expr,table.Type) { +fn (p mut Parser) if_expr() ast.Expr { p.inside_if = true // defer { // } @@ -996,9 +1046,10 @@ fn (p mut Parser) if_expr() (ast.Expr,table.Type) { else_stmts = p.parse_block() } } - mut typ := table.void_type + //mut typ := table.void_type // mut left := ast.Expr{} // If the last statement is an expression, return its type + /* if stmts.len > 0 { match stmts[stmts.len - 1] { ast.ExprStmt { @@ -1011,16 +1062,17 @@ fn (p mut Parser) if_expr() (ast.Expr,table.Type) { else {} } } + */ node = ast.IfExpr{ cond: cond stmts: stmts else_stmts: else_stmts - typ: typ + //typ: typ pos: p.tok.position() // left: left } - return node,typ + return node } fn (p mut Parser) string_expr() (ast.Expr,table.Type) { @@ -1054,7 +1106,8 @@ fn (p mut Parser) string_expr() (ast.Expr,table.Type) { return node,table.string_type } -fn (p mut Parser) array_init() (ast.Expr,table.Type) { +//fn (p mut Parser) array_init() (ast.Expr,table.Type) { +fn (p mut Parser) array_init() ast.Expr { mut node := ast.Expr{} p.check(.lsbr) // `[]` - empty array with an automatically deduced type @@ -1066,6 +1119,7 @@ fn (p mut Parser) array_init() (ast.Expr,table.Type) { } */ + /* TODO: joe if p.tok.kind == .rsbr && int(p.expected_type) != 0 && p.table.get_type_symbol(p.expected_type).kind == .array { // p.warn('[] expr') node = ast.ArrayInit{ @@ -1076,10 +1130,11 @@ fn (p mut Parser) array_init() (ast.Expr,table.Type) { p.check(.rsbr) return node,p.expected_type } + */ mut val_type := table.void_type mut exprs := []ast.Expr - mut is_fixed := false - mut fixed_size := 0 + //mut is_fixed := false + //mut fixed_size := 0 if p.tok.kind == .rsbr { p.check(.rsbr) // []string @@ -1104,9 +1159,11 @@ fn (p mut Parser) array_init() (ast.Expr,table.Type) { p.check(.comma) } } - line_nr := p.tok.line_nr + //line_nr := p.tok.line_nr p.check(.rsbr) // Fixed size array? (`[100]byte`) + // NOTE: this should be hanled in parse_type() ? + /* if exprs.len == 1 && p.tok.kind == .name && p.tok.line_nr == line_nr { is_fixed = true val_type = p.parse_type() @@ -1115,18 +1172,19 @@ fn (p mut Parser) array_init() (ast.Expr,table.Type) { fixed_size = it.val } else {} - } + } p.warn('fixed size array') } + */ } - idx := if is_fixed { p.table.find_or_register_array_fixed(val_type, fixed_size, 1) } else { p.table.find_or_register_array(val_type, 1) } - array_type := table.new_type(idx) + //idx := if is_fixed { p.table.find_or_register_array_fixed(val_type, fixed_size, 1) } else { p.table.find_or_register_array(val_type, 1) } + //array_type := table.new_type(idx) node = ast.ArrayInit{ - typ: array_type + //typ: array_type exprs: exprs pos: p.tok.position() } - return node,array_type + return node } fn (p mut Parser) parse_number_literal() (ast.Expr,table.Type) { @@ -1363,26 +1421,37 @@ fn (p mut Parser) var_decl() ast.VarDecl { } name := p.check_name() p.next() - expr,typ := p.expr(0) - if _ := p.table.find_var(name) { + //expr,typ := p.expr(0) + expr,_ := p.expr(0) + //if _ := p.table.find_var(name) { + // p.error('redefinition of `$name`') + //} + //p.table.register_var(table.Var{ + // name: name + // is_mut: is_mut + // typ: typ + //}) + if _ := p.scope.find_var(name) { p.error('redefinition of `$name`') } - p.table.register_var(table.Var{ - name: name - is_mut: is_mut - typ: typ - }) - typ_sym := p.table.get_type_symbol(typ) - p.warn('var decl name=$name typ=$typ_sym.name') + //p.scope.register_var(table.Var{ + // name: name + // is_mut: is_mut + // typ: typ + //}) + + //typ_sym := p.table.get_type_symbol(typ) + //p.warn('var decl name=$name typ=$typ_sym.name') // println(p.table.names) node := ast.VarDecl{ name: name expr: expr // p.expr(token.lowest_prec) is_mut: is_mut - typ: typ + //typ: typ pos: p.tok.position() } + p.scope.register_var(node) return node } @@ -1427,19 +1496,19 @@ fn (p mut Parser) global_decl() ast.GlobalDecl { } } -fn (p mut Parser) match_expr() (ast.Expr,table.Type) { +fn (p mut Parser) match_expr() ast.Expr { p.check(.key_match) is_mut := p.tok.kind == .key_mut if is_mut { p.next() } - cond,typ := p.expr(0) + cond,_ := p.expr(0) // sym := p.table.get_type_symbol(typ) // p.warn('match typ $sym.name') p.check(.lcbr) mut blocks := []ast.StmtBlock mut match_exprs := []ast.Expr - mut return_type := table.void_type + //mut return_type := table.void_type for { // Sum type match if p.tok.kind == .name && (p.tok.lit[0].is_capital() || p.peek_tok.kind == .dot) { @@ -1475,6 +1544,7 @@ fn (p mut Parser) match_expr() (ast.Expr,table.Type) { } } // If the last statement is an expression, return its type + /* if stmts.len > 0 { match stmts[stmts.len - 1] { ast.ExprStmt { @@ -1483,8 +1553,10 @@ fn (p mut Parser) match_expr() (ast.Expr,table.Type) { return_type = it.typ } else {} - } + } } + */ + if p.tok.kind == .rcbr { break } @@ -1494,10 +1566,11 @@ fn (p mut Parser) match_expr() (ast.Expr,table.Type) { node = ast.MatchExpr{ blocks: blocks match_exprs: match_exprs - typ: typ + //typ: typ cond: cond } - return node,return_type + return node + //return node,return_type } fn (p mut Parser) enum_decl() ast.EnumDecl { @@ -1562,18 +1635,6 @@ fn (p mut Parser) type_decl() ast.TypeDecl { } } -fn (p mut Parser) add_unresolved(key string, expr ast.Expr) table.Type { - mut idx := p.unresolved_offset + p.unresolved.len - if key in p.table.unresolved_idxs { - idx = p.table.unresolved_idxs[key] - } - else { - p.table.unresolved_idxs[key] = idx - p.unresolved << expr - } - return table.new_type((-idx) - 1) -} - fn verror(s string) { println(s) exit(1) diff --git a/vlib/v/parser/parser_test.v b/vlib/v/parser/parser_test.v index 74212bc808..0545d4da1f 100644 --- a/vlib/v/parser/parser_test.v +++ b/vlib/v/parser/parser_test.v @@ -32,20 +32,26 @@ fn test_eval() { '20', // ] + /* table := table.new_table() + mut scope := ast.Scope{start_pos: 0, parent: 0} mut stmts := []ast.Stmt for input in inputs { - stmts << parse_stmt(input, table) + stmts << parse_stmt(input, table, &scope) } file := ast.File{ stmts: stmts + scope: &scope } + mut checker := checker.new_checker(table) + checker.check(file) mut ev := eval.Eval{} s := ev.eval(file, table) println('eval done') println(s) assert s == expected.join('\n') // exit(0) + */ } fn test_parse_file() { @@ -65,6 +71,8 @@ x := 10 ' table := &table.Table{} prog := parse_file(s, table) + mut checker := checker.new_checker(table) + checker.check(prog) res := gen.cgen([prog], table) println(res) } @@ -79,14 +87,21 @@ fn test_one() { ] expected := 'int a = 10;int b = -a;int c = 20;' table := table.new_table() + mut scope := ast.Scope{start_pos: 0, parent: 0} mut e := []ast.Stmt for line in input { - e << parse_stmt(line, table) + e << parse_stmt(line, table, &scope) } program := ast.File{ stmts: e + scope: scope } + mut checker := checker.new_checker(table) + checker.check(program) + //ast.print_scope_vars(scope, 0) + //ast.print_scope_vars(program.scope, 0) res := gen.cgen([program], table).replace('\n', '').trim_space() + println(res) ok := expected == res println(res) assert ok @@ -166,12 +181,14 @@ fn test_parse_expr() { mut e := []ast.Stmt table := table.new_table() mut checker := checker.new_checker(table) + mut scope := ast.Scope{start_pos: 0, parent: 0} for s in input { println('\n\nst="$s"') - e << parse_stmt(s, table) + e << parse_stmt(s, table, &scope) } program := ast.File{ stmts: e + scope: scope } checker.check(program) res := gen.cgen([program], table) diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index a475587add..dfca75dddc 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -80,6 +80,7 @@ fn (s &Scanner) scan_res(tok_kind token.Kind, lit string) token.Token { kind: tok_kind lit: lit line_nr: s.line_nr + 1 + pos: s.pos } } diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index a077400d7d..bc79c6e9b4 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -11,7 +11,6 @@ pub mut: types []TypeSymbol // type_idxs Hashmap type_idxs map[string]int - unresolved_idxs map[string]int local_vars []Var scope_level int var_idx int @@ -308,7 +307,7 @@ pub fn (t &Table) get_type_symbol(typ Type) &TypeSymbol { return &t.types[idx] } // this should never happen - panic('get_type_symbol: invalid type $idx') + panic('get_type_symbol: invalid type $typ - $idx') } // this will override or register builtin type diff --git a/vlib/v/token/position.v b/vlib/v/token/position.v index 3f7c497069..77897debdc 100644 --- a/vlib/v/token/position.v +++ b/vlib/v/token/position.v @@ -6,14 +6,13 @@ module token pub struct Position { pub: line_nr int // the line number in the source where the token occured - // pos int // the position of the token in scanner text + pos int // the position of the token in scanner text } [inline] pub fn (tok &Token) position() Position { return Position{ line_nr: tok.line_nr - 1 - // pos: tok.pos - + pos: tok.pos } } diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 40b4bf2e92..9ebac3d12b 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -9,7 +9,7 @@ pub: lit string // literal representation of the token line_nr int // the line number in the source where the token occured // name_idx int // name table index for O(1) lookup - // pos int // the position of the token in scanner text + pos int // the position of the token in scanner text } pub enum Kind {