From 857cbfb0d24ba4e7b32678e015a70c789faf915e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 26 Feb 2020 15:51:05 +0100 Subject: [PATCH] v2: short struct init syntax; .xxx enum checks; unions; assert --- vlib/compiler/aparser.v | 4 +- vlib/compiler/enum.v | 14 +++---- vlib/compiler/expression.v | 4 +- vlib/compiler/fn.v | 20 +++++----- vlib/strconv/ftoa/f32_str.v | 16 ++++---- vlib/strconv/ftoa/tables.v | 8 ++-- vlib/v/ast/ast.v | 7 +++- vlib/v/checker/checker.v | 38 ++++++++++++------ vlib/v/parser/parser.v | 80 ++++++++++++++++++++++++++++--------- vlib/v/table/table.v | 4 +- 10 files changed, 128 insertions(+), 67 deletions(-) diff --git a/vlib/compiler/aparser.v b/vlib/compiler/aparser.v index c139e019d3..90486d093a 100644 --- a/vlib/compiler/aparser.v +++ b/vlib/compiler/aparser.v @@ -2522,8 +2522,8 @@ fn (p mut Parser) indot_expr() string { if is_map && typ != 'string' { p.error('bad element type: expecting `string`') } - T := p.table.find_type(arr_typ) - if !is_map && !T.has_method('contains') { + arr_typ2 := p.table.find_type(arr_typ) + if !is_map && !arr_typ2.has_method('contains') { p.error('$arr_typ has no method `contains`') } // `typ` is element's type diff --git a/vlib/compiler/enum.v b/vlib/compiler/enum.v index 31da5b74f2..72490ce591 100644 --- a/vlib/compiler/enum.v +++ b/vlib/compiler/enum.v @@ -109,7 +109,7 @@ int typ; // Skip nameless enums else if !no_name && !p.first_pass() { p.cgen.typedefs << 'typedef int $enum_name;' - } + } p.check(.rcbr) 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_') { p.expected_type = p.expected_type[7..] } - T := p.find_type(p.expected_type) - if T.cat == .enum_ { + tt := p.find_type(p.expected_type) + if tt.cat == .enum_ { p.check(.dot) val := p.check_name() // Make sure this enum value exists - if !T.has_enum_val(val) { - p.error('enum `$T.name` does not have value `$val`') + if !tt.has_enum_val(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 { - p.error('`$T.name` is not an enum') + p.error('`$tt.name` is not an enum') } } diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index cf1cb2a642..a6a47bbb34 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -675,8 +675,8 @@ fn (p mut Parser) expression() string { return typ } -fn (p mut Parser) handle_operator(op string, typ string, cpostfix string, ph int, T &Type) { - if T.has_method(op) { +fn (p mut Parser) handle_operator(op string, typ string,cpostfix string, ph int, tt &Type) { + if tt.has_method(op) { p.cgen.set_placeholder(ph, '${typ}_${cpostfix}(') p.gen(')') } diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index f8f6264b73..aecdd6a82a 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -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']) { // - T := p.table.find_type(typ) + tt := p.table.find_type(typ) $if !windows { $if !js { 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 $if !js { - if !T.has_method('str') { + if !tt.has_method('str') { // varg - if T.name.starts_with('varg_') { - p.gen_varg_str(T) + if tt.name.starts_with('varg_') { + p.gen_varg_str(tt) p.cgen.set_placeholder(ph, '${typ}_str(') p.gen(')') continue } // Arrays have automatic `str()` methods - else if T.name.starts_with('array_') { - p.gen_array_str(T) + else if tt.name.starts_with('array_') { + p.gen_array_str(tt) p.cgen.set_placeholder(ph, '${typ}_str(') p.gen(')') continue } // struct - else if T.cat == .struct_ { - p.gen_struct_str(T) + else if tt.cat == .struct_ { + p.gen_struct_str(tt) p.cgen.set_placeholder(ph, '${typ}_str(') p.gen(')') continue } else { - base := p.base_type(T.name) - if base != T.name { + base := p.base_type(tt.name) + if base != tt.name { base_type := p.find_type(base) if base_type.has_method('str') { p.cgen.set_placeholder(ph, '${base_type.name}_str(') diff --git a/vlib/strconv/ftoa/f32_str.v b/vlib/strconv/ftoa/f32_str.v index 37d09f37d4..5ea6002a53 100644 --- a/vlib/strconv/ftoa/f32_str.v +++ b/vlib/strconv/ftoa/f32_str.v @@ -1,6 +1,6 @@ /********************************************************************** * -* f32 to string +* f32 to string * * Copyright (c) 2019-2020 Dario Deledda. All rights reserved. * Use of this source code is governed by an MIT license @@ -9,11 +9,11 @@ * This file contains the f32 to string functions * * These functions are based on the work of: -* Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN -* Conference on Programming Language Design and ImplementationJune 2018 +* Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN +* Conference on Programming Language Design and ImplementationJune 2018 * Pages 270–282 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 * **********************************************************************/ @@ -72,7 +72,7 @@ fn (d Dec32) get_string_32(neg bool, n_digit int) string { mut x := 0 for x < (out_len-disp-1) { buf[y - x] = `0` + byte(out%10) - out /= 10 + out /= 10 i++ 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 := u32(4 * m2 - 1 - mm_shift) - mut vr := u32(0) + mut vr := u32(0) mut vp := u32(0) mut vm := u32(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) } - 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 @@ -316,7 +316,7 @@ pub fn f32_to_str(f f32, n_digit int) string { //println("with exp form") d = f32_to_decimal(mant, exp) } - + //println("${d.m} ${d.e}") return d.get_string_32(neg, n_digit) } diff --git a/vlib/strconv/ftoa/tables.v b/vlib/strconv/ftoa/tables.v index 71a07655d1..eaa114c976 100644 --- a/vlib/strconv/ftoa/tables.v +++ b/vlib/strconv/ftoa/tables.v @@ -28,7 +28,7 @@ powers_of_10 = [ // We only need to find the length of at most 17 digit numbers. ] -pow5_split_32 = [ +pow5_split_32 = [ 1152921504606846976, 1441151880758558720, 1801439850948198400, 2251799813685248000, 1407374883553280000, 1759218604441600000, 2199023255552000000, 1374389534720000000, 1717986918400000000, 2147483648000000000, 1342177280000000000, 1677721600000000000, @@ -55,8 +55,8 @@ pow5_inv_split_32 = [ ] pow5_split_64 =[ - Uint128{u64(0), 72057594037927936}, - Uint128{u64(0), 90071992547409920}, + Uint128{0, 72057594037927936}, + Uint128{0, 90071992547409920}, Uint128{u64(0), 112589990684262400}, Uint128{u64(0), 140737488355328000}, Uint128{u64(0), 87960930222080000}, @@ -677,4 +677,4 @@ pow5_inv_split_64 = [ Uint128{8599192303405213841, 224711641857789488}, Uint128{14258051472207991719, 179769313486231590} ] -) \ No newline at end of file +) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 13197fedd8..aace95be32 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -16,7 +16,7 @@ CastExpr | EnumVal | Assoc | SizeOf | None | MapInit pub type Stmt = VarDecl | GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt | HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt | -LineComment | MultiLineComment +LineComment | MultiLineComment | AssertStmt pub type Type = StructType | ArrayType @@ -469,6 +469,11 @@ pub: expr Expr } +pub struct AssertStmt { +pub: + expr Expr +} + pub struct Assoc { pub: name string diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 5cb3378e1e..9c627f6367 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -16,11 +16,12 @@ const ( ) pub struct Checker { - table &table.Table + table &table.Table mut: - file ast.File - nr_errors int - errors []string + file ast.File + nr_errors int + errors []string + expected_type table.Type } 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_ { 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 { 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 if i == 0 { elem_type = typ + c.expected_type = typ continue } 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) } ast.EnumVal { - typ_idx := c.table.find_type_idx(it.enum_name) // or { - 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 + return c.enum_val(it) } ast.FloatLiteral { return table.f64_type @@ -651,6 +649,20 @@ pub fn (c mut Checker) index_expr(node ast.IndexExpr) table.Type { 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) { c.nr_errors++ print_backtrace() diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 73cc62212b..a093fb1aa4 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -238,6 +238,9 @@ pub fn (p mut Parser) top_stmt() ast.Stmt { .key_enum { return p.enum_decl() } + .key_union { + return p.struct_decl() + } .line_comment { // p.next() return ast.LineComment{ @@ -252,7 +255,7 @@ pub fn (p mut Parser) top_stmt() ast.Stmt { } else { // #printf(""); - p.error('parser: bad top level statement') + p.error('parser: bad top level statement ' + p.tok.str()) return ast.Stmt{} } } @@ -260,6 +263,13 @@ pub fn (p mut Parser) top_stmt() ast.Stmt { pub fn (p mut Parser) stmt() ast.Stmt { match p.tok.kind { + .key_assert { + p.next() + expr,_ := p.expr(0) + return ast.AssertStmt{ + expr: expr + } + } .key_mut { return p.var_decl() } @@ -481,7 +491,7 @@ fn (p mut Parser) struct_init() ast.StructInit { mut field_names := []string mut exprs := []ast.Expr mut i := 0 - // TODO if sym.info is table.Struct + // TODO `if sym.info is table.Struct` mut is_struct := false match sym.info { table.Struct { @@ -489,25 +499,41 @@ fn (p mut Parser) struct_init() ast.StructInit { } 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 { - field_name := p.check_name() - field_names << field_name + mut 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 // p.warn('$sym.name field $field_name') if is_struct { info := sym.info as table.Struct - field := sym.find_field(field_name) or { - p.error('field `${sym.name}.$field_name` not found') - continue + if is_short_syntax {} + else { + 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.check(.colon) - expr,_ := p.expr(0) - exprs << expr + if !is_short_syntax { + p.check(.colon) + expr,_ := p.expr(0) + exprs << expr + } i++ + if p.tok.kind == .comma { + p.check(.comma) + } } node := ast.StructInit{ typ: typ @@ -609,7 +635,8 @@ pub fn (p mut Parser) expr(precedence int) (ast.Expr,table.Type) { } .dot { // .enum_val - node,typ = p.enum_val() + // node,typ = p.enum_val() + node = p.enum_val() } .chartoken { typ = table.byte_type @@ -909,14 +936,25 @@ fn (p &Parser) is_addative() bool { */ // `.green` // `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) 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 } - return node,table.int_type } 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 { is_pub := p.tok.kind == .key_pub if is_pub { 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 if is_c { p.next() // C diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index b419bb972a..6bc9c841e6 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -56,7 +56,7 @@ pub fn (t mut Table) register_global(name string, typ Type) { // mod: p.mod // is_mut: true // idx: -1 - + } } @@ -186,7 +186,7 @@ pub fn (t &Table) get_type_symbol(typ Type) &TypeSymbol { return &t.types[idx] } // 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