2020-01-23 21:04:46 +01:00
|
|
|
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
2020-01-18 23:26:14 +01:00
|
|
|
// 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.token
|
2020-02-07 07:34:18 +01:00
|
|
|
os
|
2020-01-18 23:26:14 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
pub struct Checker {
|
2020-02-07 18:46:42 +01:00
|
|
|
table &table.Table
|
2020-01-18 23:26:14 +01:00
|
|
|
mut:
|
2020-02-07 18:46:42 +01:00
|
|
|
file_name string
|
2020-02-15 13:37:48 +01:00
|
|
|
scope ast.Scope
|
2020-02-10 08:32:08 +01:00
|
|
|
resolved []table.Type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_checker(table &table.Table) Checker {
|
|
|
|
return Checker{
|
|
|
|
table: table
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 12:03:12 +01:00
|
|
|
pub fn (c mut Checker) check(ast_file ast.File) {
|
|
|
|
c.file_name = ast_file.path
|
2020-02-15 13:37:48 +01:00
|
|
|
c.scope = ast_file.scope
|
|
|
|
|
2020-01-18 23:26:14 +01:00
|
|
|
for stmt in ast_file.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 07:31:54 +01:00
|
|
|
pub fn (c mut Checker) check_files(ast_files []ast.File) {
|
|
|
|
for file in ast_files {
|
2020-01-18 23:26:14 +01:00
|
|
|
c.check(file)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) check_struct_init(struct_init ast.StructInit) table.Type {
|
2020-02-10 08:32:08 +01:00
|
|
|
// typ := c.table.find_type(struct_init.typ.typ.name) or {
|
2020-02-10 13:58:24 +01:00
|
|
|
// c.error('unknown struct: $struct_init.typ.typ.name', struct_init.pos)
|
|
|
|
// panic('')
|
2020-02-10 08:32:08 +01:00
|
|
|
// }
|
2020-02-15 13:37:48 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(struct_init.typ)
|
|
|
|
match typ_sym.kind {
|
2020-01-18 23:26:14 +01:00
|
|
|
.placeholder {
|
2020-02-15 13:37:48 +01:00
|
|
|
c.error('unknown struct: $typ_sym.name', struct_init.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
.struct_ {
|
2020-02-15 13:37:48 +01:00
|
|
|
info := typ_sym.info as table.Struct
|
|
|
|
if struct_init.exprs.len > info.fields.len {
|
|
|
|
c.error('too many fields', struct_init.pos)
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
for i, expr in struct_init.exprs {
|
2020-02-15 13:37:48 +01:00
|
|
|
//struct_field info.
|
|
|
|
field_name := struct_init.fields[i]
|
|
|
|
mut field := info.fields[i]
|
|
|
|
mut found_field := false
|
|
|
|
for f in info.fields {
|
|
|
|
if f.name == field_name {
|
|
|
|
field = f
|
|
|
|
found_field = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found_field {
|
|
|
|
c.error('struct init: no such field `$field_name` for struct `$typ_sym.name`', struct_init.pos)
|
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
expr_type := c.expr(expr)
|
|
|
|
expr_type_sym := c.table.get_type_symbol(expr_type)
|
|
|
|
field_type_sym := c.table.get_type_symbol(field.typ)
|
|
|
|
if !c.table.check(expr_type, field.typ) {
|
|
|
|
c.error('cannot assign $expr_type_sym.name as $field_type_sym.name for field $field.name', struct_init.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
return struct_init.typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) infix_expr(infix_expr ast.InfixExpr) table.Type {
|
2020-02-06 13:57:35 +01:00
|
|
|
left_type := c.expr(infix_expr.left)
|
|
|
|
right_type := c.expr(infix_expr.right)
|
2020-02-10 08:32:08 +01:00
|
|
|
if !c.table.check(right_type, left_type) {
|
|
|
|
left_type_sym := c.table.get_type_symbol(left_type)
|
|
|
|
right_type_sym := c.table.get_type_symbol(right_type)
|
2020-01-22 21:34:38 +01:00
|
|
|
// if !c.table.check(&infix_expr.right_type, &infix_expr.right_type) {
|
|
|
|
// c.error('infix expr: cannot use `$infix_expr.right_type.name` as `$infix_expr.left_type.name`', infix_expr.pos)
|
2020-02-10 08:32:08 +01:00
|
|
|
c.error('infix expr: cannot use `$right_type_sym.name` as `$left_type_sym.name`', infix_expr.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
if infix_expr.op.is_relational() {
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.bool_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
return left_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
fn (c mut Checker) check_assign_expr(assign_expr ast.AssignExpr) {
|
2020-02-06 13:57:35 +01:00
|
|
|
left_type := c.expr(assign_expr.left)
|
|
|
|
right_type := c.expr(assign_expr.val)
|
|
|
|
if !c.table.check(right_type, left_type) {
|
2020-02-10 08:32:08 +01:00
|
|
|
left_type_sym := c.table.get_type_symbol(left_type)
|
|
|
|
right_type_sym := c.table.get_type_symbol(right_type)
|
|
|
|
c.error('cannot assign $right_type_sym.name to $left_type_sym.name', assign_expr.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) call_expr(call_expr ast.CallExpr) table.Type {
|
2020-01-18 23:26:14 +01:00
|
|
|
fn_name := call_expr.name
|
|
|
|
if f := c.table.find_fn(fn_name) {
|
|
|
|
// return_ti := f.return_ti
|
2020-02-10 13:58:24 +01:00
|
|
|
if f.is_c {
|
|
|
|
return f.return_type
|
|
|
|
}
|
|
|
|
if call_expr.args.len < f.args.len {
|
|
|
|
c.error('too few arguments in call to `$fn_name`', call_expr.pos)
|
|
|
|
}
|
|
|
|
else if !f.is_variadic && call_expr.args.len > f.args.len {
|
|
|
|
c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)', call_expr.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
for i, arg_expr in call_expr.args {
|
2020-02-10 13:58:24 +01:00
|
|
|
arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] }
|
2020-02-06 13:57:35 +01:00
|
|
|
typ := c.expr(arg_expr)
|
2020-02-10 08:32:08 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
arg_typ_sym := c.table.get_type_symbol(arg.typ)
|
2020-02-10 13:58:24 +01:00
|
|
|
if !c.table.check(typ, arg.typ) {
|
2020-02-10 08:32:08 +01:00
|
|
|
c.error('!cannot use type `$typ_sym.name` as type `$arg_typ_sym.name` in argument ${i+1} to `$fn_name`', call_expr.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
2020-01-22 21:34:38 +01:00
|
|
|
return f.return_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-04 12:03:12 +01:00
|
|
|
c.error('unknown fn: $fn_name', call_expr.pos)
|
|
|
|
exit(1)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) check_method_call_expr(method_call_expr ast.MethodCallExpr) table.Type {
|
2020-02-06 13:57:35 +01:00
|
|
|
typ := c.expr(method_call_expr.expr)
|
2020-02-10 08:32:08 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
if method := typ_sym.find_method(method_call_expr.name) {
|
2020-02-04 12:03:12 +01:00
|
|
|
return method.return_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-08 09:50:12 +01:00
|
|
|
// check parent
|
2020-02-10 08:32:08 +01:00
|
|
|
if !isnil(typ_sym.parent) {
|
|
|
|
if method := typ_sym.parent.find_method(method_call_expr.name) {
|
2020-02-07 09:19:45 +01:00
|
|
|
return method.return_type
|
|
|
|
}
|
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
c.error('type `$typ_sym.name` has no method `$method_call_expr.name`', method_call_expr.pos)
|
2020-02-04 12:03:12 +01:00
|
|
|
exit(1)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) selector_expr(selector_expr ast.SelectorExpr) table.Type {
|
2020-02-06 13:57:35 +01:00
|
|
|
typ := c.expr(selector_expr.expr)
|
2020-02-10 08:32:08 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-01-18 23:26:14 +01:00
|
|
|
field_name := selector_expr.field
|
2020-02-10 08:32:08 +01:00
|
|
|
if field := typ_sym.find_field(field_name) {
|
2020-02-08 16:59:57 +01:00
|
|
|
return field.typ
|
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
// check parent
|
|
|
|
if !isnil(typ_sym.parent) {
|
|
|
|
if field := typ_sym.parent.find_field(field_name) {
|
2020-02-06 13:57:35 +01:00
|
|
|
return field.typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
if typ_sym.kind != .struct_ {
|
|
|
|
c.error('`$typ_sym.name` is not a struct', selector_expr.pos)
|
2020-02-08 16:59:57 +01:00
|
|
|
}
|
|
|
|
else {
|
2020-02-10 08:32:08 +01:00
|
|
|
c.error('unknown field `${typ_sym.name}.$field_name`', selector_expr.pos)
|
2020-02-08 16:59:57 +01:00
|
|
|
}
|
|
|
|
exit(0)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: non deferred
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) return_stmt(return_stmt ast.Return) {
|
2020-02-10 08:32:08 +01:00
|
|
|
mut got_types := []table.Type
|
2020-01-22 21:34:38 +01:00
|
|
|
if return_stmt.exprs.len == 0 {
|
|
|
|
return
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
for expr in return_stmt.exprs {
|
2020-02-06 13:57:35 +01:00
|
|
|
typ := c.expr(expr)
|
|
|
|
got_types << typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
expected_type := return_stmt.expected_type
|
2020-02-10 08:32:08 +01:00
|
|
|
expected_type_sym := c.table.get_type_symbol(expected_type)
|
2020-02-06 13:57:35 +01:00
|
|
|
mut expected_types := [expected_type]
|
2020-02-10 08:32:08 +01:00
|
|
|
if expected_type_sym.kind == .multi_return {
|
|
|
|
mr_info := expected_type_sym.info as table.MultiReturn
|
2020-02-06 13:57:35 +01:00
|
|
|
expected_types = mr_info.types
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
if expected_types.len > 0 && expected_types.len != got_types.len {
|
|
|
|
c.error('wrong number of return arguments:\n\texpected: $expected_types.str()\n\tgot: $got_types.str()', return_stmt.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
for i, exp_typ in expected_types {
|
|
|
|
got_typ := got_types[i]
|
|
|
|
if !c.table.check(got_typ, exp_typ) {
|
2020-02-10 08:32:08 +01:00
|
|
|
got_typ_sym := c.table.get_type_symbol(got_typ)
|
|
|
|
exp_typ_sym := c.table.get_type_symbol(exp_typ)
|
|
|
|
c.error('cannot use `$got_typ_sym.name` as type `$exp_typ_sym.name` in return argument', return_stmt.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 17:38:02 +01:00
|
|
|
pub fn (c &Checker) assign_stmt(assign_stmt ast.AssignStmt) {}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) array_init(array_init mut ast.ArrayInit) table.Type {
|
2020-02-10 08:32:08 +01:00
|
|
|
mut elem_type := table.void_type
|
2020-02-15 13:37:48 +01:00
|
|
|
|
|
|
|
// a = []
|
|
|
|
if array_init.exprs.len == 0 {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-01-19 13:52:34 +01:00
|
|
|
for i, expr in array_init.exprs {
|
|
|
|
c.expr(expr)
|
2020-02-06 13:57:35 +01:00
|
|
|
typ := c.expr(expr)
|
2020-01-19 13:52:34 +01:00
|
|
|
// The first element's type
|
|
|
|
if i == 0 {
|
2020-02-06 13:57:35 +01:00
|
|
|
elem_type = typ
|
2020-01-19 13:52:34 +01:00
|
|
|
continue
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
if !c.table.check(elem_type, typ) {
|
2020-02-10 08:32:08 +01:00
|
|
|
elem_type_sym := c.table.get_type_symbol(elem_type)
|
|
|
|
c.error('expected array element with type `$elem_type_sym.name`', array_init.pos)
|
2020-01-19 13:52:34 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
//idx := if is_fixed { p.table.find_or_register_array_fixed(val_type, fixed_size, 1) } else { p.table.find_or_register_array(val_type, 1) }
|
|
|
|
is_fixed := false
|
|
|
|
fixed_size := 1
|
|
|
|
idx := if is_fixed {
|
|
|
|
c.table.find_or_register_array_fixed(elem_type, fixed_size, 1)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.table.find_or_register_array(elem_type, 1)
|
|
|
|
}
|
|
|
|
array_type := table.new_type(idx)
|
|
|
|
array_init.typ = array_type
|
2020-02-06 13:57:35 +01:00
|
|
|
return array_init.typ
|
2020-01-19 13:52:34 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
fn (c mut Checker) stmt(node ast.Stmt) {
|
2020-02-03 10:27:06 +01:00
|
|
|
match mut node {
|
2020-01-18 23:26:14 +01:00
|
|
|
ast.FnDecl {
|
|
|
|
for stmt in it.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.Return {
|
2020-01-22 21:34:38 +01:00
|
|
|
c.return_stmt(it)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-06 17:38:02 +01:00
|
|
|
ast.AssignStmt {
|
|
|
|
c.assign_stmt(it)
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
ast.ConstDecl {
|
|
|
|
for i, expr in it.exprs {
|
|
|
|
mut field := it.fields[i]
|
|
|
|
typ := c.expr(expr)
|
|
|
|
mut xconst := c.table.consts[field.name]
|
|
|
|
//if xconst.typ == 0 {
|
|
|
|
xconst.typ = typ
|
|
|
|
c.table.consts[field.name] = xconst
|
|
|
|
//}
|
|
|
|
field.typ = typ
|
|
|
|
it.fields[i] = field
|
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
ast.VarDecl {
|
2020-02-15 13:37:48 +01:00
|
|
|
println('VARDECL')
|
2020-01-22 21:34:38 +01:00
|
|
|
typ := c.expr(it.expr)
|
2020-02-15 13:37:48 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
//println('var $it.name - $typ - $it.typ')
|
|
|
|
//if it.typ == 0 {
|
|
|
|
// it.typ = typ
|
|
|
|
//}
|
|
|
|
it.typ = typ
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.ForStmt {
|
|
|
|
typ := c.expr(it.cond)
|
2020-02-10 08:32:08 +01:00
|
|
|
// typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
// if typ_sym.kind != .bool {
|
|
|
|
if table.type_idx(typ) != table.bool_type_idx {
|
2020-02-03 07:02:54 +01:00
|
|
|
c.error('non-bool used as for condition', it.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
for stmt in it.stmts {
|
|
|
|
c.stmt(stmt)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.ForCStmt {
|
|
|
|
c.stmt(it.init)
|
|
|
|
c.expr(it.cond)
|
|
|
|
c.stmt(it.inc)
|
|
|
|
for stmt in it.stmts {
|
|
|
|
c.stmt(stmt)
|
2020-02-02 14:31:54 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
// ast.StructDecl {}
|
|
|
|
ast.ExprStmt {
|
|
|
|
c.expr(it.expr)
|
|
|
|
}
|
|
|
|
else {}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) expr(node ast.Expr) table.Type {
|
2020-02-06 13:57:35 +01:00
|
|
|
match mut node {
|
2020-02-03 07:02:54 +01:00
|
|
|
ast.AssignExpr {
|
|
|
|
c.check_assign_expr(it)
|
|
|
|
}
|
|
|
|
ast.IntegerLiteral {
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.int_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
ast.FloatLiteral{
|
|
|
|
return table.f64_type
|
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
ast.PostfixExpr {
|
2020-02-04 07:37:38 +01:00
|
|
|
return c.postfix_expr(it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
/*
|
2020-01-18 23:26:14 +01:00
|
|
|
ast.UnaryExpr {
|
|
|
|
c.expr(it.left)
|
|
|
|
}
|
2020-01-22 21:34:38 +01:00
|
|
|
*/
|
2020-02-03 07:02:54 +01:00
|
|
|
ast.StringLiteral {
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.string_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.PrefixExpr {
|
|
|
|
return c.expr(it.right)
|
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
|
|
|
return c.infix_expr(it)
|
|
|
|
}
|
|
|
|
ast.StructInit {
|
|
|
|
return c.check_struct_init(it)
|
|
|
|
}
|
|
|
|
ast.CallExpr {
|
|
|
|
return c.call_expr(it)
|
|
|
|
}
|
|
|
|
ast.MethodCallExpr {
|
|
|
|
return c.check_method_call_expr(it)
|
|
|
|
}
|
|
|
|
ast.ArrayInit {
|
2020-02-15 13:37:48 +01:00
|
|
|
return c.array_init(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.Ident {
|
2020-02-15 13:37:48 +01:00
|
|
|
//println('IDENT: $it.name - $it.pos.pos')
|
2020-02-03 07:02:54 +01:00
|
|
|
if it.kind == .variable {
|
2020-02-15 13:37:48 +01:00
|
|
|
//println('===========================')
|
|
|
|
//ast.print_scope_vars(&c.scope, 0)
|
|
|
|
//println('===========================')
|
|
|
|
info := it.info as ast.IdentVar
|
|
|
|
if info.typ != 0 {
|
|
|
|
return info.typ
|
|
|
|
}
|
|
|
|
if inner := c.scope.innermost(it.pos.pos) {
|
|
|
|
mut found := true
|
|
|
|
mut varscope, var := inner.find_scope_and_var(it.name) or {
|
|
|
|
//ast.print_scope_vars(inner, 0)
|
|
|
|
found = false
|
|
|
|
c.error('not found: $it.name - POS: $it.pos.pos', it.pos)
|
|
|
|
panic('')
|
|
|
|
}
|
|
|
|
if found {
|
|
|
|
mut typ := var.typ
|
|
|
|
// set var type on first use
|
|
|
|
if var.typ == 0 {
|
|
|
|
typ = c.expr(var.expr)
|
|
|
|
mut v1 := var
|
|
|
|
v1.typ = typ
|
|
|
|
varscope.vars[var.name] = v1
|
|
|
|
}
|
|
|
|
// update ident
|
|
|
|
it.kind = .variable
|
|
|
|
it.info = ast.IdentVar{
|
|
|
|
typ: typ
|
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
2020-01-22 21:34:38 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-10 18:42:53 +01:00
|
|
|
// Handle indents with unresolved types during the parsing step
|
|
|
|
// (declared after first usage)
|
2020-02-15 13:37:48 +01:00
|
|
|
else if it.kind == .constant {
|
|
|
|
if constant := c.table.find_const(it.name) {
|
|
|
|
return constant.typ
|
|
|
|
}
|
|
|
|
}
|
2020-02-10 18:42:53 +01:00
|
|
|
else if it.kind == .blank_ident {
|
2020-02-15 13:37:48 +01:00
|
|
|
println('CONST A: $it.name')
|
2020-02-10 18:42:53 +01:00
|
|
|
if constant := c.table.find_const(it.name) {
|
2020-02-15 13:37:48 +01:00
|
|
|
println('CONST: $it.name')
|
|
|
|
|
2020-02-10 18:42:53 +01:00
|
|
|
return constant.typ
|
|
|
|
}
|
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.void_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.BoolLiteral {
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.bool_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.SelectorExpr {
|
|
|
|
return c.selector_expr(it)
|
|
|
|
}
|
|
|
|
ast.IndexExpr {
|
2020-02-03 09:11:10 +01:00
|
|
|
return c.index_expr(it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.IfExpr {
|
2020-02-15 13:37:48 +01:00
|
|
|
return c.if_expr(mut it)
|
|
|
|
}
|
|
|
|
ast.MatchExpr {
|
|
|
|
return c.match_expr(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-02-10 14:43:17 +01:00
|
|
|
ast.CastExpr {
|
|
|
|
return it.typ
|
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
else {}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.void_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) match_expr(node mut ast.MatchExpr) table.Type {
|
|
|
|
t := c.expr(node.cond)
|
|
|
|
for i, block in node.blocks {
|
|
|
|
match_expr := node.match_exprs[i]
|
|
|
|
c.expr(match_expr)
|
|
|
|
for stmt in block.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
// If the last statement is an expression, return its type
|
|
|
|
if block.stmts.len > 0 {
|
|
|
|
match block.stmts[block.stmts.len - 1] {
|
|
|
|
ast.ExprStmt {
|
|
|
|
// TODO: ask alex about this
|
|
|
|
//typ := c.expr(it.expr)
|
|
|
|
//type_sym := c.table.get_type_symbol(typ)
|
|
|
|
//p.warn('match expr ret $type_sym.name')
|
|
|
|
//node.typ = typ
|
|
|
|
//return typ
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node.typ = t
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (c mut Checker) if_expr(node mut ast.IfExpr) table.Type {
|
|
|
|
typ := c.expr(node.cond)
|
|
|
|
node.typ = typ
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
// if typ_sym.kind != .bool {
|
|
|
|
if table.type_idx(typ) != table.bool_type_idx {
|
|
|
|
c.error('non-bool (`$typ_sym.name`) used as if condition', node.pos)
|
|
|
|
}
|
|
|
|
for i, stmt in node.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
if node.else_stmts.len > 0 {
|
|
|
|
for stmt in node.else_stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if node.stmts.len > 0 {
|
|
|
|
match node.stmts[node.stmts.len - 1] {
|
|
|
|
ast.ExprStmt {
|
|
|
|
//type_sym := p.table.get_type_symbol(it.typ)
|
|
|
|
//p.warn('if expr ret $type_sym.name')
|
|
|
|
//typ = it.typ
|
|
|
|
//return it.typ
|
|
|
|
t := c.expr(it.expr)
|
|
|
|
node.typ = t
|
|
|
|
return t
|
|
|
|
// return node,it.ti
|
|
|
|
// left =
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return typ
|
|
|
|
//return table.void_type
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (c mut Checker) postfix_expr(node ast.PostfixExpr) table.Type {
|
2020-02-04 07:37:38 +01:00
|
|
|
/*
|
|
|
|
match node.expr {
|
|
|
|
ast.IdentVar {
|
|
|
|
println('postfix identvar')
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
typ := c.expr(node.expr)
|
2020-02-10 08:32:08 +01:00
|
|
|
// if typ.typ.kind != .int {
|
|
|
|
if table.type_idx(typ) != table.int_type_idx {
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)', node.pos)
|
2020-02-04 07:37:38 +01:00
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) index_expr(node ast.IndexExpr) table.Type {
|
|
|
|
/*
|
|
|
|
mut typ := left_type
|
|
|
|
left_type_sym := p.table.get_type_symbol(left_type)
|
|
|
|
if left_type_sym.kind == .array {
|
|
|
|
info := left_type_sym.info as table.Array
|
|
|
|
typ = info.elem_type
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2020-02-03 09:11:10 +01:00
|
|
|
mut typ := c.expr(node.left)
|
2020-02-03 11:29:50 +01:00
|
|
|
mut is_range := false // TODO is_range := node.index is ast.RangeExpr
|
|
|
|
match node.index {
|
|
|
|
ast.RangeExpr {
|
|
|
|
is_range = true
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
if typ_sym.kind == .array {
|
2020-02-03 11:29:50 +01:00
|
|
|
if is_range {} // `x[start..end]` has the same type as `x`
|
|
|
|
else {
|
|
|
|
// Check index type
|
|
|
|
index_type := c.expr(node.index)
|
2020-02-10 08:32:08 +01:00
|
|
|
// if index_type.typ.kind != .int {
|
|
|
|
if table.type_idx(index_type) != table.int_type_idx {
|
|
|
|
index_type_sym := c.table.get_type_symbol(index_type)
|
|
|
|
c.error('non-integer index (type `$index_type_sym.name`)', node.pos)
|
2020-02-03 11:29:50 +01:00
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
info := typ_sym.info as table.Array
|
2020-02-06 13:57:35 +01:00
|
|
|
return info.elem_type
|
2020-02-03 09:11:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-02-10 08:32:08 +01:00
|
|
|
typ = table.int_type
|
2020-02-03 09:11:10 +01:00
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
2020-02-03 07:02:54 +01:00
|
|
|
pub fn (c &Checker) error(s string, pos token.Position) {
|
|
|
|
print_backtrace()
|
2020-02-07 07:34:18 +01:00
|
|
|
mut path := c.file_name
|
|
|
|
// Get relative path
|
|
|
|
workdir := os.getwd() + os.path_separator
|
|
|
|
if path.starts_with(workdir) {
|
|
|
|
path = path.replace(workdir, '')
|
|
|
|
}
|
|
|
|
final_msg_line := '$path:$pos.line_nr: checker error: $s'
|
2020-02-03 07:02:54 +01:00
|
|
|
eprintln(final_msg_line)
|
|
|
|
/*
|
2020-01-18 23:26:14 +01:00
|
|
|
if colored_output {
|
|
|
|
eprintln(term.bold(term.red(final_msg_line)))
|
|
|
|
}else{
|
|
|
|
eprintln(final_msg_line)
|
|
|
|
}
|
|
|
|
*/
|
2020-01-22 21:34:38 +01:00
|
|
|
|
2020-02-03 07:02:54 +01:00
|
|
|
exit(1)
|
|
|
|
}
|