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

@ -109,7 +109,7 @@ int typ;
// Skip nameless enums // Skip nameless enums
else if !no_name && !p.first_pass() { else if !no_name && !p.first_pass() {
p.cgen.typedefs << 'typedef int $enum_name;' p.cgen.typedefs << 'typedef int $enum_name;'
} }
p.check(.rcbr) p.check(.rcbr)
p.fgen_nl() p.fgen_nl()
p.fgen_nl() p.fgen_nl()
@ -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

@ -1,6 +1,6 @@
/********************************************************************** /**********************************************************************
* *
* f32 to string * f32 to string
* *
* Copyright (c) 2019-2020 Dario Deledda. All rights reserved. * Copyright (c) 2019-2020 Dario Deledda. All rights reserved.
* Use of this source code is governed by an MIT license * Use of this source code is governed by an MIT license
@ -9,11 +9,11 @@
* This file contains the f32 to string functions * This file contains the f32 to string functions
* *
* These functions are based on the work of: * These functions are based on the work of:
* Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN * Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN
* Conference on Programming Language Design and ImplementationJune 2018 * Conference on Programming Language Design and ImplementationJune 2018
* Pages 270282 https://doi.org/10.1145/3192366.3192369 * Pages 270282 https://doi.org/10.1145/3192366.3192369
* *
* inspired by the Go version here: * inspired by the Go version here:
* https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea * https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea
* *
**********************************************************************/ **********************************************************************/
@ -72,7 +72,7 @@ fn (d Dec32) get_string_32(neg bool, n_digit int) string {
mut x := 0 mut x := 0
for x < (out_len-disp-1) { for x < (out_len-disp-1) {
buf[y - x] = `0` + byte(out%10) buf[y - x] = `0` + byte(out%10)
out /= 10 out /= 10
i++ i++
x++ x++
} }
@ -170,7 +170,7 @@ pub fn f32_to_decimal(mant u32, exp u32) Dec32 {
mm_shift := bool_to_u32(mant != 0 || exp <= 1) mm_shift := bool_to_u32(mant != 0 || exp <= 1)
mm := u32(4 * m2 - 1 - mm_shift) mm := u32(4 * m2 - 1 - mm_shift)
mut vr := u32(0) mut vr := u32(0)
mut vp := u32(0) mut vp := u32(0)
mut vm := u32(0) mut vm := u32(0)
mut e10 := 0 mut e10 := 0
@ -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
@ -316,7 +316,7 @@ pub fn f32_to_str(f f32, n_digit int) string {
//println("with exp form") //println("with exp form")
d = f32_to_decimal(mant, exp) d = f32_to_decimal(mant, exp)
} }
//println("${d.m} ${d.e}") //println("${d.m} ${d.e}")
return d.get_string_32(neg, n_digit) return d.get_string_32(neg, n_digit)
} }

View File

@ -28,7 +28,7 @@ powers_of_10 = [
// We only need to find the length of at most 17 digit numbers. // We only need to find the length of at most 17 digit numbers.
] ]
pow5_split_32 = [ pow5_split_32 = [
1152921504606846976, 1441151880758558720, 1801439850948198400, 2251799813685248000, 1152921504606846976, 1441151880758558720, 1801439850948198400, 2251799813685248000,
1407374883553280000, 1759218604441600000, 2199023255552000000, 1374389534720000000, 1407374883553280000, 1759218604441600000, 2199023255552000000, 1374389534720000000,
1717986918400000000, 2147483648000000000, 1342177280000000000, 1677721600000000000, 1717986918400000000, 2147483648000000000, 1342177280000000000, 1677721600000000000,
@ -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},
@ -677,4 +677,4 @@ pow5_inv_split_64 = [
Uint128{8599192303405213841, 224711641857789488}, Uint128{8599192303405213841, 224711641857789488},
Uint128{14258051472207991719, 179769313486231590} Uint128{14258051472207991719, 179769313486231590}
] ]
) )

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

@ -16,11 +16,12 @@ const (
) )
pub struct Checker { pub struct Checker {
table &table.Table table &table.Table
mut: 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 := ''
field_names << field_name if is_short_syntax {
expr,_ := p.expr(0)
exprs << expr
}
else {
field_name = p.check_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
field := sym.find_field(field_name) or { if is_short_syntax {}
p.error('field `${sym.name}.$field_name` not found') else {
continue field := sym.find_field(field_name) or {
p.error('field `${sym.name}.$field_name` not found')
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()
} }
p.check(.key_struct) if p.tok.kind == .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

@ -56,7 +56,7 @@ pub fn (t mut Table) register_global(name string, typ Type) {
// mod: p.mod // mod: p.mod
// is_mut: true // is_mut: true
// idx: -1 // idx: -1
} }
} }
@ -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