From 492dfebd156f3eade7d128c0ba69bb5e3ad3c16a Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 2 Jan 2020 20:09:15 +0100 Subject: [PATCH] SelectorExpr; receivers; struct field check; if expression --- v2.v | 2 +- vlib/builtin/array.v | 8 +++-- vlib/compiler/main.v | 8 +++-- vlib/v/ast/ast.v | 26 ++++++++++---- vlib/v/gen/cgen.v | 28 +++++++++++++-- vlib/v/gen/cgen_test.v | 5 +-- vlib/v/gen/tests/1.c | 26 +++++++++++++- vlib/v/gen/tests/1.vv | 22 +++++++++++- vlib/v/gen/tests/if_expr.vv | 5 +++ vlib/v/parser/fn.v | 63 ++++++++++++++++++++++++++------- vlib/v/parser/parser.v | 69 +++++++++++++++++++++++++++++++------ vlib/v/parser/parser_test.v | 4 +-- vlib/v/table/table.v | 22 ++++++++++-- vlib/v/token/token.v | 3 ++ vlib/v/types/types.v | 42 +++++++++++++++++----- 15 files changed, 278 insertions(+), 55 deletions(-) create mode 100644 vlib/v/gen/tests/if_expr.vv diff --git a/v2.v b/v2.v index 20bc2bffc4..db3024d8b6 100644 --- a/v2.v +++ b/v2.v @@ -22,7 +22,7 @@ fn main() { println('V2 $path') table := table.new_table() program := parser.parse_file(path, table) - res := gen.cgen([program]) + res := gen.cgen([program], table) mut out := os.create('out.c')? out.writeln(cdefs) out.writeln(res) diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index bcd71e3598..efb262d241 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -15,7 +15,7 @@ pub: element_size int } -// Private function, used by V (`nums := []int`) +// Internal function, used by V (`nums := []int`) fn new_array(mylen int, cap int, elm_size int) array { cap_ := if cap == 0 { 1 } else { cap } arr := array{ @@ -28,7 +28,7 @@ fn new_array(mylen int, cap int, elm_size int) array { } // TODO -pub fn make(len, cap, elm_size int) array { +pub fn make(len int, cap int, elm_size int) array { return new_array(len, cap, elm_size) } @@ -42,7 +42,9 @@ fn new_array_from_c_array(len, cap, elm_size int, c_array voidptr) array { data: calloc(cap_ * elm_size) } // TODO Write all memory functions (like memcpy) in V - C.memcpy(arr.data, c_array, len * elm_size) + C.memcpy( +arr.data, +c_array, len * elm_size) return arr } diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index 2501fcac49..bde0b9f89f 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -14,6 +14,7 @@ import ( v.table v.parser v.gen + time ) pub const ( @@ -397,7 +398,7 @@ pub fn (v mut V) compile2() { } table := table.new_table() files := parser.parse_files(v.files, table) - c := gen.cgen(files) + c := gen.cgen(files, table) println('out: $v.out_name_c') os.write_file(v.out_name_c, c) /* @@ -423,9 +424,12 @@ pub fn (v mut V) compile_x64() { //v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare')) v.files << v.dir - table := &table.Table{} + table := &table.new_table() + ticks := time.ticks() files := parser.parse_files(v.files, table) + println('PARSE: ${time.ticks() - ticks}ms') x64.gen(files, v.out_name) + println('x64 GEN: ${time.ticks() - ticks}ms') /* for f in v.files { v.parse(f, .decl) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 631be544de..ed4e047cc3 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -8,15 +8,16 @@ import ( v.types ) -pub type Expr = BinaryExpr | UnaryExpr | IfExpr | StringLiteral | IntegerLiteral | -FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit +pub type Expr = BinaryExpr | UnaryExpr | IfExpr | StringLiteral | IntegerLiteral | +FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr -pub type Stmt = VarDecl | FnDecl | Return | Module | Import | ExprStmt | AssignStmt | +pub type Stmt = VarDecl | FnDecl | Return | Module | Import | ExprStmt | AssignStmt | ForStmt | StructDecl // Stand-alone expression in a statement list. pub struct ExprStmt { pub: expr Expr + typ types.Type } pub struct IntegerLiteral { @@ -40,6 +41,13 @@ pub: val bool } +// `foo.bar` +pub struct SelectorExpr { +pub: + expr Expr + field string +} + // module declaration pub struct Module { pub: @@ -84,10 +92,12 @@ pub: pub struct FnDecl { pub: - name string - stmts []Stmt - typ types.Type - args []Arg + name string + stmts []Stmt + typ types.Type + args []Arg + is_pub bool + receiver Field } pub struct CallExpr { @@ -164,6 +174,8 @@ pub: cond Expr stmts []Stmt else_stmts []Stmt + typ types.Type + left Expr // `a` in `a := if ...` } pub struct ForStmt { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 18b6eefe3a..924390474e 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -3,18 +3,22 @@ module gen import ( strings v.ast + v.table + v.types term ) struct Gen { out strings.Builder definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) + table &table.Table } -pub fn cgen(files []ast.File) string { +pub fn cgen(files []ast.File, table &table.Table) string { mut g := Gen{ out: strings.new_builder(100) definitions: strings.new_builder(100) + table: table } for file in files { for stmt in file.stmts { @@ -139,6 +143,9 @@ fn (g mut Gen) expr(node ast.Expr) { } ast.BinaryExpr { g.expr(it.left) + if it.op == .dot { + println('!! dot') + } g.write(' $it.op.str() ') g.expr(it.right) // if typ.name != typ2.name { @@ -184,11 +191,28 @@ fn (g mut Gen) expr(node ast.Expr) { g.write('false') } } + ast.SelectorExpr { + g.expr(it.expr) + g.write('.') + g.write(it.field) + } ast.IfExpr { + // If expression? Assign the value to a temp var. + // Previously ?: was used, but it's too unreliable. + mut tmp := '' + if it.typ.idx != types.void_type.idx { + tmp = g.table.new_tmp_var() + // g.writeln('$it.typ.name $tmp;') + } g.write('if (') g.expr(it.cond) g.writeln(') {') - for stmt in it.stmts { + for i, stmt in it.stmts { + // Assign ret value + if i == it.stmts.len - 1 && it.typ.idx != types.void_type.idx { + // g.writeln('$tmp =') + println(1) + } g.stmt(stmt) } g.writeln('}') diff --git a/vlib/v/gen/cgen_test.v b/vlib/v/gen/cgen_test.v index a5162d0d81..e9959fcc58 100644 --- a/vlib/v/gen/cgen_test.v +++ b/vlib/v/gen/cgen_test.v @@ -23,13 +23,14 @@ fn test_c_files() { } table := &table.new_table() program := parser.parse_file(path, table) - res := gen.cgen([program]) + res := gen.cgen([program], table) if compare_texts(res, ctext) { eprintln('${i}... ' + term.green('OK')) } else { eprintln('${i}... ' + term.red('FAIL')) - eprintln('expected:\n$ctext\ngot:\n$res') + eprintln(path) + eprintln('got:\n$res') } } } diff --git a/vlib/v/gen/tests/1.c b/vlib/v/gen/tests/1.c index 9bda0762a3..fbf9b38e53 100644 --- a/vlib/v/gen/tests/1.c +++ b/vlib/v/gen/tests/1.c @@ -1,4 +1,11 @@ void foo(int a); +int get_int(string a); +int get_int2(); +void myuser(); + +typedef struct { + int age; +} User; int main() { int a = 10; @@ -10,5 +17,22 @@ return 0; } void foo(int a) { - + void n = get_int2(); +} + +int get_int(string a) { + return 10; +} + +int get_int2() { + string a = tos3("hello"); + return get_int(a); +} + +void myuser() { + User user = (User){ + .age = 10, + }; + User age = user.age; + bool b = age > 0; } diff --git a/vlib/v/gen/tests/1.vv b/vlib/v/gen/tests/1.vv index b05e9fd548..860d0366cd 100644 --- a/vlib/v/gen/tests/1.vv +++ b/vlib/v/gen/tests/1.vv @@ -1,3 +1,7 @@ +struct User { + age int +} + fn main() { a := 10 a++ @@ -7,5 +11,21 @@ fn main() { } fn foo(a int) { - + n := get_int2() +} + +fn get_int(a string) int { + return 10 +} + +fn get_int2() int { + a := 'hello' + return get_int(a) +} + +fn myuser() { + user := User{age:10} + age := user.age + b := age > 0 + //b2 := user.age > 0 } diff --git a/vlib/v/gen/tests/if_expr.vv b/vlib/v/gen/tests/if_expr.vv new file mode 100644 index 0000000000..f5ba3856e7 --- /dev/null +++ b/vlib/v/gen/tests/if_expr.vv @@ -0,0 +1,5 @@ +fn foo() { + a := if true { 1 } else { 2 } + + +} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 8b6cebd16f..f935c360b2 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -20,7 +20,9 @@ pub fn (p mut Parser) call_expr() (ast.CallExpr,types.Type) { p.check(.lpar) mut is_unknown := false mut args := []ast.Expr + mut return_type := types.void_type if f := p.table.find_fn(fn_name) { + return_type = f.return_type for i, arg in f.args { e,typ := p.expr(0) if !types.check(arg.typ, typ) { @@ -51,16 +53,38 @@ pub fn (p mut Parser) call_expr() (ast.CallExpr,types.Type) { args: args is_unknown: is_unknown tok: tok + // typ: return_type + } if is_unknown { p.table.unknown_calls << node } - return node,types.int_type + return node,return_type } fn (p mut Parser) fn_decl() ast.FnDecl { + is_pub := p.tok.kind == .key_pub + if is_pub { + p.next() + } p.table.clear_vars() p.check(.key_fn) + // Receiver? + mut rec_name := '' + mut rec_type := types.void_type + if p.tok.kind == .lpar { + p.next() + rec_name = p.check_name() + if p.tok.kind == .key_mut { + p.next() + } + rec_type = p.parse_type() + p.table.register_var(table.Var{ + name: rec_name + typ: rec_type + }) + p.check(.rpar) + } name := p.check_name() // println('fn decl $name') p.check(.lpar) @@ -68,17 +92,24 @@ fn (p mut Parser) fn_decl() ast.FnDecl { mut args := []table.Var mut ast_args := []ast.Arg for p.tok.kind != .rpar { - arg_name := p.check_name() - typ := p.parse_type() - arg := table.Var{ - name: arg_name - typ: typ + mut arg_names := [p.check_name()] + // `a, b, c int` + for p.tok.kind == .comma { + p.check(.comma) + arg_names << p.check_name() } - args << arg - p.table.register_var(arg) - ast_args << ast.Arg{ - typ: typ - name: arg_name + typ := p.parse_type() + for arg_name in arg_names { + arg := table.Var{ + name: arg_name + typ: typ + } + args << arg + p.table.register_var(arg) + ast_args << ast.Arg{ + typ: typ + name: arg_name + } } if p.tok.kind != .rpar { p.check(.comma) @@ -94,6 +125,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { p.table.register_fn(table.Fn{ name: name args: args + return_type: typ }) stmts := p.parse_block() return ast.FnDecl{ @@ -101,16 +133,23 @@ fn (p mut Parser) fn_decl() ast.FnDecl { stmts: stmts typ: typ args: ast_args + is_pub: is_pub + receiver: ast.Field{ + name: rec_name + typ: rec_type + } } } pub fn (p &Parser) check_fn_calls() { - println('check fn calls') + println('check fn calls2') for call in p.table.unknown_calls { f := p.table.find_fn(call.name) or { p.error_at_line('unknown function `$call.name`', call.tok.line_nr) return } println(f.name) + // println(f.return_type.name) + // println('IN AST typ=' + call.typ.name) } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 5a68c86fd2..b2e69d72e3 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -22,6 +22,7 @@ mut: // vars []string table &table.Table return_type types.Type + is_c bool } pub fn parse_stmt(text string, table &table.Table) ast.Stmt { @@ -74,9 +75,11 @@ pub fn parse_files(paths []string, table &table.Table) []ast.File { // former get_type() pub fn (p mut Parser) parse_type() types.Type { - typ := p.table.types[p.tok.lit] - if isnil(typ.name.str) || typ.name == '' { + typ := p.table.find_type(p.tok.lit) or { + // typ := p.table.types[p.tok.lit] + // if isnil(typ.name.str) || typ.name == '' { p.error('undefined type `$p.tok.lit`') + exit(0) } p.next() return typ @@ -181,9 +184,10 @@ pub fn (p mut Parser) stmt() ast.Stmt { return p.for_statement() } else { - expr,_ := p.expr(0) + expr,typ := p.expr(0) return ast.ExprStmt{ expr: expr + typ: typ } } } @@ -242,7 +246,12 @@ pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { return } */ - + if p.tok.lit == 'C' { + p.is_c = true + println('is c') + p.next() + p.check(.dot) + } // fn call if p.peek_tok.kind == .lpar { x,typ2 := p.call_expr() // TODO `node,typ :=` should work @@ -330,6 +339,25 @@ pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { // left binding power for rbp < p.tok.precedence() { prev_tok := p.tok + if prev_tok.kind == .dot { + p.warn('dot prev_tok = $prev_tok.str() typ=$typ.name') + p.next() + field := p.check_name() + mut ok := false + for f in typ.fields { + if f.name == field { + ok = true + } + } + if !ok { + p.error('unknown field `${typ.name}.$field`') + } + node = ast.SelectorExpr{ + expr: node + field: field + } + return node,typ + } p.next() mut t2 := types.Type{} // left denotation (infix / postfix) @@ -410,23 +438,36 @@ fn (p mut Parser) for_statement() ast.ForStmt { fn (p mut Parser) if_expr() (ast.Expr,types.Type) { mut node := ast.Expr{} p.check(.key_if) - cond,typ := p.expr(0) - if !types.check(types.bool_type, typ) { + cond,cond_type := p.expr(0) + if !types.check(types.bool_type, cond_type) { p.error('non-bool used as if condition') } stmts := p.parse_block() mut else_stmts := []ast.Stmt if p.tok.kind == .key_else { - println('GOT ELSE') + // println('GOT ELSE') p.check(.key_else) else_stmts = p.parse_block() } + mut typ := types.void_type + // mut left := ast.Expr{} + match stmts[stmts.len - 1] { + ast.ExprStmt { + typ = it.typ + // return node,it.typ + // left = + } + else {} + } node = ast.IfExpr{ cond: cond stmts: stmts else_stmts: else_stmts + typ: typ + // left: left + } - return node,types.void_type + return node,typ } fn (p mut Parser) parse_string_literal() (ast.Expr,types.Type) { @@ -510,7 +551,8 @@ fn (p mut Parser) struct_decl() ast.StructDecl { p.check(.key_struct) name := p.check_name() p.check(.lcbr) - mut fields := []ast.Field + mut ast_fields := []ast.Field + mut fields := []types.Field for p.tok.kind != .rcbr { if p.tok.kind == .key_pub { p.check(.key_pub) @@ -518,19 +560,24 @@ fn (p mut Parser) struct_decl() ast.StructDecl { } field_name := p.check_name() typ := p.parse_type() - fields << ast.Field{ + ast_fields << ast.Field{ name: field_name typ: typ } + fields << types.Field{ + name: field_name + type_idx: typ.idx + } } p.check(.rcbr) p.table.register_type(types.Type{ name: name + fields: fields }) return ast.StructDecl{ name: name is_pub: is_pub - fields: fields + fields: ast_fields } } diff --git a/vlib/v/parser/parser_test.v b/vlib/v/parser/parser_test.v index e767eb3bec..20ab4e3031 100644 --- a/vlib/v/parser/parser_test.v +++ b/vlib/v/parser/parser_test.v @@ -23,7 +23,7 @@ x := 10 ' table := &table.Table{} prog := parse_file(s, table) - res := gen.cgen([prog]) + res := gen.cgen([prog], table) println(res) } @@ -79,7 +79,7 @@ fn test_parse_expr() { program := ast.File{ stmts: e } - res := gen.cgen([program]) + res := gen.cgen([program], table) println('========') println(res) println('========') diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index cc4107202a..fcdfd72e56 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -6,13 +6,15 @@ import ( ) pub struct Table { + // struct_fields map[string][]string pub mut: + types map[string]types.Type local_vars []Var // fns Hashmap fns map[string]Fn - types map[string]types.Type // unknown_calls []ast.CallExpr + tmp_cnt int } pub struct Var { @@ -24,8 +26,9 @@ pub: pub struct Fn { pub: - name string - args []Var + name string + args []Var + return_type types.Type } pub fn new_table() &Table { @@ -109,3 +112,16 @@ pub fn (t mut Table) register_fn(new_fn Fn) { pub fn (t mut Table) register_type(typ types.Type) { t.types[typ.name] = typ } + +pub fn (t &Table) find_type(name string) ?types.Type { + typ := t.types[name] + if isnil(typ.name.str) || typ.name == '' { + return none + } + return typ +} + +pub fn (t mut Table) new_tmp_var() string { + t.tmp_cnt++ + return 'tmp$t.tmp_cnt' +} diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 1d26318481..468418a439 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -311,6 +311,9 @@ pub const ( // Precedence returns a tokens precedence if defined, otherwise lowest_prec pub fn (tok Token) precedence() int { match tok.kind { + .dot { + return 8 + } // `++` | `--` .inc, .dec { return 7 diff --git a/vlib/v/types/types.v b/vlib/v/types/types.v index 609ac95d0a..508179fb7f 100644 --- a/vlib/v/types/types.v +++ b/vlib/v/types/types.v @@ -3,25 +3,51 @@ // that can be found in the LICENSE file. module types +pub enum Kind { + struct_ + builtin + enum_ +} + pub struct Type { pub: - name string - idx int + name string + idx int + // kind Kind + fields []Field +} + +pub struct Field { +pub: + name string + type_idx int } pub const ( void_type = Type{ - 'void',0} + name: 'void' + idx: 0 + } int_type = Type{ - 'int',1} + name: 'int' + idx: 1 + } string_type = Type{ - 'string',2} + name: 'string' + idx: 2 + } f64_type = Type{ - 'f64',3} + name: 'f64' + idx: 3 + } bool_type = Type{ - 'bool',4} + name: 'bool' + idx: 4 + } voidptr_type = Type{ - 'voidptr',5} + name: 'voidptr' + idx: 5 + } ) pub fn check(got, expected &Type) bool {