v2: short struct init syntax; .xxx enum checks; unions; assert

pull/3853/head
Alexander Medvednikov 2020-02-26 15:51:05 +01:00
parent c26016b132
commit 857cbfb0d2
10 changed files with 128 additions and 67 deletions

View File

@ -2522,8 +2522,8 @@ fn (p mut Parser) indot_expr() string {
if is_map && typ != 'string' { if is_map && typ != 'string' {
p.error('bad element type: expecting `string`') p.error('bad element type: expecting `string`')
} }
T := p.table.find_type(arr_typ) arr_typ2 := p.table.find_type(arr_typ)
if !is_map && !T.has_method('contains') { if !is_map && !arr_typ2.has_method('contains') {
p.error('$arr_typ has no method `contains`') p.error('$arr_typ has no method `contains`')
} }
// `typ` is element's type // `typ` is element's type

View File

@ -122,18 +122,18 @@ fn (p mut Parser) check_enum_member_access() {
if p.expected_type.starts_with('Option_') { if p.expected_type.starts_with('Option_') {
p.expected_type = p.expected_type[7..] p.expected_type = p.expected_type[7..]
} }
T := p.find_type(p.expected_type) tt := p.find_type(p.expected_type)
if T.cat == .enum_ { if tt.cat == .enum_ {
p.check(.dot) p.check(.dot)
val := p.check_name() val := p.check_name()
// Make sure this enum value exists // Make sure this enum value exists
if !T.has_enum_val(val) { if !tt.has_enum_val(val) {
p.error('enum `$T.name` does not have value `$val`') p.error('enum `$tt.name` does not have value `$val`')
} }
p.gen(mod_gen_name(T.mod) + '__' + p.expected_type + '_' + val) p.gen(mod_gen_name(tt.mod) + '__' + p.expected_type + '_' + val)
} }
else { else {
p.error('`$T.name` is not an enum') p.error('`$tt.name` is not an enum')
} }
} }

View File

