handle unknown fns; fn.v; type fixes

pull/3311/head
Alexander Medvednikov 2020-01-02 08:30:15 +01:00
parent c949e9e636
commit 460b35137a
11 changed files with 178 additions and 119 deletions

5
v2.v
View File

@ -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)

View File

@ -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')

View File

@ -95,6 +95,7 @@ pub:
name string
args []Expr
is_unknown bool
tok token.Token
}
pub struct Return {

View File

@ -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('}')

View File

@ -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
}

View File

@ -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) {
}

View File

@ -3,4 +3,9 @@ fn main() {
a++
c := -a
a == 1
foo(3)
}
fn foo(a int) {
}

View File

@ -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;

114
vlib/v/parser/fn.v 100644
View File

@ -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)
}
}

View File

@ -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)

View File

@ -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 {