SelectorExpr; receivers; struct field check; if expression

pull/3313/head
Alexander Medvednikov 2020-01-02 20:09:15 +01:00
parent 3c65af8b9a
commit 492dfebd15
15 changed files with 278 additions and 55 deletions

2
v2.v
View File

@ -22,7 +22,7 @@ fn main() {
println('V2 $path') println('V2 $path')
table := table.new_table() table := table.new_table()
program := parser.parse_file(path, table) program := parser.parse_file(path, table)
res := gen.cgen([program]) res := gen.cgen([program], table)
mut out := os.create('out.c')? mut out := os.create('out.c')?
out.writeln(cdefs) out.writeln(cdefs)
out.writeln(res) out.writeln(res)

View File

@ -15,7 +15,7 @@ pub:
element_size int 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 { fn new_array(mylen int, cap int, elm_size int) array {
cap_ := if cap == 0 { 1 } else { cap } cap_ := if cap == 0 { 1 } else { cap }
arr := array{ arr := array{
@ -28,7 +28,7 @@ fn new_array(mylen int, cap int, elm_size int) array {
} }
// TODO // 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) 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) data: calloc(cap_ * elm_size)
} }
// TODO Write all memory functions (like memcpy) in V // 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 return arr
} }

View File

@ -14,6 +14,7 @@ import (
v.table v.table
v.parser v.parser
v.gen v.gen
time
) )
pub const ( pub const (
@ -397,7 +398,7 @@ pub fn (v mut V) compile2() {
} }
table := table.new_table() table := table.new_table()
files := parser.parse_files(v.files, table) files := parser.parse_files(v.files, table)
c := gen.cgen(files) c := gen.cgen(files, table)
println('out: $v.out_name_c') println('out: $v.out_name_c')
os.write_file(v.out_name_c, 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.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare'))
v.files << v.dir v.files << v.dir
table := &table.Table{} table := &table.new_table()
ticks := time.ticks()
files := parser.parse_files(v.files, table) files := parser.parse_files(v.files, table)
println('PARSE: ${time.ticks() - ticks}ms')
x64.gen(files, v.out_name) x64.gen(files, v.out_name)
println('x64 GEN: ${time.ticks() - ticks}ms')
/* /*
for f in v.files { for f in v.files {
v.parse(f, .decl) v.parse(f, .decl)

View File

@ -8,15 +8,16 @@ import (
v.types v.types
) )
pub type Expr = BinaryExpr | UnaryExpr | IfExpr | StringLiteral | IntegerLiteral | pub type Expr = BinaryExpr | UnaryExpr | IfExpr | StringLiteral | IntegerLiteral |
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit 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 ForStmt | StructDecl
// Stand-alone expression in a statement list. // Stand-alone expression in a statement list.
pub struct ExprStmt { pub struct ExprStmt {
pub: pub:
expr Expr expr Expr
typ types.Type
} }
pub struct IntegerLiteral { pub struct IntegerLiteral {
@ -40,6 +41,13 @@ pub:
val bool val bool
} }
// `foo.bar`
pub struct SelectorExpr {
pub:
expr Expr
field string
}
// module declaration // module declaration
pub struct Module { pub struct Module {
pub: pub:
@ -84,10 +92,12 @@ pub:
pub struct FnDecl { pub struct FnDecl {
pub: pub:
name string name string
stmts []Stmt stmts []Stmt
typ types.Type typ types.Type
args []Arg args []Arg
is_pub bool
receiver Field
} }
pub struct CallExpr { pub struct CallExpr {
@ -164,6 +174,8 @@ pub:
cond Expr cond Expr
stmts []Stmt stmts []Stmt
else_stmts []Stmt else_stmts []Stmt
typ types.Type
left Expr // `a` in `a := if ...`
} }
pub struct ForStmt { pub struct ForStmt {

View File

@ -3,18 +3,22 @@ module gen
import ( import (
strings strings
v.ast v.ast
v.table
v.types
term term
) )
struct Gen { struct Gen {
out strings.Builder out strings.Builder
definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) 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{ mut g := Gen{
out: strings.new_builder(100) out: strings.new_builder(100)
definitions: strings.new_builder(100) definitions: strings.new_builder(100)
table: table
} }
for file in files { for file in files {
for stmt in file.stmts { for stmt in file.stmts {
@ -139,6 +143,9 @@ fn (g mut Gen) expr(node ast.Expr) {
} }
ast.BinaryExpr { ast.BinaryExpr {
g.expr(it.left) g.expr(it.left)
if it.op == .dot {
println('!! dot')
}
g.write(' $it.op.str() ') g.write(' $it.op.str() ')
g.expr(it.right) g.expr(it.right)
// if typ.name != typ2.name { // if typ.name != typ2.name {
@ -184,11 +191,28 @@ fn (g mut Gen) expr(node ast.Expr) {
g.write('false') g.write('false')
} }
} }
ast.SelectorExpr {
g.expr(it.expr)
g.write('.')
g.write(it.field)
}
ast.IfExpr { 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.write('if (')
g.expr(it.cond) g.expr(it.cond)
g.writeln(') {') 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.stmt(stmt)
} }
g.writeln('}') g.writeln('}')

View File

@ -23,13 +23,14 @@ fn test_c_files() {
} }
table := &table.new_table() table := &table.new_table()
program := parser.parse_file(path, table) program := parser.parse_file(path, table)
res := gen.cgen([program]) res := gen.cgen([program], table)
if compare_texts(res, ctext) { if compare_texts(res, ctext) {
eprintln('${i}... ' + term.green('OK')) eprintln('${i}... ' + term.green('OK'))
} }
else { else {
eprintln('${i}... ' + term.red('FAIL')) eprintln('${i}... ' + term.red('FAIL'))
eprintln('expected:\n$ctext\ngot:\n$res') eprintln(path)
eprintln('got:\n$res')
} }
} }
} }

View File

@ -1,4 +1,11 @@
void foo(int a); void foo(int a);
int get_int(string a);
int get_int2();
void myuser();
typedef struct {
int age;
} User;
int main() { int main() {
int a = 10; int a = 10;
@ -10,5 +17,22 @@ return 0;
} }
void foo(int a) { 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;
} }

View File

@ -1,3 +1,7 @@
struct User {
age int
}
fn main() { fn main() {
a := 10 a := 10
a++ a++
@ -7,5 +11,21 @@ fn main() {
} }
fn foo(a int) { 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
} }

View File

@ -0,0 +1,5 @@
fn foo() {
a := if true { 1 } else { 2 }
}

View File

@ -20,7 +20,9 @@ pub fn (p mut Parser) call_expr() (ast.CallExpr,types.Type) {
p.check(.lpar) p.check(.lpar)
mut is_unknown := false mut is_unknown := false
mut args := []ast.Expr mut args := []ast.Expr
mut return_type := types.void_type
if f := p.table.find_fn(fn_name) { if f := p.table.find_fn(fn_name) {
return_type = f.return_type
for i, arg in f.args { for i, arg in f.args {
e,typ := p.expr(0) e,typ := p.expr(0)
if !types.check(arg.typ, typ) { if !types.check(arg.typ, typ) {
@ -51,16 +53,38 @@ pub fn (p mut Parser) call_expr() (ast.CallExpr,types.Type) {
args: args args: args
is_unknown: is_unknown is_unknown: is_unknown
tok: tok tok: tok
// typ: return_type
} }
if is_unknown { if is_unknown {
p.table.unknown_calls << node p.table.unknown_calls << node
} }
return node,types.int_type return node,return_type
} }
fn (p mut Parser) fn_decl() ast.FnDecl { 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.table.clear_vars()
p.check(.key_fn) 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() name := p.check_name()
// println('fn decl $name') // println('fn decl $name')
p.check(.lpar) p.check(.lpar)
@ -68,17 +92,24 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
mut args := []table.Var mut args := []table.Var
mut ast_args := []ast.Arg mut ast_args := []ast.Arg
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
arg_name := p.check_name() mut arg_names := [p.check_name()]
typ := p.parse_type() // `a, b, c int`
arg := table.Var{ for p.tok.kind == .comma {
name: arg_name p.check(.comma)
typ: typ arg_names << p.check_name()
} }
args << arg typ := p.parse_type()
p.table.register_var(arg) for arg_name in arg_names {
ast_args << ast.Arg{ arg := table.Var{
typ: typ name: arg_name
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 { if p.tok.kind != .rpar {
p.check(.comma) p.check(.comma)
@ -94,6 +125,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
p.table.register_fn(table.Fn{ p.table.register_fn(table.Fn{
name: name name: name
args: args args: args
return_type: typ
}) })
stmts := p.parse_block() stmts := p.parse_block()
return ast.FnDecl{ return ast.FnDecl{
@ -101,16 +133,23 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
stmts: stmts stmts: stmts
typ: typ typ: typ
args: ast_args args: ast_args
is_pub: is_pub
receiver: ast.Field{
name: rec_name
typ: rec_type
}
} }
} }
pub fn (p &Parser) check_fn_calls() { pub fn (p &Parser) check_fn_calls() {
println('check fn calls') println('check fn calls2')
for call in p.table.unknown_calls { for call in p.table.unknown_calls {
f := p.table.find_fn(call.name) or { f := p.table.find_fn(call.name) or {
p.error_at_line('unknown function `$call.name`', call.tok.line_nr) p.error_at_line('unknown function `$call.name`', call.tok.line_nr)
return return
} }
println(f.name) println(f.name)
// println(f.return_type.name)
// println('IN AST typ=' + call.typ.name)
} }
} }

View File

@ -22,6 +22,7 @@ mut:
// vars []string // vars []string
table &table.Table table &table.Table
return_type types.Type return_type types.Type
is_c bool
} }
pub fn parse_stmt(text string, table &table.Table) ast.Stmt { 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() // former get_type()
pub fn (p mut Parser) parse_type() types.Type { pub fn (p mut Parser) parse_type() types.Type {
typ := p.table.types[p.tok.lit] typ := p.table.find_type(p.tok.lit) or {
if isnil(typ.name.str) || typ.name == '' { // typ := p.table.types[p.tok.lit]
// if isnil(typ.name.str) || typ.name == '' {
p.error('undefined type `$p.tok.lit`') p.error('undefined type `$p.tok.lit`')
exit(0)
} }
p.next() p.next()
return typ return typ
@ -181,9 +184,10 @@ pub fn (p mut Parser) stmt() ast.Stmt {
return p.for_statement() return p.for_statement()
} }
else { else {
expr,_ := p.expr(0) expr,typ := p.expr(0)
return ast.ExprStmt{ return ast.ExprStmt{
expr: expr expr: expr
typ: typ
} }
} }
} }
@ -242,7 +246,12 @@ pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) {
return return
} }
*/ */
if p.tok.lit == 'C' {
p.is_c = true
println('is c')
p.next()
p.check(.dot)
}
// fn call // fn call
if p.peek_tok.kind == .lpar { if p.peek_tok.kind == .lpar {
x,typ2 := p.call_expr() // TODO `node,typ :=` should work 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 // left binding power
for rbp < p.tok.precedence() { for rbp < p.tok.precedence() {
prev_tok := p.tok 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() p.next()
mut t2 := types.Type{} mut t2 := types.Type{}
// left denotation (infix / postfix) // 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) { fn (p mut Parser) if_expr() (ast.Expr,types.Type) {
mut node := ast.Expr{} mut node := ast.Expr{}
p.check(.key_if) p.check(.key_if)
cond,typ := p.expr(0) cond,cond_type := p.expr(0)
if !types.check(types.bool_type, typ) { if !types.check(types.bool_type, cond_type) {
p.error('non-bool used as if condition') p.error('non-bool used as if condition')
} }
stmts := p.parse_block() stmts := p.parse_block()
mut else_stmts := []ast.Stmt mut else_stmts := []ast.Stmt
if p.tok.kind == .key_else { if p.tok.kind == .key_else {
println('GOT ELSE') // println('GOT ELSE')
p.check(.key_else) p.check(.key_else)
else_stmts = p.parse_block() 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{ node = ast.IfExpr{
cond: cond cond: cond
stmts: stmts stmts: stmts
else_stmts: else_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) { 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) p.check(.key_struct)
name := p.check_name() name := p.check_name()
p.check(.lcbr) p.check(.lcbr)
mut fields := []ast.Field mut ast_fields := []ast.Field
mut fields := []types.Field
for p.tok.kind != .rcbr { for p.tok.kind != .rcbr {
if p.tok.kind == .key_pub { if p.tok.kind == .key_pub {
p.check(.key_pub) p.check(.key_pub)
@ -518,19 +560,24 @@ fn (p mut Parser) struct_decl() ast.StructDecl {
} }
field_name := p.check_name() field_name := p.check_name()
typ := p.parse_type() typ := p.parse_type()
fields << ast.Field{ ast_fields << ast.Field{
name: field_name name: field_name
typ: typ typ: typ
} }
fields << types.Field{
name: field_name
type_idx: typ.idx
}
} }
p.check(.rcbr) p.check(.rcbr)
p.table.register_type(types.Type{ p.table.register_type(types.Type{
name: name name: name
fields: fields
}) })
return ast.StructDecl{ return ast.StructDecl{
name: name name: name
is_pub: is_pub is_pub: is_pub
fields: fields fields: ast_fields
} }
} }

View File

@ -23,7 +23,7 @@ x := 10
' '
table := &table.Table{} table := &table.Table{}
prog := parse_file(s, table) prog := parse_file(s, table)
res := gen.cgen([prog]) res := gen.cgen([prog], table)
println(res) println(res)
} }
@ -79,7 +79,7 @@ fn test_parse_expr() {
program := ast.File{ program := ast.File{
stmts: e stmts: e
} }
res := gen.cgen([program]) res := gen.cgen([program], table)
println('========') println('========')
println(res) println(res)
println('========') println('========')

View File

@ -6,13 +6,15 @@ import (
) )
pub struct Table { pub struct Table {
// struct_fields map[string][]string
pub mut: pub mut:
types map[string]types.Type
local_vars []Var local_vars []Var
// fns Hashmap // fns Hashmap
fns map[string]Fn fns map[string]Fn
types map[string]types.Type
// //
unknown_calls []ast.CallExpr unknown_calls []ast.CallExpr
tmp_cnt int
} }
pub struct Var { pub struct Var {
@ -24,8 +26,9 @@ pub:
pub struct Fn { pub struct Fn {
pub: pub:
name string name string
args []Var args []Var
return_type types.Type
} }
pub fn new_table() &Table { 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) { pub fn (t mut Table) register_type(typ types.Type) {
t.types[typ.name] = typ 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'
}

View File

@ -311,6 +311,9 @@ pub const (
// Precedence returns a tokens precedence if defined, otherwise lowest_prec // Precedence returns a tokens precedence if defined, otherwise lowest_prec
pub fn (tok Token) precedence() int { pub fn (tok Token) precedence() int {
match tok.kind { match tok.kind {
.dot {
return 8
}
// `++` | `--` // `++` | `--`
.inc, .dec { .inc, .dec {
return 7 return 7

View File

@ -3,25 +3,51 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module types module types
pub enum Kind {
struct_
builtin
enum_
}
pub struct Type { pub struct Type {
pub: pub:
name string name string
idx int idx int
// kind Kind
fields []Field
}
pub struct Field {
pub:
name string
type_idx int
} }
pub const ( pub const (
void_type = Type{ void_type = Type{
'void',0} name: 'void'
idx: 0
}
int_type = Type{ int_type = Type{
'int',1} name: 'int'
idx: 1
}
string_type = Type{ string_type = Type{
'string',2} name: 'string'
idx: 2
}
f64_type = Type{ f64_type = Type{
'f64',3} name: 'f64'
idx: 3
}
bool_type = Type{ bool_type = Type{
'bool',4} name: 'bool'
idx: 4
}
voidptr_type = Type{ voidptr_type = Type{
'voidptr',5} name: 'voidptr'
idx: 5
}
) )
pub fn check(got, expected &Type) bool { pub fn check(got, expected &Type) bool {