@ -675,8 +675,8 @@ fn (p mut Parser) expression() string {
return typ return typ
} }
fn (p mut Parser) handle_operator(op string, typ string, cpostfix string, ph int, T &Type) { fn (p mut Parser) handle_operator(op string, typ string,cpostfix string, ph int, tt &Type) {
if T.has_method(op) { if tt.has_method(op) {
p.cgen.set_placeholder(ph, '${typ}_${cpostfix}(') p.cgen.set_placeholder(ph, '${typ}_${cpostfix}(')
p.gen(')') p.gen(')')
} }

View File

@ -1138,7 +1138,7 @@ fn (p mut Parser) fn_call_args(f mut Fn, generic_param_types []string) {
} }
if i == 0 && (f.name == 'println' || f.name == 'print') && !(typ in ['string', 'ustring', 'void']) { if i == 0 && (f.name == 'println' || f.name == 'print') && !(typ in ['string', 'ustring', 'void']) {
// //
T := p.table.find_type(typ) tt := p.table.find_type(typ)
$if !windows { $if !windows {
$if !js { $if !js {
fmt := p.typ_to_fmt(typ, 0) fmt := p.typ_to_fmt(typ, 0)
@ -1156,31 +1156,31 @@ fn (p mut Parser) fn_call_args(f mut Fn, generic_param_types []string) {
} }
// Make sure this type has a `str()` method // Make sure this type has a `str()` method
$if !js { $if !js {
if !T.has_method('str') { if !tt.has_method('str') {
// varg // varg
if T.name.starts_with('varg_') { if tt.name.starts_with('varg_') {
p.gen_varg_str(T) p.gen_varg_str(tt)
p.cgen.set_placeholder(ph, '${typ}_str(') p.cgen.set_placeholder(ph, '${typ}_str(')
p.gen(')') p.gen(')')
continue continue
} }
// Arrays have automatic `str()` methods // Arrays have automatic `str()` methods
else if T.name.starts_with('array_') { else if tt.name.starts_with('array_') {
p.gen_array_str(T) p.gen_array_str(tt)
p.cgen.set_placeholder(ph, '${typ}_str(') p.cgen.set_placeholder(ph, '${typ}_str(')
p.gen(')') p.gen(')')
continue continue
} }
// struct // struct
else if T.cat == .struct_ { else if tt.cat == .struct_ {
p.gen_struct_str(T) p.gen_struct_str(tt)
p.cgen.set_placeholder(ph, '${typ}_str(') p.cgen.set_placeholder(ph, '${typ}_str(')
p.gen(')') p.gen(')')
continue continue
} }
else { else {
base := p.base_type(T.name) base := p.base_type(tt.name)
if base != T.name { if base != tt.name {
base_type := p.find_type(base) base_type := p.find_type(base)
if base_type.has_method('str') { if base_type.has_method('str') {
p.cgen.set_placeholder(ph, '${base_type.name}_str(') p.cgen.set_placeholder(ph, '${base_type.name}_str(')

View File

@ -291,7 +291,7 @@ pub fn f32_to_decimal(mant u32, exp u32) Dec32 {
out = vr + bool_to_u32(vr == vm || last_removed_digit >= 5) out = vr + bool_to_u32(vr == vm || last_removed_digit >= 5)
} }
return Dec32{m: out, e: e10 + removed} return Dec32{m: out e: e10 + removed}
} }
// f32_to_str return a string in scientific notation with max n_digit after the dot // f32_to_str return a string in scientific notation with max n_digit after the dot

View File

@ -55,8 +55,8 @@ pow5_inv_split_32 = [
] ]
pow5_split_64 =[ pow5_split_64 =[
Uint128{u64(0), 72057594037927936}, Uint128{0, 72057594037927936},
Uint128{u64(0), 90071992547409920}, Uint128{0, 90071992547409920},
Uint128{u64(0), 112589990684262400}, Uint128{u64(0), 112589990684262400},
Uint128{u64(0), 140737488355328000}, Uint128{u64(0), 140737488355328000},
Uint128{u64(0), 87960930222080000}, Uint128{u64(0), 87960930222080000},

View File

@ -16,7 +16,7 @@ CastExpr | EnumVal | Assoc | SizeOf | None | MapInit
pub type Stmt = VarDecl | GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | pub type Stmt = VarDecl | GlobalDecl | FnDecl | Return | Module | Import | ExprStmt |
ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt | ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt |
HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt | HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt |
LineComment | MultiLineComment LineComment | MultiLineComment | AssertStmt
pub type Type = StructType | ArrayType pub type Type = StructType | ArrayType
@ -469,6 +469,11 @@ pub:
expr Expr expr Expr
} }
pub struct AssertStmt {
pub:
expr Expr
}
pub struct Assoc { pub struct Assoc {
pub: pub:
name string name string

View File

@ -21,6 +21,7 @@ mut:
file ast.File file ast.File
nr_errors int nr_errors int
errors []string errors []string
expected_type table.Type
} }
pub fn new_checker(table &table.Table) Checker { pub fn new_checker(table &table.Table) Checker {
@ -78,6 +79,10 @@ pub fn (c mut Checker) check_struct_init(struct_init ast.StructInit) table.Type
} }
.struct_ { .struct_ {
info := typ_sym.info as table.Struct info := typ_sym.info as table.Struct
if struct_init.fields.len == 0 {
// Short syntax TODO check
return struct_init.typ
}
if struct_init.exprs.len > info.fields.len { if struct_init.exprs.len > info.fields.len {
c.error('too many fields', struct_init.pos) c.error('too many fields', struct_init.pos)
} }
@ -279,6 +284,7 @@ pub fn (c mut Checker) array_init(array_init mut ast.ArrayInit) table.Type {
// The first element's type // The first element's type
if i == 0 { if i == 0 {
elem_type = typ elem_type = typ
c.expected_type = typ
continue continue
} }
if !c.table.check(elem_type, typ) { if !c.table.check(elem_type, typ) {
@ -366,15 +372,7 @@ pub fn (c mut Checker) expr(node ast.Expr) table.Type {
c.check_assign_expr(it) c.check_assign_expr(it)
} }
ast.EnumVal { ast.EnumVal {
typ_idx := c.table.find_type_idx(it.enum_name) // or { return c.enum_val(it)
typ := c.table.find_type(it.enum_name) or {
panic(err)
}
info := typ.info as table.Enum
if !(it.val in info.vals) {
c.error('enum `$it.enum_name` does not have a value `$it.val`', it.pos)
}
return typ_idx
} }
ast.FloatLiteral { ast.FloatLiteral {
return table.f64_type return table.f64_type
@ -651,6 +649,20 @@ pub fn (c mut Checker) index_expr(node ast.IndexExpr) table.Type {
return typ return typ
} }
// `.green` or `Color.green`
// If a short form is used, `expected_type` needs to be an enum
// with this value.
pub fn (c mut Checker) enum_val(node ast.EnumVal) table.Type {
typ_idx := if node.enum_name == '' { c.expected_type } else { c.table.find_type_idx(node.enum_name) }
typ := c.table.get_type_symbol(table.Type(typ_idx))
// println('checker: enum val $c.expected_type $typ.name')
info := typ.info as table.Enum
if !(node.val in info.vals) {
c.error('enum `$typ.name` does not have a value `$node.val`', node.pos)
}
return typ_idx
}
pub fn (c mut Checker) error(s string, pos token.Position) { pub fn (c mut Checker) error(s string, pos token.Position) {
c.nr_errors++ c.nr_errors++
print_backtrace() print_backtrace()

View File

@ -238,6 +238,9 @@ pub fn (p mut Parser) top_stmt() ast.Stmt {
.key_enum { .key_enum {
return p.enum_decl() return p.enum_decl()
} }
.key_union {
return p.struct_decl()
}
.line_comment { .line_comment {
// p.next() // p.next()
return ast.LineComment{ return ast.LineComment{
@ -252,7 +255,7 @@ pub fn (p mut Parser) top_stmt() ast.Stmt {
} }
else { else {
// #printf(""); // #printf("");
p.error('parser: bad top level statement') p.error('parser: bad top level statement ' + p.tok.str())
return ast.Stmt{} return ast.Stmt{}
} }
} }
@ -260,6 +263,13 @@ pub fn (p mut Parser) top_stmt() ast.Stmt {
pub fn (p mut Parser) stmt() ast.Stmt { pub fn (p mut Parser) stmt() ast.Stmt {
match p.tok.kind { match p.tok.kind {
.key_assert {
p.next()
expr,_ := p.expr(0)
return ast.AssertStmt{
expr: expr
}
}
.key_mut { .key_mut {
return p.var_decl() return p.var_decl()
} }
@ -481,7 +491,7 @@ fn (p mut Parser) struct_init() ast.StructInit {
mut field_names := []string mut field_names := []string
mut exprs := []ast.Expr mut exprs := []ast.Expr
mut i := 0 mut i := 0
// TODO if sym.info is table.Struct // TODO `if sym.info is table.Struct`
mut is_struct := false mut is_struct := false
match sym.info { match sym.info {
table.Struct { table.Struct {
@ -489,25 +499,41 @@ fn (p mut Parser) struct_init() ast.StructInit {
} }
else {} else {}
} }
is_short_syntax := !(p.peek_tok.kind == .colon || p.tok.kind == .rcbr) // `Vec{a,b,c}`
// p.warn(is_short_syntax.str())
for p.tok.kind != .rcbr { for p.tok.kind != .rcbr {
field_name := p.check_name() mut field_name := ''
if is_short_syntax {
expr,_ := p.expr(0)
exprs << expr
}
else {
field_name = p.check_name()
field_names << field_name field_names << field_name
}
// Set expected type for this field's expression // Set expected type for this field's expression
// p.warn('$sym.name field $field_name') // p.warn('$sym.name field $field_name')
if is_struct { if is_struct {
info := sym.info as table.Struct info := sym.info as table.Struct
if is_short_syntax {}
else {
field := sym.find_field(field_name) or { field := sym.find_field(field_name) or {
p.error('field `${sym.name}.$field_name` not found') p.error('field `${sym.name}.$field_name` not found')
continue continue
} }
p.expected_type = field.typ p.expected_type = field.typ
}
// p.warn('setting exp $field.typ') // p.warn('setting exp $field.typ')
} }
// if !is_short_syntax {
p.check(.colon) p.check(.colon)
expr,_ := p.expr(0) expr,_ := p.expr(0)
exprs << expr exprs << expr
}
i++ i++
if p.tok.kind == .comma {
p.check(.comma)
}
} }
node := ast.StructInit{ node := ast.StructInit{
typ: typ typ: typ
@ -609,7 +635,8 @@ pub fn (p mut Parser) expr(precedence int) (ast.Expr,table.Type) {
} }
.dot { .dot {
// .enum_val // .enum_val
node,typ = p.enum_val() // node,typ = p.enum_val()
node = p.enum_val()
} }
.chartoken { .chartoken {
typ = table.byte_type typ = table.byte_type
@ -909,14 +936,25 @@ fn (p &Parser) is_addative() bool {
*/ */
// `.green` // `.green`
// `pref.BuildMode.default_mode` // `pref.BuildMode.default_mode`
fn (p mut Parser) enum_val() (ast.Expr,table.Type) { fn (p mut Parser) enum_val() ast.EnumVal {
p.check(.dot) p.check(.dot)
val := p.check_name() val := p.check_name()
mut node := ast.Expr{} /*
node = ast.EnumVal{ if p.expected_type == 0 {
p.error('wtf')
}
*/
/*
sym := p.table.get_type_symbol(p.expected_type) //or {
//return ast.EnumVal{val:val}
//}
p.warn(sym.name)
*/
return ast.EnumVal{
val: val val: val
} }
return node,table.int_type
} }
fn (p mut Parser) for_statement() ast.Stmt { fn (p mut Parser) for_statement() ast.Stmt {
@ -1332,12 +1370,18 @@ fn (p mut Parser) const_decl() ast.ConstDecl {
} }
} }
// structs and unions
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 {
p.next() p.next()
} }
if p.tok.kind == .key_struct {
p.check(.key_struct) p.check(.key_struct)
}
else {
p.check(.key_union)
}
is_c := p.tok.lit == 'C' && p.peek_tok.kind == .dot is_c := p.tok.lit == 'C' && p.peek_tok.kind == .dot
if is_c { if is_c {
p.next() // C p.next() // C

View File

@ -186,7 +186,7 @@ pub fn (t &Table) get_type_symbol(typ Type) &TypeSymbol {
return &t.types[idx] return &t.types[idx]
} }
// this should never happen // this should never happen
panic('get_type_symbol: invalid type $typ - $idx') panic('get_type_symbol: invalid type $typ - ${idx}. This should neer happen')
} }
// this will override or register builtin type // this will override or register builtin type