parser: return multi expr

pull/4905/head
Tanel Liiv 2020-05-16 00:14:53 +03:00 committed by GitHub
parent 7caebc5781
commit 44502a3fb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 231 additions and 85 deletions

View File

@ -755,6 +755,8 @@ pub:
pub struct ConcatExpr {
pub:
vals []Expr
pub mut:
return_type table.Type
}
pub struct None {

View File

@ -1588,6 +1588,13 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) {
pub fn (mut c Checker) expr(node ast.Expr) table.Type {
match mut node {
ast.AnonFn {
keep_ret_type := c.fn_return_type
c.fn_return_type = it.decl.return_type
c.stmts(it.decl.stmts)
c.fn_return_type = keep_ret_type
return it.typ
}
ast.ArrayInit {
return c.array_init(mut it)
}
@ -1645,6 +1652,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
ast.CharLiteral {
return table.byte_type
}
ast.ConcatExpr {
return c.concat_expr(mut it)
}
ast.EnumVal {
return c.enum_val(mut it)
}
@ -1730,13 +1740,6 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
it.expr_type = c.expr(it.expr)
return table.string_type
}
ast.AnonFn {
keep_ret_type := c.fn_return_type
c.fn_return_type = it.decl.return_type
c.stmts(it.decl.stmts)
c.fn_return_type = keep_ret_type
return it.typ
}
else {
tnode := typeof(node)
if tnode != 'unknown v.ast.Expr' {
@ -1858,6 +1861,23 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
return table.void_type
}
pub fn (mut c Checker) concat_expr(concat_expr mut ast.ConcatExpr) table.Type {
mut mr_types := []table.Type{}
for expr in concat_expr.vals {
mr_types << c.expr(expr)
}
if concat_expr.vals.len == 1 {
typ := mr_types[0]
concat_expr.return_type = typ
return typ
} else {
typ := c.table.find_or_register_multi_return(mr_types)
table.new_type(typ)
concat_expr.return_type = typ
return typ
}
}
pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
node.is_expr = c.expected_type != table.void_type
node.expected_type = c.expected_type

View File

@ -1163,6 +1163,18 @@ fn (g &Gen) autofree_var_call(free_fn_name string, v ast.Var) string {
fn (mut g Gen) expr(node ast.Expr) {
// println('cgen expr() line_nr=$node.pos.line_nr')
match node {
ast.AnonFn {
// TODO: dont fiddle with buffers
pos := g.out.len
def_pos := g.definitions.len
g.stmt(it.decl)
fn_body := g.out.after(pos)
g.out.go_back(fn_body.len)
g.definitions.go_back(g.definitions.len - def_pos)
g.definitions.write(fn_body)
fsym := g.table.get_type_symbol(it.typ)
g.write('&${fsym.name}')
}
ast.ArrayInit {
g.array_init(it)
}
@ -1226,6 +1238,9 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.CharLiteral {
g.write("'$it.val'")
}
ast.ConcatExpr {
g.concat_expr(it)
}
ast.EnumVal {
// g.write('${it.mod}${it.enum_name}_$it.val')
styp := g.typ(it.typ)
@ -1375,18 +1390,6 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.TypeOf {
g.typeof_expr(it)
}
ast.AnonFn {
// TODO: dont fiddle with buffers
pos := g.out.len
def_pos := g.definitions.len
g.stmt(it.decl)
fn_body := g.out.after(pos)
g.out.go_back(fn_body.len)
g.definitions.go_back(g.definitions.len - def_pos)
g.definitions.write(fn_body)
fsym := g.table.get_type_symbol(it.typ)
g.write('&${fsym.name}')
}
else {
// #printf("node=%d\n", node.typ);
println(term.red('cgen.expr(): bad node ' + typeof(node)))
@ -1524,7 +1527,7 @@ fn (mut g Gen) assign_expr(node ast.AssignExpr) {
g.or_block(tmp_opt, or_stmts, return_type)
unwrapped_type_str := g.typ(return_type.set_flag(.unset))
ident := node.left as ast.Ident
if ident.info is ast.IdentVar {
if ident.kind != .blank_ident && ident.info is ast.IdentVar {
ident_var := ident.info as ast.IdentVar
if ident_var.is_optional {
// var is already an optional, just copy the value
@ -1847,6 +1850,26 @@ fn (mut g Gen) ident(node ast.Ident) {
g.write(g.get_ternary_name(name))
}
fn (mut g Gen) concat_expr(node ast.ConcatExpr) {
styp := g.typ(node.return_type)
sym := g.table.get_type_symbol(node.return_type)
is_multi := sym.kind == .multi_return
if !is_multi {
g.expr(node.vals[0])
} else {
g.write('($styp){')
for i, expr in node.vals {
g.write('.arg$i=')
g.expr(expr)
if i < node.vals.len - 1 {
g.write(',')
}
}
g.write('}')
}
}
fn (mut g Gen) if_expr(node ast.IfExpr) {
if node.is_expr || g.inside_ternary != 0 {
g.inside_ternary++

View File

@ -6,13 +6,21 @@ module parser
import v.ast
fn (mut p Parser) assign_stmt() ast.Stmt {
is_static := p.tok.kind == .key_static
if is_static {
p.next()
return p.partial_assign_stmt([])
}
fn (mut p Parser) partial_assign_stmt(known_lhs []ast.Ident) ast.Stmt {
mut idents := known_lhs
mut op := p.tok.kind
// read (more) idents until assignment sign
for op !in [.decl_assign, .assign] {
idents << p.parse_assign_ident()
if p.tok.kind == .comma {
p.next()
}
op = p.tok.kind
}
idents := p.parse_assign_lhs()
op := p.tok.kind
p.next() // :=, =
p.next()
pos := p.tok.position()
exprs := p.parse_assign_rhs()
is_decl := op == .decl_assign
@ -46,7 +54,7 @@ fn (mut p Parser) assign_stmt() ast.Stmt {
right: exprs
op: op
pos: pos
is_static: is_static
is_static: false // individual idents may be static
}
}
@ -72,31 +80,9 @@ pub fn (mut p Parser) assign_expr(left ast.Expr) ast.AssignExpr {
return node
}
fn (mut p Parser) parse_assign_lhs() []ast.Ident {
mut idents := []ast.Ident{}
for {
is_mut := p.tok.kind == .key_mut
if is_mut {
p.next()
}
is_static := p.tok.kind == .key_static
if is_static {
p.next()
}
mut ident := p.parse_ident(false, false)
ident.is_mut = is_mut
ident.info = ast.IdentVar{
is_mut: is_mut
is_static: is_static
}
idents << ident
if p.tok.kind == .comma {
p.next()
} else {
break
}
}
return idents
fn (mut p Parser) parse_assign_ident() ast.Ident {
/// returns a single parsed ident
return p.parse_ident(false, false)
}
// right hand side of `=` or `:=` in `a,b,c := 1,2,3`

View File

@ -447,9 +447,12 @@ pub fn (mut p Parser) stmt() ast.Stmt {
return p.for_stmt()
}
.name {
if p.peek_tok.kind in [.decl_assign, .comma] {
if p.peek_tok.kind == .decl_assign {
// `x := ...`
return p.assign_stmt()
} else if p.peek_tok.kind == .comma {
// `a, b ...`
return p.parse_comma_separated()
} else if p.peek_tok.kind == .colon {
// `label:`
name := p.check_name()
@ -524,15 +527,11 @@ pub fn (mut p Parser) stmt() ast.Stmt {
name: name
}
}
.key_const {
p.error_with_pos('const can only be defined at the top level (outside of functions)', p.tok.position())
}
else {
if p.tok.kind == .key_const {
p.error_with_pos('const can only be defined at the top level (outside of functions)', p.tok.position())
}
epos := p.tok.position()
return ast.ExprStmt{
expr: p.expr(0)
pos: epos
}
return p.parse_comma_separated()
}
}
}
@ -645,27 +644,93 @@ pub fn (mut p Parser) warn_with_pos(s string, pos token.Position) {
}
}
pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident {
// p.warn('name ')
pos := p.tok.position()
mut name := p.check_name()
if name == '_' {
return ast.Ident{
name: '_'
kind: .blank_ident
pos: pos
fn (mut p Parser) parse_comma_separated() ast.Stmt {
// in here might be 1) multi-expr 2) multi-assign
// 1, a, c ... } // multi-expression
// a, mut b ... :=/= // multi-assign
// collect things upto hard boundaries
mut collected := []ast.Expr{}
mut op := p.tok.kind
for op !in [.rcbr, .decl_assign, .assign] {
if op == .name {
collected << p.name_expr()
} else {
collected << p.expr(0)
}
if p.tok.kind == .comma {
p.next()
} else {
break
}
op = p.tok.kind
}
is_assignment := p.tok.kind in [.decl_assign, .assign]
if is_assignment {
mut idents := []ast.Ident{}
for c in collected {
idents << c as ast.Ident
}
return p.partial_assign_stmt(idents)
} else {
if collected.len == 1 {
epos := p.tok.position()
return ast.ExprStmt{
expr: collected[0]
pos: epos
}
}
return ast.ExprStmt{
expr: ast.ConcatExpr {
vals: collected
}
pos: p.tok.position()
}
}
if p.expr_mod.len > 0 {
name = '${p.expr_mod}.$name'
}
pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident {
// p.warn('name ')
is_mut := p.tok.kind == .key_mut
if is_mut {
p.next()
}
return ast.Ident{
kind: .unresolved
name: name
is_c: is_c
is_js: is_js
mod: p.mod
pos: pos
is_static := p.tok.kind == .key_static
if is_static {
p.next()
}
if p.tok.kind == .name {
pos := p.tok.position()
mut name := p.check_name()
if name == '_' {
return ast.Ident{
name: '_'
kind: .blank_ident
pos: pos
info: ast.IdentVar {
is_mut: false
is_static: false
}
}
}
if p.expr_mod.len > 0 {
name = '${p.expr_mod}.$name'
}
mut ident := ast.Ident{
kind: .unresolved
name: name
is_c: is_c
is_js: is_js
mod: p.mod
pos: pos
}
ident.is_mut = is_mut
ident.info = ast.IdentVar{
is_mut: is_mut
is_static: is_static
}
return ident
} else {
p.error('unexpected token `$p.tok.lit`')
}
}

View File

@ -15,6 +15,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
p.is_stmt_ident = false
// Prefix
match p.tok.kind {
.key_mut, .key_static {
node = p.parse_assign_ident()
}
.name {
node = p.name_expr()
p.is_stmt_ident = is_stmt_ident

View File

@ -1,3 +1,7 @@
struct Object {
name string
value int
}
fn multireturner(n int, s string) (int, string) {
return n + 1, s
@ -47,12 +51,55 @@ fn test_assign_multireturn_expression() {
assert i == 1
assert j == 'good'
// TODO: returning non-function calls does not work yet due to parsing issues
/*
e, f := if true {
1, 'awesome'
k, l, m := if true {
1, 'awesome', [13]
} else {
0, 'bad'
0, 'bad', [0]
}
*/
assert k == 1
assert l == 'awesome'
assert m == [13]
n, o, p := if false {
1, 'awesome', [13]
} else {
0, 'bad', [0]
}
assert n == 0
assert o == 'bad'
assert p == [0]
mut var1 := 17
var2 := 'awesome'
q, r, s := if true {
1 + var1, var2, [13]
} else {
0, 'bad', [0]
}
assert q == 18
assert r == 'awesome'
assert s == [13]
val1 := 1
val2 := 0
t, u, v := if true {
val1, 'awesome', [13]
} else {
val2, 'bad', [0]
}
assert t == val1
assert u == 'awesome'
assert v == [13]
val3 := Object { name: 'foo', value: 19 }
x, y, z := if true {
1 + 1, 'awe' + 'some', { val3 | name: 'bar' }
} else {
0, '0', Object {}
}
assert x == 2
assert y == 'awesome'
assert z.name == 'bar'
assert z.value == 19
}