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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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