From 5ba354fa2caa060c7c9397eafd00ce453c60b7ee Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 1 Oct 2019 06:33:03 +0300 Subject: [PATCH] `if a := foo() {` syntax for handling optionals --- compiler/gen_c.v | 7 +- compiler/parser.v | 147 +++++++++++++++++++++++++++-------- compiler/table.v | 3 +- compiler/tests/option_test.v | 8 ++ compiler/token.v | 9 +-- vlib/builtin/hashmap.v | 4 + 6 files changed, 137 insertions(+), 41 deletions(-) diff --git a/compiler/gen_c.v b/compiler/gen_c.v index 61e2aced7c..038ad578cb 100644 --- a/compiler/gen_c.v +++ b/compiler/gen_c.v @@ -6,6 +6,11 @@ const ( dot_ptr = '->' ) +/* +fn (p mut Parser) gen_or_else(pos int) string { +} +*/ + // returns the type of the new variable fn (p mut Parser) gen_var_decl(name string, is_static bool) string { // Generate expression to tmp because we need its type first @@ -69,7 +74,7 @@ fn (p mut Parser) gen_fn_decl(f Fn, typ, str_args string) { p.genln('$dll_export_linkage$typ $fn_name_cgen($str_args) {') } -// blank identifer assignment `_ = 111` +// blank identifer assignment `_ = 111` fn (p mut Parser) gen_blank_identifier_assign() { assign_error_tok_idx := p.token_idx p.check_name() diff --git a/compiler/parser.v b/compiler/parser.v index 47fd80e6a9..1a391717f9 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -473,10 +473,10 @@ fn (p mut Parser) import_statement() { } fn (p mut Parser) const_decl() { - if p.tok == .key_import { + if p.tok == .key_import { p.error_with_token_index( '`import const` was removed from the language, ' + - 'because predeclaring C constants is not needed anymore. ' + + 'because predeclaring C constants is not needed anymore. ' + 'You can use them directly with C.CONST_NAME', p.cur_tok_index() ) @@ -1651,39 +1651,15 @@ fn (p mut Parser) name_expr() string { } return p.expected_type } - // ////////////////////////// - // module ? - if p.peek() == .dot && ((name == p.mod && p.table.known_mod(name)) || - p.import_table.known_alias(name)) && !is_c && - !p.known_var(name) // Allow shadowing (`gg = gg.newcontext(); gg.foo()`) - { - mut mod := name - // must be aliased module - if name != p.mod && p.import_table.known_alias(name) { - p.import_table.register_used_import(name) - // we replaced "." with "_dot_" in p.mod for C variable names, - // do same here. - mod = p.import_table.resolve_alias(name).replace('.', '_dot_') - } - p.next() - p.check(.dot) - name = p.lit - p.fgen(name) - name = prepend_mod(mod, name) - } - else if !p.table.known_type(name) && !p.known_var(name) && - !p.table.known_fn(name) && !p.table.known_const(name) && !is_c - { - name = p.prepend_mod(name) - } - // Variable + // Variable, checked before modules, so module shadowing is allowed. + // (`gg = gg.newcontext(); gg.draw_rect(...)`) for { // TODO remove + mut v := p.find_var_check_new_var(name) or { break } if name == '_' { p.error('cannot use `_` as value') } - mut v := p.find_var_check_new_var(name) or { break } if ptr { - p.gen('& /*v*/ ') + p.gen('&') } else if deref { p.gen('*') @@ -1716,6 +1692,73 @@ fn (p mut Parser) name_expr() string { } return typ } // TODO REMOVE for{} + // Module? + if p.peek() == .dot && ((name == p.mod && p.table.known_mod(name)) || + p.import_table.known_alias(name)) && !is_c { + mut mod := name + // must be aliased module + if name != p.mod && p.import_table.known_alias(name) { + p.import_table.register_used_import(name) + // we replaced "." with "_dot_" in p.mod for C variable names, + // do same here. + mod = p.import_table.resolve_alias(name).replace('.', '_dot_') + } + p.next() + p.check(.dot) + name = p.lit + p.fgen(name) + name = prepend_mod(mod, name) + } + // Unknown name, try prepending the module name to it + // TODO perf + else if !p.table.known_type(name) && + !p.table.known_fn(name) && !p.table.known_const(name) && !is_c + { + name = p.prepend_mod(name) + } + + // Variable, checked before modules, so module shadowing is allowed. + // (`gg = gg.newcontext(); gg.draw_rect(...)`) + for { // TODO remove + mut v := p.find_var_check_new_var(name) or { break } + if name == '_' { + p.error('cannot use `_` as value') + } + if ptr { + p.gen('&') + } + else if deref { + p.gen('*') + } + if p.pref.autofree && v.typ == 'string' && v.is_arg && + p.assigned_type == 'string' { + p.warn('setting moved ' + v.typ) + p.mark_arg_moved(v) + } + mut typ := p.var_expr(v) + // *var + if deref { + if !typ.contains('*') && !typ.ends_with('ptr') { + println('name="$name", t=$v.typ') + p.error('dereferencing requires a pointer, but got `$typ`') + } + typ = typ.replace('ptr', '')// TODO + typ = typ.replace('*', '')// TODO + } + // &var + else if ptr { + typ += '*' + } + if p.inside_return_expr { + //println('marking $v.name returned') + p.mark_var_returned(v) + // v.is_returned = true // TODO modifying a local variable + // that's not used afterwards, this should be a compilation + // error + } + return typ + } // TODO REMOVE for{} + // if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) { // known type? int(4.5) or Color.green (enum) if p.table.known_type(name) { @@ -1970,7 +2013,7 @@ fn (p mut Parser) dot(str_typ_ string, method_ph int) string { //} mut str_typ := str_typ_ p.check(.dot) - is_variadic_arg := str_typ.starts_with('...') + is_variadic_arg := str_typ.starts_with('...') if is_variadic_arg { str_typ = str_typ.right(3) } mut typ := p.find_type(str_typ) if typ.name.len == 0 { @@ -3133,6 +3176,14 @@ fn (p mut Parser) get_tmp_counter() int { return p.tmp_cnt } +// returns expression's type, and entire expression's string representation) +fn (p mut Parser) tmp_expr() (string, string) { + p.cgen.start_tmp() + typ := p.bool_expression() + val := p.cgen.end_tmp() + return typ, val +} + fn (p mut Parser) if_st(is_expr bool, elif_depth int) string { if is_expr { //if p.fileis('if_expr') { @@ -3146,7 +3197,36 @@ fn (p mut Parser) if_st(is_expr bool, elif_depth int) string { p.fgen('if ') } p.next() - p.check_types(p.bool_expression(), 'bool') + // `if a := opt() { }` syntax + if p.tok == .name && p.peek() == .decl_assign { + option_tmp := p.get_tmp() + var_name := p.lit + p.next() + p.check(.decl_assign) + option_type, expr := p.tmp_expr()// := p.bool_expression() + typ := option_type.right(7) + // Option_User tmp = get_user(1); + // if (tmp.ok) { + // User user = *(User*)tmp.data; + // [statements] + // } + p.cgen.insert_before('$option_type $option_tmp = $expr; ') + p.check(.lcbr) + p.genln(option_tmp + '.ok) {') + p.genln('$typ $var_name = *($typ*) $option_tmp . data;') + p.register_var(Var { + name: var_name + typ: typ + is_mut: false // TODO + //is_alloc: p.is_alloc || typ.starts_with('array_') + //line_nr: p.tokens[ var_token_idx ].line_nr + //token_idx: var_token_idx + }) + p.statements() + return 'void' + } else { + p.check_types(p.bool_expression(), 'bool') + } if is_expr { p.gen(') ? (') } @@ -3731,7 +3811,8 @@ fn (p mut Parser) return_st() { } p.inside_return_expr = false // Automatically wrap an object inside an option if the function - // returns an option + // returns an option: + // `return val` => `return opt_ok(val)` if p.cur_fn.typ.ends_with(expr_type) && !is_none && p.cur_fn.typ.starts_with('Option_') { tmp := p.get_tmp() diff --git a/compiler/table.v b/compiler/table.v index 6fd52f62d6..ca5a0b47fc 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -351,12 +351,11 @@ fn (table &Table) known_type(typ_ string) bool { fn (table &Table) known_type_fast(t &Type) bool { return t.name != '' && !t.is_placeholder - } fn (t &Table) find_fn(name string) ?Fn { f := t.fns[name] - if !isnil(f.name.str) { + if f.name.str != 0 { // TODO return f } return none diff --git a/compiler/tests/option_test.v b/compiler/tests/option_test.v index 8342d12be7..780f55fbae 100644 --- a/compiler/tests/option_test.v +++ b/compiler/tests/option_test.v @@ -34,3 +34,11 @@ fn test_option_for_base_type_without_variable() { println('nice') println(val2) } + +fn test_if_opt() { + if val := err_call(false) { + assert val == 42 + } + assert 1 == 1 + println('nice') +} diff --git a/compiler/token.v b/compiler/token.v index 2ce83c18ff..bb92a0148b 100644 --- a/compiler/token.v +++ b/compiler/token.v @@ -35,11 +35,10 @@ enum Token { left_shift righ_shift //at // @ - // = := += -= - assign - decl_assign - plus_assign - minus_assign + assign // = + decl_assign // := + plus_assign // += + minus_assign // -= div_assign mult_assign xor_assign diff --git a/vlib/builtin/hashmap.v b/vlib/builtin/hashmap.v index c0ebb5a1ab..65fcb1af10 100644 --- a/vlib/builtin/hashmap.v +++ b/vlib/builtin/hashmap.v @@ -8,6 +8,10 @@ module builtin This is work in progress. A very early test version of the hashmap with a fixed size. Only works with string keys and int values for now. + + I added this to improve performance of the V compiler, + which uses lots of O(log n) map get's. Turned out with N < 10 000 + the performance gains are basically non-existent. */ import math