cgen: if/match expressions and other fixes

pull/4053/head
Alexander Medvednikov 2020-03-18 08:41:49 +01:00
parent 3fecf154aa
commit a011b8951a
10 changed files with 134 additions and 48 deletions

View File

@ -94,6 +94,7 @@ fn parse_flags(flag string, f mut flag.Instance, prefs mut flag.MainCmdPreferenc
println('V error: Error parsing flag. Expected value for `-$flag`.')
exit(1)
}
return
}
}
}

View File

@ -313,6 +313,12 @@ fn (m map) get(key string, out voidptr) bool {
return false
}
// TODO
/*
fn (m &map) get2(key string, out voidptr) voidptr {
}
*/
fn (m map) exists(key string) bool {
if m.value_bytes == 0 {
return false

View File

@ -10,15 +10,15 @@ import (
pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl
pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral |
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr |
AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr |
CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr |
pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral |
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr |
AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr |
CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr |
ConcatExpr | Type | AsCast
pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt |
ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt |
HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt |
pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt |
ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt |
HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt |
LineComment | MultiLineComment | AssertStmt | UnsafeStmt | GoStmt
// pub type Type = StructType | ArrayType
// pub struct StructType {
@ -340,6 +340,7 @@ pub:
left Expr // `a` in `a := if ...`
pos token.Position
mut:
is_expr bool
typ table.Type
has_else bool
}
@ -351,7 +352,8 @@ pub:
branches []MatchBranch
pos token.Position
mut:
is_expr bool // returns a value
is_expr bool // returns a value
return_type table.Type
expr_type table.Type // type of `x` in `match x {`
is_sum_type bool
}
@ -591,6 +593,7 @@ pub:
pub struct SizeOf {
pub:
typ table.Type
type_name string
}
@ -641,6 +644,7 @@ enum BinaryOp {
}
*/
[inline]
pub fn expr_is_blank_ident(expr Expr) bool {
match expr {
@ -656,14 +660,13 @@ pub fn expr_is_blank_ident(expr Expr) bool {
[inline]
pub fn expr_is_call(expr Expr) bool {
return match expr {
CallExpr {
CallExpr{
true
}
MethodCallExpr {
MethodCallExpr{
true
}
else {
false
}
false}
}
}

View File

@ -270,6 +270,7 @@ pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type {
// TODO: clean this up, remove dupe code & consider merging method/fn call everywhere
pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr) table.Type {
c.expected_type = table.void_type
typ := c.expr(method_call_expr.expr)
method_call_expr.expr_type = typ
typ_sym := c.table.get_type_symbol(typ)
@ -492,7 +493,7 @@ pub fn (c mut Checker) array_init(array_init mut ast.ArrayInit) table.Type {
else {
c.error('expecting `int` for fixed size', array_init.pos)
}
}
}
idx := c.table.find_or_register_array_fixed(array_init.elem_type, fixed_size, 1)
array_type := table.new_type(idx)
array_init.typ = array_type
@ -525,6 +526,7 @@ fn (c mut Checker) stmt(node ast.Stmt) {
c.expr(it.expr)
}
ast.FnDecl {
c.expected_type = table.void_type
c.fn_return_type = it.return_type
for stmt in it.stmts {
c.stmt(stmt)
@ -815,14 +817,25 @@ pub fn (c mut Checker) match_expr(node mut ast.MatchExpr) table.Type {
}
}
}
// if ret_type != table.void_type {
// node.is_expr = c.expected_type != table.void_type
// node.expected_type = c.expected_type
// }
node.return_type = ret_type
node.expr_type = expr_type
// println('!m $expr_type')
return ret_type
}
pub fn (c mut Checker) if_expr(node mut ast.IfExpr) table.Type {
if c.expected_type != 0 {
// sym := c.table.get_type_symbol(c.expected_type)
// println('$c.file.path $node.pos.line_nr IF: checker exp type = ' + sym.name)
node.is_expr = true
}
typ := c.expr(node.cond)
node.typ = typ
node.typ = table.void_type
// node.typ = typ
typ_sym := c.table.get_type_symbol(typ)
// if typ_sym.kind != .bool {
if table.type_idx(typ) != table.bool_type_idx {

View File

@ -22,6 +22,7 @@ mut:
is_array_set bool
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
optionals []string // to avoid duplicates TODO perf, use map
inside_ternary bool // ?: comma separated statements on a single line
}
pub fn cgen(files []ast.File, table &table.Table) string {
@ -116,7 +117,7 @@ pub fn (g mut Gen) write_typedef_types() {
if !info.has_decl && !info.is_anon {
fn_name := func.name.replace('.', '__')
g.definitions.write('typedef ${g.typ(func.return_type)} (*$fn_name)(')
for i,arg in func.args {
for i, arg in func.args {
g.definitions.write(g.typ(arg.typ))
if i < func.args.len - 1 {
g.definitions.write(',')
@ -191,7 +192,9 @@ pub fn (g mut Gen) reset_tmp_count() {
fn (g mut Gen) stmts(stmts []ast.Stmt) {
for stmt in stmts {
g.stmt(stmt)
g.writeln('')
if !g.inside_ternary {
g.writeln('')
}
}
}
@ -245,7 +248,9 @@ fn (g mut Gen) stmt(node ast.Stmt) {
// no ; after an if expression
ast.IfExpr {}
else {
g.writeln(';')
if !g.inside_ternary {
g.writeln(';')
}
}
}
}
@ -342,7 +347,7 @@ fn (g mut Gen) stmt(node ast.Stmt) {
// use instead of expr() when you need to cast to sum type (can add other casts also)
fn (g mut Gen) expr_with_cast(got_type table.Type, exp_type table.Type, expr ast.Expr) {
// cast to sum type
if exp_type != table.void_type && exp_type != 0 && got_type != 0{
if exp_type != table.void_type && exp_type != 0 && got_type != 0 {
exp_sym := g.table.get_type_symbol(exp_type)
if exp_sym.kind == .sum_type {
sum_info := exp_sym.info as table.SumType
@ -427,7 +432,8 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.write(' = ')
if !is_decl {
g.expr_with_cast(assign_stmt.left_types[i], ident_var_info.typ, val)
} else {
}
else {
g.expr(val)
}
}
@ -811,7 +817,8 @@ fn (g mut Gen) expr(node ast.Expr) {
*/
ast.SizeOf {
g.write('sizeof($it.type_name)')
styp := g.typ(it.typ)
g.write('sizeof($styp)')
}
ast.StringLiteral {
// In C calls we have to generate C strings
@ -966,44 +973,65 @@ fn (g mut Gen) match_expr(node ast.MatchExpr) {
g.writeln('// match 0')
return
}
is_expr := node.return_type != table.void_type
if is_expr {
g.inside_ternary = true
// g.write('/* EM ret type=${g.typ(node.return_type)} */')
}
type_sym := g.table.get_type_symbol(node.expr_type)
mut tmp := ''
if type_sym.kind != .void {
tmp = g.new_tmp_var()
}
//styp := g.typ(node.expr_type)
//g.write('$styp $tmp = ')
//g.expr(node.cond)
//g.writeln(';') // $it.blocks.len')
// styp := g.typ(node.expr_type)
// g.write('$styp $tmp = ')
// g.expr(node.cond)
// g.writeln(';') // $it.blocks.len')
// mut sum_type_str = ''
for j, branch in node.branches {
if j == node.branches.len - 1 {
// last block is an `else{}`
g.writeln('else {')
if is_expr {
// TODO too many branches. maybe separate ?: matches
g.write(' : ')
}
else {
g.writeln('else {')
}
}
else {
if j > 0 {
g.write('else ')
if is_expr {
g.write(' : ')
}
else {
g.write('else ')
}
}
if is_expr {
g.write('(')
}
else {
g.write('if (')
}
g.write('if (')
for i, expr in branch.exprs {
if node.is_sum_type {
g.expr(node.cond )
g.expr(node.cond)
g.write('.typ == ')
//g.write('${tmp}.typ == ')
// g.write('${tmp}.typ == ')
// sum_type_str
}
else if type_sym.kind == .string {
g.write('string_eq(')
//
g.expr(node.cond)
g.write(', ')
//g.write('string_eq($tmp, ')
g.expr(node.cond)
g.write(', ')
// g.write('string_eq($tmp, ')
}
else {
g.expr(node.cond)
g.write(' == ')
//g.write('$tmp == ')
// g.write('$tmp == ')
}
g.expr(expr)
if type_sym.kind == .string {
@ -1013,7 +1041,12 @@ g.write(', ')
g.write(' || ')
}
}
g.writeln(') {')
if is_expr {
g.write(') ? ')
}
else {
g.writeln(') {')
}
}
if node.is_sum_type && branch.exprs.len > 0 {
// The first node in expr is an ast.Type
@ -1022,7 +1055,10 @@ g.write(', ')
match fe {
ast.Type {
it_type := g.typ(it.typ)
g.writeln('$it_type* it = ($it_type*)${tmp}.obj; // ST it')
// g.writeln('$it_type* it = ($it_type*)${tmp}.obj; // ST it')
g.write('$it_type* it = ($it_type*)')
g.expr(node.cond)
g.writeln('.obj; // ST it')
}
else {
verror('match sum type')
@ -1030,8 +1066,11 @@ g.write(', ')
}
}
g.stmts(branch.stmts)
g.writeln('}')
if !g.inside_ternary {
g.writeln('}')
}
}
g.inside_ternary = false
}
fn (g mut Gen) ident(node ast.Ident) {
@ -1067,12 +1106,13 @@ fn (g mut Gen) if_expr(node ast.IfExpr) {
}
// one line ?:
// TODO clean this up once `is` is supported
if node.stmts.len == 1 && node.else_stmts.len == 1 && type_sym.kind != .void {
if node.is_expr && node.stmts.len == 1 && node.else_stmts.len == 1 && type_sym.kind != .void {
cond := node.cond
stmt1 := node.stmts[0]
else_stmt1 := node.else_stmts[0]
match stmt1 {
ast.ExprStmt {
g.inside_ternary = true
g.expr(cond)
g.write(' ? ')
expr_stmt := stmt1 as ast.ExprStmt
@ -1081,7 +1121,7 @@ fn (g mut Gen) if_expr(node ast.IfExpr) {
g.stmt(else_stmt1)
}
else {}
}
}
}
else {
mut is_guard := false
@ -1096,11 +1136,12 @@ fn (g mut Gen) if_expr(node ast.IfExpr) {
g.writeln('if (($guard_ok = ${it.var_name}.ok)) {')
}
else {
g.inside_ternary = false
g.write('if (')
g.expr(node.cond)
g.writeln(') {')
}
}
}
for i, stmt in node.stmts {
// Assign ret value
if i == node.stmts.len - 1 && type_sym.kind != .void {}
@ -1113,8 +1154,9 @@ fn (g mut Gen) if_expr(node ast.IfExpr) {
g.writeln('}')
if node.else_stmts.len > 0 {
if is_guard {
g.writeln('if !$guard_ok { /* else */')
} else {
g.writeln('if (!$guard_ok) { /* else */')
}
else {
g.writeln('else { ')
}
for stmt in node.else_stmts {
@ -1123,6 +1165,7 @@ fn (g mut Gen) if_expr(node ast.IfExpr) {
g.writeln('}')
}
}
g.inside_ternary = false
}
fn (g mut Gen) index_expr(node ast.IndexExpr) {
@ -1210,6 +1253,10 @@ fn (g mut Gen) index_expr(node ast.IndexExpr) {
fn (g mut Gen) return_statement(it ast.Return) {
g.write('return')
if g.fn_decl.name == 'main' {
g.writeln(' 0;')
return
}
// multiple returns
if it.exprs.len > 1 {
typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)

View File

@ -55,8 +55,8 @@ fn compare_texts(a, b, path string) bool {
println('${path}: got\n$a')
println('${term_fail} ${i}')
println(term.red('i=$i "$line_a" expected="$line_b"'))
// println(lines_b[i + 1])
// println(lines_b[i + 2])
println(lines_b[i + 1])
println(lines_b[i + 2])
// exit(1)
return false
}

View File

@ -149,7 +149,6 @@ multi_return_int_string multi_return() {
void variadic(varg_int a) {
int x = path_sep;
int y = true ? 1 : 0;
;
}
void ensure_cap(int required, int cap) {
@ -172,6 +171,9 @@ void matches() {
else {
}
;
string x = (a == 10) ? tos3("ten") : (a == 30) ? tos3("thirty") : tos3("unknown");
int xx = (a == 10) ? 100 : (a == 30) ? 300 : 0;
println((a == 10) ? tos3("ten") : tos3("not ten"));
}
//10

View File

@ -156,6 +156,17 @@ fn matches() {
}
else{}
}
x := match a {
10 { 'ten' }
30 { 'thirty' }
else { 'unknown' }
}
xx := match a {
10 { 100 }
30 { 300 }
else { 0 }
}
println(match a { 10 { 'ten' } else { 'not ten' } })
/*
n := match a {
1 { 10 }

View File

@ -40,11 +40,11 @@ void println(string s) {
void handle_expr(Expr e) {
if (e.typ == _type_idx_IfExpr) {
IfExpr* it = (IfExpr*)tmp1.obj; // ST it
IfExpr* it = (IfExpr*)e.obj; // ST it
println(tos3("if"));
}
else if (e.typ == _type_idx_IntegerLiteral) {
IntegerLiteral* it = (IntegerLiteral*)tmp1.obj; // ST it
IntegerLiteral* it = (IntegerLiteral*)e.obj; // ST it
println(tos3("integer"));
}
else {

View File

@ -748,10 +748,13 @@ pub fn (p mut Parser) expr(precedence int) ast.Expr {
if p.tok.kind == .amp {
p.next()
}
type_name := p.check_name()
// type_name := p.check_name()
sizeof_type := p.parse_type()
p.check(.rpar)
node = ast.SizeOf{
type_name: type_name
typ: sizeof_type
// type_name: type_name
}
}
// Map `{"age": 20}` or `{ x | foo:bar, a:10 }`