v2: type checker + lots of other changes

pull/3497/head
joe-conigliaro 2020-01-19 09:26:14 +11:00 committed by Alexander Medvednikov
parent 8c1b03c731
commit 09d1eb7c55
16 changed files with 893 additions and 546 deletions

View File

@ -8,11 +8,7 @@ import (
os.cmdline os.cmdline
strings strings
filepath filepath
v.gen.x64 v.builder
v.table
v.parser
v.gen
time
) )
pub const ( pub const (
@ -390,29 +386,15 @@ pub fn (v mut V) compile2() {
println('all .v files before:') println('all .v files before:')
println(v.files) println(v.files)
} }
v.add_v_files_to_compile() // v.add_v_files_to_compile()
v.files << v.dir
if v.pref.is_verbose { if v.pref.is_verbose {
println('all .v files:') println('all .v files:')
println(v.files) println(v.files)
} }
table := table.new_table() mut b := builder.new_builder()
files := parser.parse_files(v.files, table) b.build_c(v.files, v.out_name)
c := gen.cgen(files, table)
println('out: $v.out_name_c')
os.write_file(v.out_name_c, c)
/*
cgen.genln(c_builtin_types)
if !v.pref.is_bare {
cgen.genln(c_headers)
}
else {
cgen.genln(bare_c_headers)
}
}
*/
v.cc() v.cc()
} }
pub fn (v mut V) compile_x64() { pub fn (v mut V) compile_x64() {
@ -423,12 +405,8 @@ 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.new_table() mut b := builder.new_builder()
ticks := time.ticks() b.build_x64(v.files, v.out_name)
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')
} }
fn (v mut V) generate_init() { fn (v mut V) generate_init() {

View File

@ -45,6 +45,7 @@ pub:
// `foo.bar` // `foo.bar`
pub struct SelectorExpr { pub struct SelectorExpr {
pub: pub:
pos token.Position
expr Expr expr Expr
field string field string
} }
@ -60,11 +61,13 @@ pub:
pub struct Field { pub struct Field {
pub: pub:
name string name string
// type_idx int
ti types.TypeIdent ti types.TypeIdent
} }
pub struct StructDecl { pub struct StructDecl {
pub: pub:
pos token.Position
name string name string
fields []Field fields []Field
is_pub bool is_pub bool
@ -72,7 +75,7 @@ pub:
pub struct StructInit { pub struct StructInit {
pub: pub:
// typ types.TypeIdent pos token.Position
ti types.TypeIdent ti types.TypeIdent
fields []string fields []string
exprs []Expr exprs []Expr
@ -81,7 +84,9 @@ pub:
// import statement // import statement
pub struct Import { pub struct Import {
pub: pub:
mods map[string]string // alias -> module pos token.Position
mod string
alias string
// expr Expr // expr Expr
} }
@ -103,24 +108,27 @@ pub:
pub struct CallExpr { pub struct CallExpr {
pub: pub:
// func Expr // tok token.Token
pos token.Position
mut:
// func Expr
name string name string
args []Expr args []Expr
is_unknown bool
tok token.Token
} }
pub struct MethodCallExpr { pub struct MethodCallExpr {
pub: pub:
// tok token.Token
pos token.Position
expr Expr expr Expr
name string name string
args []Expr args []Expr
is_unknown bool
tok token.Token
} }
pub struct Return { pub struct Return {
pub: pub:
pos token.Position
expected_ti types.TypeIdent // TODO: remove once checker updated
exprs []Expr exprs []Expr
} }
@ -144,12 +152,30 @@ pub struct VarDecl {
pub: pub:
name string name string
expr Expr expr Expr
is_mut bool
mut:
ti types.TypeIdent ti types.TypeIdent
pos token.Position
} }
pub struct File { pub struct File {
pub: pub:
stmts []Stmt mod Module
imports []Import
stmts []Stmt
}
pub struct IdentVar {
pub:
expr Expr
ti types.TypeIdent
}
type IdentInfo = IdentVar
pub enum IdentKind {
blank_ident
variable
} }
// A single identifier // A single identifier
@ -157,14 +183,18 @@ pub struct Ident {
pub: pub:
name string name string
tok_kind token.Kind tok_kind token.Kind
pos token.Position
value string value string
mut:
kind IdentKind
info IdentInfo
} }
pub struct BinaryExpr { pub struct BinaryExpr {
pub: pub:
// tok_kind token.Kind
// op BinaryOp // op BinaryOp
op token.Kind op token.Kind
pos token.Position
left Expr left Expr
// left_ti types.TypeIdent // left_ti types.TypeIdent
right Expr right Expr
@ -231,6 +261,7 @@ pub:
pub struct ReturnStmt { pub struct ReturnStmt {
tok_kind token.Kind // or pos tok_kind token.Kind // or pos
pos token.Position
results []Expr results []Expr
} }
@ -246,9 +277,10 @@ pub:
pub struct AssignExpr { pub struct AssignExpr {
pub: pub:
op token.Kind
pos token.Position
left Expr left Expr
val Expr val Expr
op token.Kind
} }
pub struct ArrayInit { pub struct ArrayInit {

View File

@ -0,0 +1,45 @@
module builder
import (
os
time
v.table
v.checker
v.parser
v.gen
v.gen.x64
)
pub struct Builder {
pub:
table &table.Table
checker checker.Checker
}
pub fn new_builder() Builder {
table := table.new_table()
return Builder{
table: table
checker: checker.new_checker(table)
}
}
pub fn (b mut Builder) gen_c(v_files []string) string {
ast_files := parser.parse_files(v_files, b.table)
b.checker.check_files(v_files, ast_files)
return gen.cgen(ast_files, b.table)
}
pub fn (b mut Builder) build_c(v_files []string, out_file string) {
os.write_file(out_file, b.gen_c(v_files))
}
pub fn (b mut Builder) build_x64(v_files []string, out_file string) {
ticks := time.ticks()
ast_files := parser.parse_files(v_files, b.table)
println('PARSE: ${time.ticks() - ticks}ms')
b.checker.check_files(v_files, ast_files)
println('CHECK: ${time.ticks() - ticks}ms')
x64.gen(ast_files, out_file)
println('x64 GEN: ${time.ticks() - ticks}ms')
}

View File

@ -0,0 +1,258 @@
// 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 checker
import (
v.ast
v.table
v.types
v.token
)
pub struct Checker {
table &table.Table
mut:
file_name string
// TODO: resolved
}
pub fn new_checker(table &table.Table) Checker {
return Checker{
table: table
}
}
pub fn (c &Checker) check(ast_file ast.File) {
for stmt in ast_file.stmts {
c.stmt(stmt)
}
}
pub fn (c mut Checker) check_files(v_files []string, ast_files []ast.File) {
for i, file in ast_files {
c.file_name = v_files[i]
c.check(file)
}
}
pub fn (c &Checker) check_struct_init(struct_init ast.StructInit) {
typ := c.table.find_type(struct_init.ti.name) or {
c.error('unknown struct: $struct_init.ti.name', struct_init.pos)
panic('')
}
match typ.kind {
.placeholder {
c.error('unknown struct: $struct_init.ti.name', struct_init.pos)
}
.struct_ {
info := typ.info as types.Struct
for i, expr in struct_init.exprs {
field := info.fields[i]
expr_ti := c.table.get_expr_ti(expr)
if !c.table.check(expr_ti, field.ti) {
c.error('cannot assign $expr_ti.name as $field.ti.name for field $field.name', struct_init.pos)
}
}
}
else {}
}
}
pub fn (c &Checker) check_binary_expr(binary_expr ast.BinaryExpr) {
left_ti := c.table.get_expr_ti(binary_expr.left)
right_ti := c.table.get_expr_ti(binary_expr.right)
if !c.table.check(&right_ti, &left_ti) {
c.error('binary expr: cannot use $right_ti.name as $left_ti.name', binary_expr.pos)
}
}
fn (c &Checker) check_assign_expr(assign_expr ast.AssignExpr) {
left_ti := c.table.get_expr_ti(assign_expr.left)
right_ti := c.table.get_expr_ti(assign_expr.val)
if !c.table.check(right_ti, left_ti) {
c.error('cannot assign $right_ti.name to $left_ti.name', assign_expr.pos)
}
}
pub fn (c &Checker) check_call_expr(call_expr ast.CallExpr) {
fn_name := call_expr.name
if f := c.table.find_fn(fn_name) {
// return_ti := f.return_ti
if call_expr.args.len < f.args.len {
c.error('too few arguments in call to `$fn_name`', call_expr.pos)
} else if call_expr.args.len > f.args.len {
c.error('too many arguments in call to `$fn_name`', call_expr.pos)
}
for i, arg in f.args {
arg_expr := call_expr.args[i]
ti := c.table.get_expr_ti(arg_expr)
if !c.table.check(&ti, &arg.ti) {
c.error('cannot use type `$ti.name` as type `$arg.ti.name` in argument to `$fn_name`', call_expr.pos)
}
}
} else {
c.error('unknown fn: $fn_name', call_expr.pos)
// c.warn('unknown function `$fn_name`')
}
}
pub fn (c &Checker) check_method_call_expr(method_call_expr ast.MethodCallExpr) {
ti := c.table.get_expr_ti(method_call_expr.expr)
if !c.table.has_method(ti.idx, method_call_expr.name) {
c.error('type `$ti.name` has no method `$method_call_expr.name`', method_call_expr.pos)
}
}
pub fn (c &Checker) check_selector_expr(selector_expr ast.SelectorExpr) {
ti := c.table.get_expr_ti(selector_expr.expr)
field_name := selector_expr.field
// struct_ := c.table.types[ti.idx] as types.Struct
typ := c.table.types[ti.idx]
match typ.kind {
.struct_ {
// if !c.table.struct_has_field(it, field) {
// c.error('AAA unknown field `${it.name}.$field`')
// }
// TODO: fix bug
c.table.struct_find_field(typ, field_name) or {
c.error('unknown field `${typ.name}.$field_name`', selector_expr.pos)
}
}
else {
c.error('$ti.name is not a struct', selector_expr.pos)
}
}
}
// TODO: non deferred
pub fn (c &Checker) check_return_stmt(return_stmt ast.Return) {
mut got_tis := []types.TypeIdent
for expr in return_stmt.exprs {
ti := c.table.get_expr_ti(expr)
got_tis << ti
}
expected_ti := return_stmt.expected_ti
mut expected_tis := [expected_ti]
if expected_ti.kind == .multi_return {
mr_type := c.table.types[expected_ti.idx]
mr_info := mr_type.info as types.MultiReturn
expected_tis = mr_info.tis
}
if expected_tis.len != got_tis.len {
c.error('wrong number of return arguments:\n\texpected: $expected_tis.str()\n\tgot: $got_tis.str()', return_stmt.pos)
}
for i, exp_ti in expected_tis {
got_ti := got_tis[i]
if !c.table.check(got_ti, exp_ti) {
c.error('cannot use `$got_ti.name` as type `$exp_ti.name` in return argument', return_stmt.pos)
}
}
}
fn (c &Checker) stmt(node ast.Stmt) {
match node {
ast.FnDecl {
for stmt in it.stmts {
c.stmt(stmt)
}
}
ast.Return {
c.check_return_stmt(it)
}
ast.VarDecl {
c.expr(it.expr)
}
ast.ForStmt {
c.expr(it.cond)
for stmt in it.stmts {
c.stmt(stmt)
}
}
ast.ForCStmt {
c.stmt(it.init)
c.expr(it.cond)
c.stmt(it.inc)
for stmt in it.stmts {
c.stmt(stmt)
}
}
// ast.StructDecl {}
ast.ExprStmt {
c.expr(it.expr)
}
else {}
}
}
fn (c &Checker) expr(node ast.Expr) {
match node {
ast.AssignExpr {
c.check_assign_expr(it)
}
// ast.IntegerLiteral {}
// ast.FloatLiteral {}
ast.PostfixExpr {
c.expr(it.expr)
}
ast.UnaryExpr {
c.expr(it.left)
}
// ast.StringLiteral {}
ast.PrefixExpr {
c.expr(it.right)
}
ast.BinaryExpr {
c.check_binary_expr(it)
}
ast.StructInit {
c.check_struct_init(it)
}
ast.CallExpr {
c.check_call_expr(it)
}
ast.MethodCallExpr {
c.check_method_call_expr(it)
}
ast.ArrayInit {
for expr in it.exprs {
c.expr(expr)
}
}
// ast.Ident {}
// ast.BoolLiteral {}
ast.SelectorExpr {
c.check_selector_expr(it)
}
ast.IndexExpr {
c.expr(it.left)
c.expr(it.index)
}
ast.IfExpr {
c.expr(it.cond)
for i, stmt in it.stmts {
c.stmt(stmt)
}
if it.else_stmts.len > 0 {
for stmt in it.else_stmts {
c.stmt(stmt)
}
}
}
else {}
}
}
pub fn (c &Checker) error(s string, pos token.Position) {
print_backtrace()
final_msg_line := '$c.file_name:$pos.line_nr: error: $s'
eprintln(final_msg_line)
/*
if colored_output {
eprintln(term.bold(term.red(final_msg_line)))
}else{
eprintln(final_msg_line)
}
*/
exit(1)
}

View File

@ -55,15 +55,18 @@ fn (g mut Gen) stmt(node ast.Stmt) {
g.write('int ${it.name}(') g.write('int ${it.name}(')
} }
else { else {
g.write('$it.ti.name ${it.name}(') ti := g.table.refresh_ti(it.ti)
g.definitions.write('$it.ti.name ${it.name}(') g.write('$ti.name ${it.name}(')
g.definitions.write('$ti.name ${it.name}(')
} }
for i, arg in it.args { for i, arg in it.args {
g.write(arg.ti.name + ' ' + arg.name) // t := g.table.get_type(arg.ti.idx)
ti := g.table.refresh_ti(arg.ti)
g.write(ti.name + ' ' + arg.name)
if i < it.args.len - 1 { if i < it.args.len - 1 {
g.write(', ') g.write(', ')
} }
g.definitions.write(arg.ti.name + ' ' + arg.name) g.definitions.write(ti.name + ' ' + arg.name)
} }
g.writeln(') { ') g.writeln(') { ')
if !is_main { if !is_main {
@ -82,7 +85,9 @@ fn (g mut Gen) stmt(node ast.Stmt) {
g.write('return ') g.write('return ')
// multiple returns // multiple returns
if it.exprs.len > 1 { if it.exprs.len > 1 {
g.write('($g.fn_decl.ti.name){') // t := g.table.get_type(g.fn_decl.ti.idx)
ti := g.table.refresh_ti(g.fn_decl.ti)
g.write('($ti.name){')
for i, expr in it.exprs { for i, expr in it.exprs {
g.write('.arg$i=') g.write('.arg$i=')
g.expr(expr) g.expr(expr)
@ -99,7 +104,14 @@ fn (g mut Gen) stmt(node ast.Stmt) {
g.writeln(';') g.writeln(';')
} }
ast.VarDecl { ast.VarDecl {
g.write('$it.ti.name $it.name = ') mut ti := it.ti
if ti.kind == .unresolved {
ti = g.table.get_expr_ti(it.expr)
// println('A $it.ti.name')
// println('B $ti.name')
// panic("############# UNRESOLVED")
}
g.write('$ti.name $it.name = ')
g.expr(it.expr) g.expr(it.expr)
g.writeln(';') g.writeln(';')
} }
@ -128,7 +140,9 @@ fn (g mut Gen) stmt(node ast.Stmt) {
ast.StructDecl { ast.StructDecl {
g.writeln('typedef struct {') g.writeln('typedef struct {')
for field in it.fields { for field in it.fields {
g.writeln('\t$field.ti.name $field.name;') // t := g.table.get_type(field.ti.idx)
ti := g.table.refresh_ti(field.ti)
g.writeln('\t$ti.name $field.name;')
} }
g.writeln('} $it.name;') g.writeln('} $it.name;')
} }
@ -197,7 +211,9 @@ fn (g mut Gen) expr(node ast.Expr) {
} }
// `user := User{name: 'Bob'}` // `user := User{name: 'Bob'}`
ast.StructInit { ast.StructInit {
g.writeln('($it.ti.name){') // t := g.table.get_type(it.ti.idx)
ti := g.table.refresh_ti(it.ti)
g.writeln('($ti.name){')
for i, field in it.fields { for i, field in it.fields {
g.write('\t.$field = ') g.write('\t.$field = ')
g.expr(it.exprs[i]) g.expr(it.exprs[i])
@ -215,8 +231,11 @@ fn (g mut Gen) expr(node ast.Expr) {
} }
g.write(')') g.write(')')
} }
ast.MethodCallExpr {}
ast.ArrayInit { ast.ArrayInit {
g.writeln('new_array_from_c_array($it.exprs.len, $it.exprs.len, sizeof($it.ti.name), {\t') // t := g.table.get_type(it.ti.idx)
ti := g.table.refresh_ti(it.ti)
g.writeln('new_array_from_c_array($it.exprs.len, $it.exprs.len, sizeof($ti.name), {\t')
for expr in it.exprs { for expr in it.exprs {
g.expr(expr) g.expr(expr)
g.write(', ') g.write(', ')
@ -248,17 +267,18 @@ fn (g mut Gen) expr(node ast.Expr) {
ast.IfExpr { ast.IfExpr {
// If expression? Assign the value to a temp var. // If expression? Assign the value to a temp var.
// Previously ?: was used, but it's too unreliable. // Previously ?: was used, but it's too unreliable.
ti := g.table.refresh_ti(it.ti)
mut tmp := '' mut tmp := ''
if it.ti.kind != .void { if ti.kind != .void {
tmp = g.table.new_tmp_var() tmp = g.table.new_tmp_var()
// g.writeln('$it.ti.name $tmp;') // g.writeln('$ti.name $tmp;')
} }
g.write('if (') g.write('if (')
g.expr(it.cond) g.expr(it.cond)
g.writeln(') {') g.writeln(') {')
for i, stmt in it.stmts { for i, stmt in it.stmts {
// Assign ret value // Assign ret value
if i == it.stmts.len - 1 && it.ti.kind != .void { if i == it.stmts.len - 1 && ti.kind != .void {
// g.writeln('$tmp =') // g.writeln('$tmp =')
println(1) println(1)
} }

View File

@ -1,10 +1,7 @@
import ( import (
os os
filepath filepath
v.parser v.builder
v.ast
v.gen
v.table
term term
) )
@ -26,9 +23,8 @@ fn test_c_files() {
ctext := os.read_file('$vroot/vlib/v/gen/tests/${i}.c') or { ctext := os.read_file('$vroot/vlib/v/gen/tests/${i}.c') or {
panic(err) panic(err)
} }
table := &table.new_table() mut b := builder.new_builder()
program := parser.parse_file(path, table) res := b.gen_c([path])
res := gen.cgen([program], table)
if compare_texts(res, ctext) { if compare_texts(res, ctext) {
eprintln('${term_ok} ${i}') eprintln('${term_ok} ${i}')
} }

View File

@ -3,16 +3,19 @@ module gen
import ( import (
strings strings
v.ast v.ast
v.table
term term
) )
struct JsGen { struct JsGen {
out strings.Builder out strings.Builder
table &table.Table
} }
pub fn jsgen(program ast.File) string { pub fn jsgen(program ast.File, table &table.Table) string {
mut g := JsGen{ mut g := JsGen{
out: strings.new_builder(100) out: strings.new_builder(100)
table: table
} }
for stmt in program.stmts { for stmt in program.stmts {
g.stmt(stmt) g.stmt(stmt)
@ -34,9 +37,11 @@ pub fn (g mut JsGen) writeln(s string) {
fn (g mut JsGen) stmt(node ast.Stmt) { fn (g mut JsGen) stmt(node ast.Stmt) {
match node { match node {
ast.FnDecl { ast.FnDecl {
g.write('/** @return { $it.ti.name } **/\nfunction ${it.name}(') ti := g.table.refresh_ti(it.ti)
g.write('/** @return { $ti.name } **/\nfunction ${it.name}(')
for arg in it.args { for arg in it.args {
g.write(' /** @type { arg.ti.name } **/ $arg.name') arg_ti := g.table.refresh_ti(arg.ti)
g.write(' /** @type { $arg_ti.name } **/ $arg.name')
} }
g.writeln(') { ') g.writeln(') { ')
for stmt in it.stmts { for stmt in it.stmts {
@ -55,7 +60,8 @@ fn (g mut JsGen) stmt(node ast.Stmt) {
g.writeln(';') g.writeln(';')
} }
ast.VarDecl { ast.VarDecl {
g.write('var /* $it.ti.name */ $it.name = ') ti := g.table.refresh_ti(it.ti)
g.write('var /* $ti.name */ $it.name = ')
g.expr(it.expr) g.expr(it.expr)
g.writeln(';') g.writeln(';')
} }
@ -114,7 +120,8 @@ fn (g mut JsGen) expr(node ast.Expr) {
} }
// `user := User{name: 'Bob'}` // `user := User{name: 'Bob'}`
ast.StructInit { ast.StructInit {
g.writeln('/*$it.ti.name*/{') ti := g.table.refresh_ti(it.ti)
g.writeln('/*$ti.name*/{')
for i, field in it.fields { for i, field in it.fields {
g.write('\t$field : ') g.write('\t$field : ')
g.expr(it.exprs[i]) g.expr(it.exprs[i])

View File

@ -31,7 +31,7 @@ i < 10; i++;
1, 2, 3, 1, 2, 3,
}); });
int number = nums[0]; int number = nums[0];
void n = get_int2(); int n = get_int2();
} }
int get_int(string a) { int get_int(string a) {

View File

@ -149,10 +149,11 @@ pub fn (p mut Parser) parse_ti() types.TypeIdent {
else { else {
// struct / enum // struct / enum
mut idx := p.table.find_type_idx(name) mut idx := p.table.find_type_idx(name)
// add placeholder if idx > 0 {
if idx == 0 { return types.new_ti(p.table.types[idx].kind, name, idx, nr_muls)
idx = p.table.add_placeholder_type(name)
} }
// not found - add placeholder
idx = p.table.add_placeholder_type(name)
return types.new_ti(.placeholder, name, idx, nr_muls) return types.new_ti(.placeholder, name, idx, nr_muls)
} }
} }

View File

@ -13,50 +13,30 @@ pub fn (p mut Parser) call_expr() (ast.CallExpr,types.TypeIdent) {
tok := p.tok tok := p.tok
fn_name := p.check_name() fn_name := p.check_name()
p.check(.lpar) p.check(.lpar)
mut is_unknown := false
is_unknown = false
mut args := []ast.Expr mut args := []ast.Expr
mut return_ti := types.void_ti // mut return_ti := types.void_ti
if f := p.table.find_fn(fn_name) { for p.tok.kind != .rpar {
// println('found fn $fn_name') e,_ := p.expr(0)
return_ti = f.return_ti args << e
for i, arg in f.args { if p.tok.kind != .rpar {
e,ti := p.expr(0) p.check(.comma)
if !types.check(&arg.ti, &ti) {
p.error('cannot use type `$ti.name` as type `$arg.ti.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) p.check(.rpar)
node := ast.CallExpr{ node := ast.CallExpr{
name: fn_name name: fn_name
args: args args: args
is_unknown: is_unknown // tok: tok
tok: tok pos: tok.position()
// typ: return_ti
} }
if is_unknown { mut ti := types.unresolved_ti
p.table.unknown_calls << node if f := p.table.find_fn(fn_name) {
ti = f.return_ti
} }
return node,return_ti println('adding call_expr check $fn_name')
return node, ti
} }
pub fn (p mut Parser) call_args() []ast.Expr { pub fn (p mut Parser) call_args() []ast.Expr {
@ -170,6 +150,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl {
pub fn (p &Parser) check_fn_calls() { pub fn (p &Parser) check_fn_calls() {
println('check fn calls2') 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)
@ -179,4 +160,5 @@ pub fn (p &Parser) check_fn_calls() {
// println(f.return_ti.name) // println(f.return_ti.name)
// println('IN AST typ=' + call.typ.name) // println('IN AST typ=' + call.typ.name)
} }
*/
} }

View File

@ -0,0 +1,4 @@
// 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

View File

@ -60,6 +60,22 @@ pub fn parse_file(path string, table &table.Table) ast.File {
file_name: path file_name: path
} }
p.read_first_token() p.read_first_token()
// module decl
module_decl := if p.tok.kind == .key_module {
p.module_decl()
} else {
ast.Module{
name: 'main'
}
}
// imports
mut imports := []ast.Import
for p.tok.kind == .key_import {
imports << p.import_stmt()
}
// TODO: import only mode
for { for {
// res := s.scan() // res := s.scan()
if p.tok.kind == .eof { if p.tok.kind == .eof {
@ -69,11 +85,12 @@ pub fn parse_file(path string, table &table.Table) ast.File {
// println('stmt at ' + p.tok.str()) // println('stmt at ' + p.tok.str())
stmts << p.top_stmt() stmts << p.top_stmt()
} }
p.check_fn_calls()
// println('nr stmts = $stmts.len') // println('nr stmts = $stmts.len')
// println(stmts[0]) // println(stmts[0])
return ast.File{ return ast.File{
stmts: stmts mod: module_decl
imports: imports
stmts: stmts
} }
} }
@ -136,14 +153,11 @@ fn (p mut Parser) check_name() string {
pub fn (p mut Parser) top_stmt() ast.Stmt { pub fn (p mut Parser) top_stmt() ast.Stmt {
match p.tok.kind { match p.tok.kind {
.key_module {
return p.module_decl()
}
.key_import {
return p.import_stmt()
}
.key_pub { .key_pub {
match p.peek_tok.kind { match p.peek_tok.kind {
.key_const {
return p.const_decl()
}
.key_fn { .key_fn {
return p.fn_decl() return p.fn_decl()
} }
@ -154,16 +168,16 @@ pub fn (p mut Parser) top_stmt() ast.Stmt {
p.error('wrong pub keyword usage') p.error('wrong pub keyword usage')
return ast.Stmt{} return ast.Stmt{}
} }
}
// .key_const {
// return p.const_decl()
// }
// .key_enum { // .key_enum {
// return p.enum_decl() // return p.enum_decl()
// } // }
// .key_type { // .key_type {
// return p.type_decl() // return p.type_decl()
// } // }
}
}
.key_const {
return p.const_decl()
} }
.key_fn { .key_fn {
return p.fn_decl() return p.fn_decl()
@ -213,11 +227,12 @@ pub fn (p mut Parser) stmt() ast.Stmt {
pub fn (p mut Parser) assign_expr(left ast.Expr) ast.AssignExpr { pub fn (p mut Parser) assign_expr(left ast.Expr) ast.AssignExpr {
op := p.tok.kind op := p.tok.kind
p.next() p.next()
val,_ := p.expr(0) val, _ := p.expr(0)
node := ast.AssignExpr{ node := ast.AssignExpr{
left: left left: left
op: op
val: val val: val
op: op
pos: p.tok.position()
} }
return node return node
} }
@ -238,7 +253,7 @@ pub fn (p mut Parser) assign_stmt() ast.AssignStmt {
// println('assignn_stmt() ' + op.str()) // println('assignn_stmt() ' + op.str())
p.next() p.next()
right_expr,right_type := p.expr(0) right_expr,right_type := p.expr(0)
if !types.check(left_type, right_type) { if !p.table.check(left_type, right_type) {
p.error('oops') p.error('oops')
} }
return ast.AssignStmt{ return ast.AssignStmt{
@ -282,7 +297,8 @@ pub fn (p &Parser) warn(s string) {
pub fn (p mut Parser) name_expr() (ast.Expr,types.TypeIdent) { pub fn (p mut Parser) name_expr() (ast.Expr,types.TypeIdent) {
mut node := ast.Expr{} mut node := ast.Expr{}
mut ti := types.void_ti // mut ti := types.void_ti
mut ti := types.unresolved_ti
if p.tok.lit == 'C' { if p.tok.lit == 'C' {
p.next() p.next()
p.check(.dot) p.check(.dot)
@ -293,7 +309,8 @@ pub fn (p mut Parser) name_expr() (ast.Expr,types.TypeIdent) {
} }
// fn call // fn call
if p.peek_tok.kind == .lpar { if p.peek_tok.kind == .lpar {
x,ti2 := p.call_expr() // TODO `node,typ :=` should work println('calling $p.tok.lit')
x, ti2 := p.call_expr() // TODO `node,typ :=` should work
node = x node = x
ti = ti2 ti = ti2
} }
@ -308,22 +325,21 @@ pub fn (p mut Parser) name_expr() (ast.Expr,types.TypeIdent) {
field_name := p.check_name() field_name := p.check_name()
field_names << field_name field_names << field_name
p.check(.colon) p.check(.colon)
// expr,field_type := p.expr(0)
expr,_ := p.expr(0) expr,_ := p.expr(0)
// if !types.check( ,field_type
exprs << expr exprs << expr
} }
node = ast.StructInit{ node = ast.StructInit{
ti: ti ti: ti
exprs: exprs exprs: exprs
fields: field_names fields: field_names
pos: p.tok.position()
} }
p.check(.rcbr) p.check(.rcbr)
} }
else { else {
// p.warn('name ') // p.warn('name ')
// left := p.parse_ident() // left := p.parse_ident()
node = ast.Ident{ mut ident := ast.Ident{
name: p.tok.lit name: p.tok.lit
} }
var := p.table.find_var(p.tok.lit) or { var := p.table.find_var(p.tok.lit) or {
@ -331,6 +347,13 @@ pub fn (p mut Parser) name_expr() (ast.Expr,types.TypeIdent) {
exit(0) exit(0)
} }
ti = var.ti ti = var.ti
ident.kind = .variable
ident.info = ast.IdentVar {
ti: ti
expr: var.expr
}
// ident.ti = ti
node = ident
p.next() p.next()
} }
return node,ti return node,ti
@ -438,59 +461,33 @@ fn (p mut Parser) index_expr(left ast.Expr) (ast.Expr,types.TypeIdent) {
return node,ti return node,ti
} }
fn (p mut Parser) dot_expr(left ast.Expr, ti types.TypeIdent) (ast.Expr,types.TypeIdent) { fn (p mut Parser) dot_expr(left ast.Expr, left_ti &types.TypeIdent) (ast.Expr,types.TypeIdent) {
p.next() p.next()
field_name := p.check_name() field_name := p.check_name()
println('# $ti.name $ti.idx - $field_name') ti := types.unresolved_ti
if ti.kind != .void {
p.warn('#### void type in dot_expr - field: $field_name')
}
struc := p.table.types[ti.idx] as types.Struct
// Method call // Method call
if p.tok.kind == .lpar { if p.tok.kind == .lpar {
if !p.table.struct_has_method(struc, field_name) {
p.error('type `$struc.name` has no method `$field_name`')
}
p.next() p.next()
args := p.call_args() args := p.call_args()
println('method call $field_name') mcall_expr := ast.MethodCallExpr{
mut node := ast.Expr{}
node = ast.MethodCallExpr{
expr: left expr: left
name: field_name name: field_name
args: args args: args
pos: p.tok.position()
} }
return node,types.int_ti mut node := ast.Expr{}
node = mcall_expr
return node, ti
} }
if !p.table.struct_has_field(struc, field_name) {
// t :=
p.error('type `$struc.name` has no field `$field_name`')
}
/*
// p.next()
field := p.check_name()
if !ti.type_kind in [.placeholder, .struct_] {
println('kind: $ti.str()')
p.error('cannot access field, `$ti.type_name` is not a struct')
}
typ := p.table.types[ti.type_idx] as types.Struct
mut ok := false
for f in typ.fields {
if f.name == field {
ok = true
}
}
if !ok {
p.error('unknown field `${typ.name}.$field`')
}
*/
mut node := ast.Expr{} sel_expr := ast.SelectorExpr{
node = ast.SelectorExpr{
expr: left expr: left
field: field_name field: field_name
pos: p.tok.position()
} }
return node,types.int_ti mut node := ast.Expr{}
node = sel_expr
return node, ti
} }
fn (p mut Parser) infix_expr(left ast.Expr) (ast.Expr,types.TypeIdent) { fn (p mut Parser) infix_expr(left ast.Expr) (ast.Expr,types.TypeIdent) {
@ -505,9 +502,10 @@ fn (p mut Parser) infix_expr(left ast.Expr) (ast.Expr,types.TypeIdent) {
} }
mut expr := ast.Expr{} mut expr := ast.Expr{}
expr = ast.BinaryExpr{ expr = ast.BinaryExpr{
op: op
left: left left: left
right: right right: right
op: op
pos: p.tok.position()
} }
return expr,ti return expr,ti
} }
@ -587,7 +585,7 @@ fn (p mut Parser) for_statement() ast.Stmt {
} }
// `for cond {` // `for cond {`
cond,ti := p.expr(0) cond,ti := p.expr(0)
if !types.check(types.bool_ti, ti) { if !p.table.check(types.bool_ti, ti) {
p.error('non-bool used as for condition') p.error('non-bool used as for condition')
} }
stmts := p.parse_block() stmts := p.parse_block()
@ -604,7 +602,7 @@ fn (p mut Parser) if_expr() (ast.Expr,types.TypeIdent) {
mut node := ast.Expr{} mut node := ast.Expr{}
p.check(.key_if) p.check(.key_if)
cond,cond_ti := p.expr(0) cond,cond_ti := p.expr(0)
// if !types.check(types.bool_ti, cond_ti) { // if !p.table.check(types.bool_ti, cond_ti) {
if cond_ti.kind != .bool { if cond_ti.kind != .bool {
p.error('non-bool used as if condition') p.error('non-bool used as if condition')
} }
@ -670,7 +668,7 @@ fn (p mut Parser) array_init() (ast.Expr,types.TypeIdent) {
if i == 0 { if i == 0 {
val_ti = ti val_ti = ti
} }
else if !types.check(val_ti, ti) { else if !p.table.check(val_ti, ti) {
p.error('expected array element with type `$val_ti.name`') p.error('expected array element with type `$val_ti.name`')
} }
exprs << expr exprs << expr
@ -714,25 +712,57 @@ fn (p mut Parser) parse_number_literal() (ast.Expr,types.TypeIdent) {
fn (p mut Parser) module_decl() ast.Module { fn (p mut Parser) module_decl() ast.Module {
p.check(.key_module) p.check(.key_module)
p.next() name := p.check_name()
return ast.Module{} return ast.Module{
name: name
}
} }
fn (p mut Parser) import_stmt() ast.Import { fn (p mut Parser) parse_import() ast.Import {
p.check(.key_import) mod_name := p.check_name()
name := p.check_name() mut mod_alias := mod_name
mut alias := name
if p.tok.kind == .key_as { if p.tok.kind == .key_as {
p.check(.key_as) p.check(.key_as)
alias = p.check_name() mod_alias = p.check_name()
} }
mut mods := map[string]string
mods[alias] = name
return ast.Import{ return ast.Import{
mods: mods mod: mod_name
alias: mod_alias
pos: p.tok.position()
} }
} }
fn (p mut Parser) import_stmt() []ast.Import {
p.check(.key_import)
mut imports := []ast.Import
if p.tok.kind == .lpar {
p.check(.lpar)
for p.tok.kind != .rpar {
imports << p.parse_import()
}
p.check(.rpar)
} else {
imports << p.parse_import()
}
return imports
}
// TODO
//fn (p mut Parser) const_decl() ast... {
fn (p mut Parser) const_decl() ast.Stmt {
p.check(.key_const)
p.check(.lpar)
for p.tok.kind != .rpar {
name := p.check_name()
println('const: $name')
p.check(.assign)
_, _ := p.expr(0)
// expr, ti := p.expr(0)
}
p.check(.rpar)
return ast.Stmt{}
}
fn (p mut Parser) struct_decl() ast.StructDecl { fn (p mut Parser) struct_decl() ast.StructDecl {
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
if is_pub { if is_pub {
@ -756,18 +786,23 @@ fn (p mut Parser) struct_decl() ast.StructDecl {
} }
fields << types.Field{ fields << types.Field{
name: field_name name: field_name
type_idx: ti.idx // type_idx: ti.idx
ti: ti
} }
} }
p.check(.rcbr) p.check(.rcbr)
p.table.register_struct(types.Struct{ p.table.register_type(table.Type{
kind: .struct_
name: name name: name
fields: fields info: types.Struct{
fields: fields
}
}) })
return ast.StructDecl{ return ast.StructDecl{
name: name name: name
is_pub: is_pub is_pub: is_pub
fields: ast_fields fields: ast_fields
pos: p.tok.position()
} }
} }
@ -776,11 +811,12 @@ fn (p mut Parser) return_stmt() ast.Return {
// return expressions // return expressions
mut exprs := []ast.Expr mut exprs := []ast.Expr
// return type idents // return type idents
mut got_tis := []types.TypeIdent // mut got_tis := []types.TypeIdent
for { for {
expr,ti := p.expr(0) // expr,ti := p.expr(0)
expr, _ := p.expr(0)
exprs << expr exprs << expr
got_tis << ti // got_tis << ti
if p.tok.kind == .comma { if p.tok.kind == .comma {
p.check(.comma) p.check(.comma)
} }
@ -788,23 +824,13 @@ fn (p mut Parser) return_stmt() ast.Return {
break break
} }
} }
mut expected_tis := [p.return_ti] // TODO: consider non deferred
if p.return_ti.kind == .multi_return { stmt := ast.Return{
mr_type := p.table.types[p.return_ti.idx] as types.MultiReturn expected_ti: p.return_ti
expected_tis = mr_type.tis
}
if expected_tis.len != got_tis.len {
p.error('wrong number of return arguments:\n\texpected: $expected_tis.str()\n\tgot: $got_tis.str()')
}
for i, exp_ti in expected_tis {
got_ti := got_tis[i]
if !types.check(got_ti, exp_ti) {
p.error('cannot use `$got_ti.name` as type `$exp_ti.name` in return argument')
}
}
return ast.Return{
exprs: exprs exprs: exprs
pos: p.tok.position()
} }
return stmt
} }
fn (p mut Parser) var_decl() ast.VarDecl { fn (p mut Parser) var_decl() ast.VarDecl {
@ -820,25 +846,29 @@ fn (p mut Parser) var_decl() ast.VarDecl {
} }
name := p.tok.lit name := p.tok.lit
p.read_first_token() p.read_first_token()
expr,ti := p.expr(token.lowest_prec) expr, ti := p.expr(token.lowest_prec)
if _ := p.table.find_var(name) { if _ := p.table.find_var(name) {
p.error('redefinition of `$name`') p.error('redefinition of `$name`')
} }
p.table.register_var(table.Var{ p.table.register_var(table.Var{
name: name name: name
ti: ti
is_mut: is_mut is_mut: is_mut
expr: expr
ti: ti
}) })
// println(p.table.names) // println(p.table.names)
// println('added var `$name` with type $t.name') node := ast.VarDecl{
return ast.VarDecl{
name: name name: name
expr: expr // p.expr(token.lowest_prec) expr: expr // p.expr(token.lowest_prec)
is_mut: is_mut
ti: ti ti: ti
pos: p.tok.position()
} }
return node
} }
fn verror(s string) { fn verror(s string) {
println(s) println(s)
exit(1) exit(1)

View File

@ -4,6 +4,7 @@ import (
v.ast v.ast
v.gen v.gen
v.table v.table
v.checker
term term
) )
@ -37,7 +38,7 @@ fn test_one() {
// //
] ]
expected := 'int a = 10;int b = -a;int c = 20;' expected := 'int a = 10;int b = -a;int c = 20;'
table := &table.Table{} table := table.new_table()
mut e := []ast.Stmt mut e := []ast.Stmt
for line in input { for line in input {
e << parse_stmt(line, table) e << parse_stmt(line, table)
@ -118,7 +119,8 @@ fn test_parse_expr() {
'-a;', '-a;',
] ]
mut e := []ast.Stmt mut e := []ast.Stmt
table := &table.Table{} table := table.new_table()
mut checker := checker.new_checker(table)
for s in input { for s in input {
// println('\n\nst="$s"') // println('\n\nst="$s"')
e << parse_stmt(s, table) e << parse_stmt(s, table)
@ -126,6 +128,7 @@ fn test_parse_expr() {
program := ast.File{ program := ast.File{
stmts: e stmts: e
} }
checker.check(program)
res := gen.cgen([program], table) res := gen.cgen([program], table)
println('========') println('========')
println(res) println(res)

View File

@ -1,30 +1,21 @@
module table module table
import ( import (
v.types
v.ast v.ast
v.types
) )
pub struct Table { pub struct Table {
// struct_fields map[string][]string // struct_fields map[string][]string
pub mut: pub mut:
types []types.Type types []Type
// type_idxs Hashmap // type_idxs Hashmap
type_idxs map[string]int type_idxs map[string]int
local_vars []Var local_vars []Var
// fns Hashmap // fns Hashmap
fns map[string]Fn fns map[string]Fn
methods [][]Fn
//
unknown_calls []ast.CallExpr
tmp_cnt int tmp_cnt int
} imports []string
pub struct Var {
pub:
name string
ti types.TypeIdent
is_mut bool
} }
pub struct Fn { pub struct Fn {
@ -34,6 +25,29 @@ pub:
return_ti types.TypeIdent return_ti types.TypeIdent
} }
pub struct Var {
pub:
name string
is_mut bool
expr ast.Expr
mut:
ti types.TypeIdent
}
pub struct Type {
pub:
parent_idx int
mut:
info types.TypeInfo
kind types.Kind
name string
methods []Fn
}
pub fn (t Type) str() string {
return t.name
}
pub fn new_table() &Table { pub fn new_table() &Table {
mut t := &Table{} mut t := &Table{}
t.register_builtin_types() t.register_builtin_types()
@ -41,55 +55,56 @@ pub fn new_table() &Table {
} }
pub fn (t mut Table) register_builtin_types() { pub fn (t mut Table) register_builtin_types() {
// add dummy type at 0 so nothing can go there // reserve index 0 so nothing can go there
// save index check, 0 will mean not found // save index check, 0 will mean not found
t.register_type(types.Type{}, 'dymmy_type_at_idx_0', 0) t.register_type(Type{kind: .placeholder, name: 'reserved_0'})
t.register_type(types.Primitive{ t.register_type(Type{kind: .void, name: 'void'})
idx: types.void_type_idx t.register_type(Type{kind: .voidptr, name: 'voidptr'})
kind: .void t.register_type(Type{kind: .charptr, name: 'charptr'})
}, 'void', types.void_type_idx) t.register_type(Type{kind: .byteptr, name: 'byteptr'})
t.register_type(types.Primitive{ t.register_type(Type{kind: .i8, name: 'i8'})
idx: types.voidptr_type_idx t.register_type(Type{kind: .i16, name: 'i16'})
kind: .voidptr t.register_type(Type{kind: .int, name: 'int'})
}, 'voidptr', types.voidptr_type_idx) t.register_type(Type{kind: .i64, name: 'i64'})
t.register_type(types.Primitive{ t.register_type(Type{kind: .u16, name: 'u16'})
idx: types.charptr_type_idx t.register_type(Type{kind: .u32, name: 'u32'})
kind: .charptr t.register_type(Type{kind: .u64, name: 'u64'})
}, 'charptr', types.charptr_type_idx) t.register_type(Type{kind: .f32, name: 'f32'})
t.register_type(types.Primitive{ t.register_type(Type{kind: .f64, name: 'f64'})
idx: types.byteptr_type_idx t.register_type(Type{kind: .string, name: 'string'})
kind: .byteptr t.register_type(Type{kind: .char, name: 'char'})
}, 'byteptr', types.byteptr_type_idx) t.register_type(Type{kind: .byte, name: 'byte'})
t.register_type(types.Int{ t.register_type(Type{kind: .bool, name: 'bool'})
types.i8_type_idx,8,false}, 'i8', types.i8_type_idx) }
t.register_type(types.Int{
types.i16_type_idx,16,false}, 'i16', types.i16_type_idx) pub fn (t &Table) refresh_ti(ti types.TypeIdent) types.TypeIdent {
t.register_type(types.Int{ if ti.idx == 0 {
types.i64_type_idx,32,false}, 'int', types.int_type_idx) return ti
t.register_type(types.Int{ }
types.i64_type_idx,64,false}, 'i64', types.i64_type_idx) if ti.kind in [.placeholder, .unresolved] {
t.register_type(types.Int{ typ := t.types[ti.idx]
types.u16_type_idx,16,true}, 'u16', types.u16_type_idx) return { ti|
t.register_type(types.Int{ kind: typ.kind,
types.u32_type_idx,32,true}, 'u32', types.u32_type_idx) name: typ.name
t.register_type(types.Int{ }
types.u64_type_idx,64,true}, 'u64', types.u64_type_idx) }
t.register_type(types.Float{ return ti
types.f64_type_idx,32}, 'f32', types.f32_type_idx) }
t.register_type(types.Float{
types.f64_type_idx,64}, 'f64', types.f64_type_idx) pub fn (t &Table) get_type(idx int) Type {
t.register_type(types.String{ if idx == 0 {
types.string_type_idx}, 'string', types.string_type_idx) panic('get_type: idx 0')
t.register_type(types.Primitive{ }
idx: types.char_type_idx return t.types[idx]
kind: .char }
}, 'char', types.char_type_idx)
t.register_type(types.Primitive{ pub fn (t &Table) find_var_idx(name string) int {
idx: types.byte_type_idx for i,var in t.local_vars {
kind: .byte if var.name == name {
}, 'byte', types.byte_type_idx) return i
t.register_type(types.Bool{ }
types.bool_type_idx}, 'bool', types.bool_type_idx) }
return -1
} }
pub fn (t &Table) find_var(name string) ?Var { pub fn (t &Table) find_var(name string) ?Var {
@ -122,6 +137,7 @@ pub fn (t mut Table) clear_vars() {
} }
pub fn (t mut Table) register_var(v Var) { pub fn (t mut Table) register_var(v Var) {
println('register_var: $v.name - $v.ti.name')
t.local_vars << v t.local_vars << v
/* /*
mut new_var := { mut new_var := {
@ -159,68 +175,66 @@ pub fn (t mut Table) register_fn(new_fn Fn) {
t.fns[new_fn.name] = new_fn t.fns[new_fn.name] = new_fn
} }
pub fn (t mut Table) register_method(ti types.TypeIdent, new_fn Fn) bool { pub fn (t mut Table) register_method(ti &types.TypeIdent, new_fn Fn) bool {
println('register method `$new_fn.name` tiname=$ti.name ') idx := ti.idx
/* println('register method `$new_fn.name` type=$ti.name idx=$ti.idx')
match t.types[ti.idx] { mut methods := t.types[idx].methods
types.Struct { methods << new_fn
println('got struct') t.types[idx].methods = methods
}
else {
return false
}
}
mut struc := t.types[ti.idx] as types.Struct
if struc.methods.len == 0 {
struc.methods = make(0, 0, sizeof(types.Field))
}
println('register method `$new_fn.name` struct=$struc.name ')
struc.methods << types.Field{
name: new_fn.name
}
t.types[ti.idx] = struc
*/
println('register method `$new_fn.name` struct=$ti.name ')
println('##### $ti.idx - $t.methods.len')
t.methods[ti.idx] << new_fn
return true return true
} }
pub fn (t &Table) has_method(type_idx int, name string) bool {
t.find_method(type_idx, name) or {
return false
}
return true
}
pub fn (t &Table) find_method(type_idx int, name string) ?Fn {
for method in t.types[type_idx].methods {
if method.name == name {
return method
}
}
return none
}
pub fn (t mut Table) new_tmp_var() string { pub fn (t mut Table) new_tmp_var() string {
t.tmp_cnt++ t.tmp_cnt++
return 'tmp$t.tmp_cnt' return 'tmp$t.tmp_cnt'
} }
pub fn (t &Table) struct_has_field(s &types.Struct, name string) bool { pub fn (t &Table) struct_has_field(s &Type, name string) bool {
println('struct_has_field($s.name, $name) s.idx=$s.idx types.len=$t.types.len s.parent_idx=$s.parent_idx') println('struct_has_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
// for typ in t.types { // for typ in t.types {
// println('$typ.idx $typ.name') // println('$typ.idx $typ.name')
// } // }
for field in s.fields { if _ := t.struct_find_field(s, name) {
if field.name == name { return true
return true
}
}
if s.parent_idx != 0 {
parent := t.types[s.parent_idx] as types.Struct
println('got parent $parent.name')
for field in parent.fields {
if field.name == name {
return true
}
}
} }
return false return false
} }
pub fn (t &Table) struct_has_method(s &types.Struct, name string) bool { pub fn (t &Table) struct_find_field(s &Type, name string) ?types.Field {
for field in t.methods[s.idx] { println('struct_find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
info := s.info as types.Struct
for field in info.fields {
if field.name == name { if field.name == name {
return true return field
} }
} }
return false if s.parent_idx != 0 {
parent := t.types[s.parent_idx]
parent_info := s.info as types.Struct
println('got parent $parent.name')
for field in parent_info.fields {
if field.name == name {
return field
}
}
}
return none
} }
[inline] [inline]
@ -229,7 +243,7 @@ pub fn (t &Table) find_type_idx(name string) int {
} }
[inline] [inline]
pub fn (t &Table) find_type(name string) ?types.Type { pub fn (t &Table) find_type(name string) ?Type {
idx := t.type_idxs[name] idx := t.type_idxs[name]
if idx > 0 { if idx > 0 {
return t.types[idx] return t.types[idx]
@ -238,52 +252,30 @@ pub fn (t &Table) find_type(name string) ?types.Type {
} }
[inline] [inline]
pub fn (t mut Table) register_type(typ types.Type, name string, idx int) { pub fn (t mut Table) register_type(typ Type) int {
// sanity check
if idx != t.types.len {
panic('error registering type $name, type.idx must = table.types.len (got `$idx` expected `$t.types.len`')
}
t.type_idxs[name] = idx
t.types << typ
e := []Fn
t.methods << e // TODO [] breaks V
}
pub fn (t mut Table) register_struct(typ types.Struct) int {
println('register_struct($typ.name)')
// existing
existing_idx := t.type_idxs[typ.name] existing_idx := t.type_idxs[typ.name]
if existing_idx > 0 { if existing_idx > 0 {
ex_type := t.types[existing_idx] ex_type := t.types[existing_idx]
match ex_type { match ex_type.kind {
types.Placeholder { .placeholder {
// override placeholder // override placeholder
println('overriding type placeholder `$it.name` with struct') println('overriding type placeholder `$typ.name`')
mut struct_type := types.Type{} t.types[existing_idx] = {typ|
struct_type = { methods: ex_type.methods
typ |
idx:existing_idx
} }
t.types[existing_idx] = struct_type
return existing_idx
}
types.Struct {
return existing_idx return existing_idx
} }
else { else {
if ex_type.kind == typ.kind {
return existing_idx
}
panic('cannot register type `$typ.name`, another type with this name exists') panic('cannot register type `$typ.name`, another type with this name exists')
} }
}
} }
}
// register
println('registering: $typ.name')
idx := t.types.len idx := t.types.len
struct_type := { t.types << typ
typ | t.type_idxs[typ.name] = idx
idx:idx,
parent_idx:0,
}
t.register_type(struct_type, typ.name, idx)
return idx return idx
} }
@ -295,13 +287,15 @@ pub fn (t mut Table) find_or_register_map(key_ti &types.TypeIdent, value_ti &typ
return existing_idx,name return existing_idx,name
} }
// register // register
idx := t.types.len map_type := Type{
map_type := types.Map{ kind: .map
name: name name: name
key_type_idx: key_ti.idx info: types.Map{
value_type_idx: value_ti.idx key_type_idx: key_ti.idx
value_type_idx: value_ti.idx
}
} }
t.register_type(map_type, name, idx) idx := t.register_type(map_type)
return idx,name return idx,name
} }
@ -314,16 +308,17 @@ pub fn (t mut Table) find_or_register_array(elem_ti &types.TypeIdent, nr_dims in
} }
// register // register
parent_idx := t.type_idxs['array'] parent_idx := t.type_idxs['array']
idx := t.types.len array_type := Type{
array_type := types.Array{
idx: idx
parent_idx: parent_idx parent_idx: parent_idx
kind: .array
name: name name: name
elem_type_idx: elem_ti.idx info: types.Array{
elem_is_ptr: elem_ti.is_ptr() elem_type_idx: elem_ti.idx
nr_dims: nr_dims elem_is_ptr: elem_ti.is_ptr()
nr_dims: nr_dims
}
} }
t.register_type(array_type, name, idx) idx := t.register_type(array_type)
return idx,name return idx,name
} }
@ -335,16 +330,17 @@ pub fn (t mut Table) find_or_register_array_fixed(elem_ti &types.TypeIdent, size
return existing_idx,name return existing_idx,name
} }
// register // register
idx := t.types.len array_fixed_type := Type{
array_fixed_type := types.ArrayFixed{ kind: .array_fixed
idx: idx
name: name name: name
elem_type_idx: elem_ti.idx info: types.ArrayFixed{
elem_is_ptr: elem_ti.is_ptr() elem_type_idx: elem_ti.idx
size: size elem_is_ptr: elem_ti.is_ptr()
nr_dims: nr_dims size: size
nr_dims: nr_dims
}
} }
t.register_type(array_fixed_type, name, idx) idx := t.register_type(array_fixed_type)
return idx,name return idx,name
} }
@ -359,13 +355,14 @@ pub fn (t mut Table) find_or_register_multi_return(mr_tis []types.TypeIdent) (in
return existing_idx,name return existing_idx,name
} }
// register // register
idx := t.types.len mr_type := Type{
mr_type := types.MultiReturn{ kind: .multi_return
idx: idx
name: name name: name
tis: mr_tis info: types.MultiReturn{
tis: mr_tis
}
} }
t.register_type(mr_type, name, idx) idx := t.register_type(mr_type)
return idx,name return idx,name
} }
@ -377,22 +374,121 @@ pub fn (t mut Table) find_or_register_variadic(variadic_ti &types.TypeIdent) (in
return existing_idx,name return existing_idx,name
} }
// register // register
idx := t.types.len variadic_type := Type{
variadic_type := types.Variadic{ kind: .variadic
idx: idx name: name
ti: variadic_ti info: types.Variadic{
ti: variadic_ti
}
} }
t.register_type(variadic_type, name, idx) idx := t.register_type(variadic_type)
return idx,name return idx,name
} }
pub fn (t mut Table) add_placeholder_type(name string) int { pub fn (t mut Table) add_placeholder_type(name string) int {
idx := t.types.len ph_type := Type{
ph_type := types.Placeholder{ kind: .placeholder
idx: idx
name: name name: name
} }
println('added placeholder: $name - $idx ') idx := t.register_type(ph_type)
t.register_type(ph_type, name, idx) println('added placeholder: $name - $idx')
return idx return idx
} }
// [inline]
// pub fn (t &Table) update_ti(ti &types.TypeIdent) types.TypeIdent {
// if ti.kind == .unresolved {
// }
// }
pub fn (t &Table) check(got, expected &types.TypeIdent) bool {
println('check: $got.name, $expected.name')
if expected.kind == .voidptr {
return true
}
//if expected.name == 'array' {
// return true
//}
if got.idx != expected.idx {
return false
}
return true
}
[inline]
pub fn (t &Table) get_expr_ti(expr ast.Expr) types.TypeIdent {
match expr {
ast.ArrayInit{
return it.ti
}
ast.IndexExpr{
return t.get_expr_ti(it.left)
}
ast.CallExpr {
func := t.find_fn(it.name) or {
return types.void_ti
}
return func.return_ti
}
ast.MethodCallExpr {
ti := t.get_expr_ti(it.expr)
func := t.find_method(ti.idx, it.name) or {
return types.void_ti
}
return func.return_ti
}
ast.Ident {
if it.kind == .variable {
info := it.info as ast.IdentVar
if info.ti.kind != .unresolved {
return info.ti
}
return t.get_expr_ti(info.expr)
}
return types.void_ti
}
ast.StructInit {
return it.ti
}
ast.StringLiteral {
return types.string_ti
}
ast.IntegerLiteral {
return types.int_ti
}
ast.SelectorExpr {
ti := t.get_expr_ti(it.expr)
kind := t.types[ti.idx].kind
if ti.kind == .placeholder {
println(' ##### PH $ti.name')
}
if !(kind in [.placeholder, .struct_]) {
return types.void_ti
}
struct_ := t.types[ti.idx]
struct_info := struct_.info as types.Struct
for field in struct_info.fields {
if field.name == it.field {
return field.ti
}
}
if struct_.parent_idx != 0 {
parent := t.types[struct_.parent_idx]
parent_info := parent.info as types.Struct
for field in parent_info.fields {
if field.name == it.field {
return field.ti
}
}
}
return types.void_ti
}
ast.BinaryExpr {
return t.get_expr_ti(it.left)
}
else {
return types.void_ti
}
}
}

View File

@ -0,0 +1,18 @@
// 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 token
pub struct Position {
pub:
line_nr int // the line number in the source where the token occured
// pos int // the position of the token in scanner text
}
[inline]
pub fn (tok &Token) position() Position {
return Position{
line_nr: tok.line_nr
// pos: tok.pos
}
}

View File

@ -29,14 +29,10 @@ pub enum Kind {
voidptr voidptr
charptr charptr
byteptr byteptr
const_
enum_
struct_
int
i8 i8
i16 i16
int
i64 i64
byte
u16 u16
u32 u32
u64 u64
@ -44,20 +40,25 @@ pub enum Kind {
f64 f64
string string
char char
byte
bool bool
const_
enum_
struct_
array array
array_fixed array_fixed
map map
multi_return multi_return
variadic variadic
unresolved
} }
pub type Type = Placeholder | Primitive | Const | Enum | Struct | Int | Float | pub type TypeInfo = Array | ArrayFixed | Map | Struct | MultiReturn | Variadic
String | Bool | Array | ArrayFixed | Map | MultiReturn | Variadic
pub struct TypeIdent { pub struct TypeIdent {
pub: pub:
idx int idx int
mut:
kind Kind kind Kind
name string name string
nr_muls int nr_muls int
@ -73,17 +74,18 @@ pub fn new_ti(kind Kind, name string, idx int, nr_muls int) TypeIdent {
} }
} }
[inline] // [inline]
pub fn new_builtin_ti(kind Kind, nr_muls int) TypeIdent { // pub fn new_builtin_ti(kind Kind, nr_muls int) TypeIdent {
return TypeIdent{ // return TypeIdent{
idx: -int(kind) - 1 // idx: -int(kind) - 1
kind: kind // kind: kind
name: kind.str() // name: kind.str()
nr_muls: nr_muls // nr_muls: nr_muls
} // }
} // }
pub const ( pub const (
unresolved_ti = new_ti(.unresolved, 'unresolved', 0, 0)
void_ti = new_ti(.void, 'void', void_type_idx, 0) void_ti = new_ti(.void, 'void', void_type_idx, 0)
int_ti = new_ti(.int, 'int', int_type_idx, 0) int_ti = new_ti(.int, 'int', int_type_idx, 0)
string_ti = new_ti(.string, 'string', string_type_idx, 0) string_ti = new_ti(.string, 'string', string_type_idx, 0)
@ -115,24 +117,15 @@ pub fn (ti &TypeIdent) str() string {
for _ in 0 .. ti.nr_muls { for _ in 0 .. ti.nr_muls {
muls += '&' muls += '&'
} }
return '$muls$ti.name' // return '$muls$ti.name'
} return '$muls$ti.idx'
pub fn check(got, expected &TypeIdent) bool {
if expected.kind == .voidptr {
return true
}
if expected.name == 'array' {
return true
}
if got.idx != expected.idx {
return false
}
return true
} }
pub fn (k Kind) str() string { pub fn (k Kind) str() string {
k_str := match k { k_str := match k {
.unresolved{
'unresolved'
}
.placeholder{ .placeholder{
'placeholder' 'placeholder'
} }
@ -148,12 +141,12 @@ pub fn (k Kind) str() string {
.byteptr{ .byteptr{
'byteptr' 'byteptr'
} }
.const_{ // .const_{
'const' // 'const'
} // }
.enum_{ // .enum_{
'enum' // 'enum'
} // }
.struct_{ .struct_{
'struct' 'struct'
} }
@ -222,75 +215,42 @@ pub fn (kinds []Kind) str() string {
return kinds_str return kinds_str
} }
pub struct Placeholder { // pub struct Const {
pub: // pub:
idx int // name string
name string // }
// kind Kind
}
// Void | Voidptr | Charptr | Byteptr
pub struct Primitive {
pub:
idx int
kind Kind
}
pub struct Const { // pub struct Enum {
pub: // pub:
idx int // name string
name string // }
}
pub struct Enum {
pub:
idx int
name string
}
pub struct Struct { pub struct Struct {
pub:
idx int
parent_idx int
name string
pub mut: pub mut:
fields []Field fields []Field
methods []Field // TODO Method
} }
pub struct Field { pub struct Field {
pub: pub:
name string name string
type_idx int ti TypeIdent
// type_idx int
} }
pub struct Int { // pub struct Int {
pub: // pub:
idx int // bit_size u32
bit_size u32 // is_unsigned bool
is_unsigned bool // }
}
pub struct Float { // pub struct Float {
pub: // pub:
idx int // bit_size u32
bit_size u32 // }
}
pub struct String {
pub:
idx int
}
pub struct Bool {
pub:
idx int
}
pub struct Array { pub struct Array {
pub: pub:
idx int
parent_idx int
name string
elem_type_kind Kind elem_type_kind Kind
elem_type_idx int elem_type_idx int
elem_is_ptr bool elem_is_ptr bool
@ -299,9 +259,6 @@ pub:
pub struct ArrayFixed { pub struct ArrayFixed {
pub: pub:
idx int
parent_idx int
name string
elem_type_kind Kind elem_type_kind Kind
elem_type_idx int elem_type_idx int
elem_is_ptr bool elem_is_ptr bool
@ -311,8 +268,6 @@ pub:
pub struct Map { pub struct Map {
pub: pub:
idx int
name string
key_type_kind Kind key_type_kind Kind
key_type_idx int key_type_idx int
value_type_kind Kind value_type_kind Kind
@ -321,89 +276,11 @@ pub:
pub struct MultiReturn { pub struct MultiReturn {
pub: pub:
idx int
name string name string
tis []TypeIdent tis []TypeIdent
} }
pub struct Variadic { pub struct Variadic {
pub: pub:
idx int
ti TypeIdent ti TypeIdent
} }
pub fn (t Primitive) str() string {
s := match t.kind {
.void{
'void'
}
.voidptr{
'voidptr'
}
.charptr{
'charptr'
}
.byteptr{
'byteptr'
}
.char{
'char'
}
.byte{
'byte'
}
else {
'unknown'}
}
return s
}
pub fn (t Const) str() string {
return t.name
}
pub fn (t Enum) str() string {
return t.name
}
pub fn (t Struct) str() string {
return t.name
}
pub fn (t Int) str() string {
return if t.is_unsigned { 'u$t.bit_size' } else { 'i$t.bit_size' }
}
pub fn (t Float) str() string {
return 'f$t.bit_size'
}
pub fn (t String) str() string {
return 'string'
}
pub fn (t Array) str() string {
return t.name
}
pub fn (t ArrayFixed) str() string {
return t.name
}
pub fn (t Map) str() string {
return t.name
}
pub fn (t MultiReturn) str() string {
return t.name
}
pub fn (t Variadic) str() string {
return 'variadic_$t.ti.kind.str()'
}
/*
pub fn (s &Struct) has_field(name string) bool {
}
*/