diff --git a/v2.v b/v2.v index e96f0f03c5..33d7f175ab 100644 --- a/v2.v +++ b/v2.v @@ -20,9 +20,8 @@ string tos3(char* s) { return (string){ .str = s }; } fn main() { path := os.args[1] println('V2 $path') - text := os.read_file(path)? - table := &table.Table{} - program := parser.parse_file(text, table) + table := table.new_table() + program := parser.parse_file(path, table) res := gen.cgen([program]) mut out := os.create('out.c')? out.writeln(cdefs) diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index eeb6af078b..2501fcac49 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -395,7 +395,7 @@ pub fn (v mut V) compile2() { println('all .v files:') println(v.files) } - table := &table.Table{} + table := table.new_table() files := parser.parse_files(v.files, table) c := gen.cgen(files) println('out: $v.out_name_c') diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 37da026a4a..631be544de 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -95,6 +95,7 @@ pub: name string args []Expr is_unknown bool + tok token.Token } pub struct Return { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index aa91deed2d..18b6eefe3a 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -7,12 +7,14 @@ import ( ) struct Gen { - out strings.Builder + out strings.Builder + definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) } pub fn cgen(files []ast.File) string { mut g := Gen{ out: strings.new_builder(100) + definitions: strings.new_builder(100) } for file in files { for stmt in file.stmts { @@ -20,7 +22,7 @@ pub fn cgen(files []ast.File) string { g.writeln('') } } - return g.out.str() + return g.definitions.str() + g.out.str() } pub fn (g &Gen) save() {} @@ -42,20 +44,26 @@ fn (g mut Gen) stmt(node ast.Stmt) { g.writeln(';') } ast.FnDecl { - if it.name == 'main' { + is_main := it.name == 'main' + if is_main { g.write('int ${it.name}(') } else { g.write('$it.typ.name ${it.name}(') + g.definitions.write('$it.typ.name ${it.name}(') } for arg in it.args { g.write(arg.typ.name + ' ' + arg.name) + g.definitions.write(arg.typ.name + ' ' + arg.name) } g.writeln(') { ') + if !is_main { + g.definitions.writeln(');') + } for stmt in it.stmts { g.stmt(stmt) } - if it.name == 'main' { + if is_main { g.writeln('return 0;') } g.writeln('}') diff --git a/vlib/v/gen/cgen_test.v b/vlib/v/gen/cgen_test.v index 4494d831dc..a5162d0d81 100644 --- a/vlib/v/gen/cgen_test.v +++ b/vlib/v/gen/cgen_test.v @@ -21,7 +21,7 @@ fn test_c_files() { ctext := os.read_file('$vroot/vlib/v/gen/tests/${i}.c') or { panic(err) } - table := &table.Table{} + table := &table.new_table() program := parser.parse_file(path, table) res := gen.cgen([program]) if compare_texts(res, ctext) { @@ -46,7 +46,7 @@ fn compare_texts(a, b string) bool { for i, line_a in lines_a { line_b := lines_b[i] if line_a.trim_space() != line_b.trim_space() { - println(term.red('i=$i a="$line_a" b="$line_b"')) + println(term.red('i=$i V="$line_a" C="$line_b"')) // exit(1) return false } diff --git a/vlib/v/gen/tests/1.c b/vlib/v/gen/tests/1.c index fb1b0206c4..9bda0762a3 100644 --- a/vlib/v/gen/tests/1.c +++ b/vlib/v/gen/tests/1.c @@ -1,7 +1,14 @@ +void foo(int a); + int main() { int a = 10; a++; int c = -a; a == 1; +foo(3); return 0; } + +void foo(int a) { + +} diff --git a/vlib/v/gen/tests/1.vv b/vlib/v/gen/tests/1.vv index af5e50cc96..b05e9fd548 100644 --- a/vlib/v/gen/tests/1.vv +++ b/vlib/v/gen/tests/1.vv @@ -3,4 +3,9 @@ fn main() { a++ c := -a a == 1 + foo(3) +} + +fn foo(a int) { + } diff --git a/vlib/v/gen/tests/2.c b/vlib/v/gen/tests/2.c index 0f03e87129..90e697ec2f 100644 --- a/vlib/v/gen/tests/2.c +++ b/vlib/v/gen/tests/2.c @@ -1,3 +1,13 @@ +int function1(); +void foo(int a); +void init_user(); +User get_user(); +void puts(string s); +void function2(); +void init_array(); +void end(); + + int function1() { int a = 10 + 1; int b = a + 1; diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v new file mode 100644 index 0000000000..e58593edb8 --- /dev/null +++ b/vlib/v/parser/fn.v @@ -0,0 +1,114 @@ +// Copyright (c) 2019 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.scanner + v.ast + v.token + v.table + v.types + term + os +) + +pub fn (p mut Parser) call_expr() (ast.CallExpr,types.Type) { + // println('got fn call') + tok := p.tok + fn_name := p.check_name() + p.check(.lpar) + mut is_unknown := false + mut args := []ast.Expr + if f := p.table.find_fn(fn_name) { + for i, arg in f.args { + e,typ := p.expr(0) + if !types.check(arg.typ, typ) { + p.error('cannot use type `$typ.name` as type `$arg.typ.name` in argument to `$fn_name`') + } + args << e + if i < f.args.len - 1 { + p.check(.comma) + } + } + if p.tok.kind == .comma { + p.error('too many arguments in call to `$fn_name`') + } + }else{ + is_unknown = true + p.warn('unknown function `$fn_name`') + for p.tok.kind != .rpar { + e,_ := p.expr(0) + args << e + if p.tok.kind != .rpar { + p.check(.comma) + } + } + } + p.check(.rpar) + node := ast.CallExpr{ + name: fn_name + args: args + is_unknown: is_unknown + tok: tok + } + if is_unknown { + p.table.unknown_calls << node + } + return node,types.int_type +} + +fn (p mut Parser) fn_decl() ast.FnDecl { + p.table.clear_vars() + p.check(.key_fn) + name := p.check_name() + // println('fn decl $name') + p.check(.lpar) + // Args + mut args := []table.Var + mut ast_args := []ast.Arg + for p.tok.kind != .rpar { + arg_name := p.check_name() + typ := p.parse_type() + args << table.Var{ + name: arg_name + typ: typ + } + ast_args << ast.Arg{ + typ: typ + name: arg_name + } + if p.tok.kind != .rpar { + p.check(.comma) + } + } + p.check(.rpar) + // Return type + mut typ := types.void_type + if p.tok.kind == .name { + typ = p.parse_type() + p.return_type = typ + } + p.table.register_fn(table.Fn{ + name: name + args: args + }) + stmts := p.parse_block() + return ast.FnDecl{ + name: name + stmts: stmts + typ: typ + args: ast_args + } +} + +pub fn (p &Parser) check_fn_calls() { + println('CHEKC FN CALLS') + 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(call.name) + } +} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 8242168764..82803536a5 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -55,6 +55,7 @@ pub fn parse_file(path string, table &table.Table) ast.File { // println(s) stmts << s // p.stmt() } + p.check_fn_calls() // println('nr stmts = $stmts.len') // println(stmts[0]) return ast.File{ @@ -86,6 +87,7 @@ pub fn parse_files(paths []string, table &table.Table) []ast.File { // println(s) stmts << s // p.stmt() } + p.check_fn_calls() // println('nr stmts = $stmts.len') // println(stmts[0]) files << ast.File{ @@ -95,31 +97,14 @@ pub fn parse_files(paths []string, table &table.Table) []ast.File { return files } +// former get_type() pub fn (p mut Parser) parse_type() types.Type { - defer { - p.next() - } - match p.tok.lit { - 'int' { - return types.int_type - } - 'f64' { - return types.f64_type - } - 'string' { - return types.string_type - } - 'voidptr' { - return types.voidptr_type - } - else { - typ := p.table.types[p.tok.lit] - if isnil(typ.name.str) || typ.name == '' { - p.error('undefined type `$p.tok.lit`') - } - return typ - } + typ := p.table.types[p.tok.lit] + if isnil(typ.name.str) || typ.name == '' { + p.error('undefined type `$p.tok.lit`') } + p.next() + return typ } pub fn (p mut Parser) read_first_token() { @@ -259,50 +244,13 @@ pub fn (p &Parser) error(s string) { exit(1) } -pub fn (p &Parser) warn(s string) { - println(term.blue('x.v:$p.tok.line_nr: $s')) +pub fn (p &Parser) error_at_line(s string, line_nr int) { + println(term.bold(term.red('$p.file_name:$line_nr: $s'))) + exit(1) } -pub fn (p mut Parser) call_expr() (ast.CallExpr,types.Type) { - // println('got fn call') - fn_name := p.check_name() - p.check(.lpar) - mut is_unknown := false - mut args := []ast.Expr - if f := p.table.find_fn(fn_name) { - for i, arg in f.args { - e,typ := p.expr(0) - if !types.check(arg.typ, typ) { - p.error('cannot use type `$typ.name` as type `$arg.typ.name` in argument to `$fn_name`') - } - args << e - if i < f.args.len - 1 { - p.check(.comma) - } - } - if p.tok.kind == .comma { - p.error('too many arguments in call to `$fn_name`') - } - }else{ - is_unknown = true - p.warn('unknown function `$fn_name`') - for p.tok.kind != .rpar { - p.expr(0) - if p.tok.kind != .rpar { - p.check(.comma) - } - } - } - p.check(.rpar) - node := ast.CallExpr{ - name: fn_name - args: args - is_unknown: is_unknown - } - if is_unknown { - p.table.unknown_calls << node - } - return node,types.int_type +pub fn (p &Parser) warn(s string) { + println(term.blue('x.v:$p.tok.line_nr: $s')) } // Implementation of Pratt Precedence @@ -611,50 +559,6 @@ fn (p mut Parser) struct_decl() ast.StructDecl { } } -fn (p mut Parser) fn_decl() ast.FnDecl { - p.table.clear_vars() - p.check(.key_fn) - name := p.check_name() - // println('fn decl $name') - p.check(.lpar) - // Args - mut args := []table.Var - mut ast_args := []ast.Arg - for p.tok.kind != .rpar { - arg_name := p.check_name() - typ := p.parse_type() - args << table.Var{ - name: arg_name - typ: typ - } - ast_args << ast.Arg{ - typ: typ - name: arg_name - } - if p.tok.kind != .rpar { - p.check(.comma) - } - } - p.check(.rpar) - // Return type - mut typ := types.void_type - if p.tok.kind == .name { - typ = p.parse_type() - p.return_type = typ - } - p.table.register_fn(table.Fn{ - name: name - args: args - }) - stmts := p.parse_block() - return ast.FnDecl{ - name: name - stmts: stmts - typ: typ - args: ast_args - } -} - fn (p mut Parser) return_stmt() ast.Return { p.next() expr,t := p.expr(0) diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 7905a9cd08..cc4107202a 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -28,6 +28,17 @@ pub: args []Var } +pub fn new_table() &Table { + mut t := &Table{} + t.register_type(types.void_type) + t.register_type(types.int_type) + t.register_type(types.string_type) + t.register_type(types.f64_type) + t.register_type(types.bool_type) + t.register_type(types.voidptr_type) + return t +} + pub fn (t &Table) find_var(name string) ?Var { /* for i in 0 .. p.var_idx {