`if a := foo() {` syntax for handling optionals

pull/2204/head
Alexander Medvednikov 2019-10-01 06:33:03 +03:00
parent 56e4ed1e6b
commit 5ba354fa2c
6 changed files with 137 additions and 41 deletions

View File

@ -6,6 +6,11 @@ const (
dot_ptr = '->' dot_ptr = '->'
) )
/*
fn (p mut Parser) gen_or_else(pos int) string {
}
*/
// returns the type of the new variable // returns the type of the new variable
fn (p mut Parser) gen_var_decl(name string, is_static bool) string { fn (p mut Parser) gen_var_decl(name string, is_static bool) string {
// Generate expression to tmp because we need its type first // 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) {') 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() { fn (p mut Parser) gen_blank_identifier_assign() {
assign_error_tok_idx := p.token_idx assign_error_tok_idx := p.token_idx
p.check_name() p.check_name()

View File

@ -473,10 +473,10 @@ fn (p mut Parser) import_statement() {
} }
fn (p mut Parser) const_decl() { fn (p mut Parser) const_decl() {
if p.tok == .key_import { if p.tok == .key_import {
p.error_with_token_index( p.error_with_token_index(
'`import const` was removed from the language, ' + '`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', 'You can use them directly with C.CONST_NAME',
p.cur_tok_index() p.cur_tok_index()
) )
@ -1651,39 +1651,15 @@ fn (p mut Parser) name_expr() string {
} }
return p.expected_type return p.expected_type
} }
// ////////////////////////// // Variable, checked before modules, so module shadowing is allowed.
// module ? // (`gg = gg.newcontext(); gg.draw_rect(...)`)
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
for { // TODO remove for { // TODO remove
mut v := p.find_var_check_new_var(name) or { break }
if name == '_' { if name == '_' {
p.error('cannot use `_` as value') p.error('cannot use `_` as value')
} }
mut v := p.find_var_check_new_var(name) or { break }
if ptr { if ptr {
p.gen('& /*v*/ ') p.gen('&')
} }
else if deref { else if deref {
p.gen('*') p.gen('*')
@ -1716,6 +1692,73 @@ fn (p mut Parser) name_expr() string {
} }
return typ return typ
} // TODO REMOVE for{} } // 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) { // if known_type || is_c_struct_init || (p.first_pass() && p.peek() == .lcbr) {
// known type? int(4.5) or Color.green (enum) // known type? int(4.5) or Color.green (enum)
if p.table.known_type(name) { 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_ mut str_typ := str_typ_
p.check(.dot) 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) } if is_variadic_arg { str_typ = str_typ.right(3) }
mut typ := p.find_type(str_typ) mut typ := p.find_type(str_typ)
if typ.name.len == 0 { if typ.name.len == 0 {
@ -3133,6 +3176,14 @@ fn (p mut Parser) get_tmp_counter() int {
return p.tmp_cnt 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 { fn (p mut Parser) if_st(is_expr bool, elif_depth int) string {
if is_expr { if is_expr {
//if p.fileis('if_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.fgen('if ')
} }
p.next() 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 { if is_expr {
p.gen(') ? (') p.gen(') ? (')
} }
@ -3731,7 +3811,8 @@ fn (p mut Parser) return_st() {
} }
p.inside_return_expr = false p.inside_return_expr = false
// Automatically wrap an object inside an option if the function // 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 && if p.cur_fn.typ.ends_with(expr_type) && !is_none &&
p.cur_fn.typ.starts_with('Option_') { p.cur_fn.typ.starts_with('Option_') {
tmp := p.get_tmp() tmp := p.get_tmp()

View File

@ -351,12 +351,11 @@ fn (table &Table) known_type(typ_ string) bool {
fn (table &Table) known_type_fast(t &Type) bool { fn (table &Table) known_type_fast(t &Type) bool {
return t.name != '' && !t.is_placeholder return t.name != '' && !t.is_placeholder
} }
fn (t &Table) find_fn(name string) ?Fn { fn (t &Table) find_fn(name string) ?Fn {
f := t.fns[name] f := t.fns[name]
if !isnil(f.name.str) { if f.name.str != 0 { // TODO
return f return f
} }
return none return none

View File

@ -34,3 +34,11 @@ fn test_option_for_base_type_without_variable() {
println('nice') println('nice')
println(val2) println(val2)
} }
fn test_if_opt() {
if val := err_call(false) {
assert val == 42
}
assert 1 == 1
println('nice')
}

View File

@ -35,11 +35,10 @@ enum Token {
left_shift left_shift
righ_shift righ_shift
//at // @ //at // @
// = := += -= assign // =
assign decl_assign // :=
decl_assign plus_assign // +=
plus_assign minus_assign // -=
minus_assign
div_assign div_assign
mult_assign mult_assign
xor_assign xor_assign

View File

@ -8,6 +8,10 @@ module builtin
This is work in progress. This is work in progress.
A very early test version of the hashmap with a fixed size. A very early test version of the hashmap with a fixed size.
Only works with string keys and int values for now. 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 import math