From b5fe40624c4bb1b0a0b08fd4ff9dc2f47591ff31 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 28 Dec 2019 14:11:05 +0100 Subject: [PATCH] staments + a sum type fix --- vlib/compiler/expression.v | 10 ++- vlib/compiler/tests/type_test.v | 11 +++ vlib/v/ast/ast.v | 41 +++++++++--- vlib/v/cgen/cgen.v | 56 ++++++++++------ vlib/v/cgen/tests/2.c | 2 + vlib/v/cgen/tests/2.v | 5 +- vlib/v/parser/parser.v | 115 +++++++++++++++++++------------- vlib/v/parser/parser_test.v | 70 ++++++++++--------- vlib/v/token/token.v | 91 +++++++++++++------------ 9 files changed, 244 insertions(+), 157 deletions(-) diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index 6eb094c4b2..44ceb98511 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -47,19 +47,17 @@ fn (p mut Parser) bool_expression() string { println(tok.str()) p.error('expr() returns empty type') } - if p.inside_return_expr { //is_ret { // return a,b hack TODO + if p.inside_return_expr && p.expected_type.contains('_MulRet_') { //is_ret { // return a,b hack TODO expected = p.expected_type } if expected != typ && expected in p.table.sum_types { // TODO perf + //p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type') T := p.table.find_type(typ) if T.parent == expected { - //p.warn('SUM CAST exp=$expected typ=$typ p.exp=$p.expected_type') p.cgen.set_placeholder(start_ph, - //'/*SUM TYPE CAST*/($expected) { .obj = &($typ[]) { ') - '/*SUM TYPE CAST*/($expected) { .obj = memdup(& ') + '/*SUM TYPE CAST2*/($expected) { .obj = memdup( &($typ[]) { ') tt := typ.all_after('_') // TODO - //p.gen('}, .typ = SumType_${tt} }')//${val}_type }') - p.gen(', sizeof($typ) ), .typ = SumType_${tt} }')//${val}_type }') + p.gen('}, sizeof($typ) ), .typ = SumType_${tt} }')//${val}_type }') } } return typ diff --git a/vlib/compiler/tests/type_test.v b/vlib/compiler/tests/type_test.v index 0a0ae2acc4..08ae3455f5 100644 --- a/vlib/compiler/tests/type_test.v +++ b/vlib/compiler/tests/type_test.v @@ -31,6 +31,17 @@ struct BinExpr { } +fn expr1() Expr { + mut e := Expr{} + e = BinExpr{} + return e + //return BinExpr{} +} + +fn expr2() Expr { + return BinExpr{} +} + struct UnaryExpr { } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 649dcc9dac..2abf804b5d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -8,12 +8,16 @@ import ( v.types ) -struct Foo {} - pub type Expr = BinaryExpr | UnaryExpr | IfExpr | StringLiteral | IntegerLiteral | FloatLiteral | -VarDecl | FnDecl | Return +Ident + +pub type Stmt = VarDecl | FnDecl | Return | Module | Import | ExprStmt +// Stand-alone expression in a statement list. +pub struct ExprStmt { +pub: + expr Expr +} -pub type Stmt = Foo // VarDecl pub struct IntegerLiteral { pub: val int @@ -49,8 +53,8 @@ pub: pub struct FnDecl { pub: name string - // stmts []Stmt - exprs []Expr + stmts []Stmt + // exprs []Expr typ types.Type } @@ -84,11 +88,13 @@ pub: pub struct Program { pub: - exprs []Expr - // stmts []Stmt + stmts []Stmt } + // A single identifier -struct Ident { +pub struct Ident { +pub: + name string tok_kind token.TokenKind value string } @@ -145,6 +151,23 @@ pub fn (x Expr) str() string { } } +pub fn (node Stmt) str() string { + match node { + VarDecl { + return it.name + ' = ' + it.expr.str() + } + ExprStmt { + return it.expr.str() + } + FnDecl { + return 'fn ${it.name}() { $it.stmts.len stmts }' + } + else { + return '[unhandled stmt str]' + } + } +} + /* enum BinaryOp { sum diff --git a/vlib/v/cgen/cgen.v b/vlib/v/cgen/cgen.v index b54abff8cb..e888af22be 100644 --- a/vlib/v/cgen/cgen.v +++ b/vlib/v/cgen/cgen.v @@ -13,8 +13,8 @@ pub fn gen(program ast.Program) string { mut g := Gen{ out: strings.new_builder(100) } - for expr in program.exprs { - g.expr(expr) + for stmt in program.stmts { + g.stmt(stmt) g.writeln('') } return (g.out.str()) @@ -30,6 +30,35 @@ pub fn (g mut Gen) writeln(s string) { g.out.writeln(s) } +fn (g mut Gen) stmt(node ast.Stmt) { + match node { + ast.FnDecl { + g.writeln('$it.typ.name ${it.name}() { ') + for stmt in it.stmts { + g.stmt(stmt) + } + g.writeln('}') + } + ast.Return { + g.write('return ') + g.expr(it.expr) + g.writeln(';') + } + ast.VarDecl { + g.write('$it.typ.name $it.name = ') + g.expr(it.expr) + g.writeln(';') + } + ast.ExprStmt { + g.expr(it.expr) + g.writeln(';') + } + else { + verror('stmt bad node') + } + } +} + fn (g mut Gen) expr(node ast.Expr) { // println('cgen expr()') match node { @@ -46,18 +75,6 @@ fn (g mut Gen) expr(node ast.Expr) { ast.StringLiteral { g.write('tos3("$it.val")') } - ast.FnDecl { - g.writeln('$it.typ.name ${it.name}() { ') - for expr in it.exprs { - g.expr(expr) - } - g.writeln('}') - } - ast.Return { - g.write('return ') - g.expr(it.expr) - g.writeln(';') - } ast.BinaryExpr { g.expr(it.left) match it.op { @@ -79,17 +96,18 @@ fn (g mut Gen) expr(node ast.Expr) { else {} } g.expr(it.right) + // if it.op in [.plus_assign] { + // g.writeln(';') + // } // if typ.name != typ2.name { // verror('bad types $typ.name $typ2.name') // } } - ast.VarDecl { - g.write('$it.typ.name $it.name = ') - g.expr(it.expr) - g.writeln(';') + ast.Ident { + g.write('$it.name') } else { - println('bad node') + println('cgen.expr(): bad node') } } } diff --git a/vlib/v/cgen/tests/2.c b/vlib/v/cgen/tests/2.c index 55f6ec6e67..cb29fee31b 100644 --- a/vlib/v/cgen/tests/2.c +++ b/vlib/v/cgen/tests/2.c @@ -1,5 +1,6 @@ int function1() { int a = 10 + 1; + int b = a + 1; return 0; } @@ -8,4 +9,5 @@ void function2() { f64 f = 10.1; string s = tos3("hi"); int m = 10; + m += 10; } diff --git a/vlib/v/cgen/tests/2.v b/vlib/v/cgen/tests/2.v index ed936972ad..052cfef3e3 100644 --- a/vlib/v/cgen/tests/2.v +++ b/vlib/v/cgen/tests/2.v @@ -1,5 +1,6 @@ fn function1() int { a := 10 + 1 + b := a + 1 return 0 } @@ -8,7 +9,7 @@ fn function2() { x := 0 f := 10.1 s := 'hi' - mut m := 10 - //m += 10 + m := 10 + m += 10 //c := 0 } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index d69dcadba9..00be54da1d 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -21,7 +21,7 @@ mut: return_type types.Type } -pub fn parse_expr(text string, table &table.Table) ast.Expr { +pub fn parse_stmt(text string, table &table.Table) ast.Stmt { s := scanner.new_scanner(text) mut p := Parser{ scanner: s @@ -29,8 +29,7 @@ pub fn parse_expr(text string, table &table.Table) ast.Expr { } p.next() p.next() - expr,_ := p.expr(token.lowest_prec) - return expr + return p.stmt() } pub fn (p mut Parser) get_type() types.Type { @@ -55,43 +54,48 @@ pub fn (p mut Parser) get_type() types.Type { } pub fn parse_file(text string, table &table.Table) ast.Program { - s := scanner.new_scanner(text) - mut exprs := []ast.Expr + mut stmts := []ast.Stmt mut p := Parser{ - scanner: s + scanner: scanner.new_scanner(text) table: table } - p.next() - p.next() + p.read_first_token() for { // res := s.scan() if p.tok.kind == .eof { break } // println('expr at ' + p.tok.str()) - expr,_ := p.expr(token.lowest_prec) - exprs << expr + s := p.stmt() + println(s) + stmts << s // p.stmt() } - // println('nr exprs = $exprs.len') - println(exprs[0]) + println('nr stmts = $stmts.len') + // println(stmts[0]) return ast.Program{ - exprs} + stmts: stmts + } } -pub fn (p mut Parser) parse_block() []ast.Expr { - mut exprs := []ast.Expr +pub fn (p mut Parser) read_first_token() { + // need to call next() twice to get peek token and current token + p.next() + p.next() +} + +pub fn (p mut Parser) parse_block() []ast.Stmt { + mut stmts := []ast.Stmt for { // res := s.scan() if p.tok.kind in [.eof, .rcbr] { break } // println('expr at ' + p.tok.str()) - expr,_ := p.expr(token.lowest_prec) - exprs << expr + stmts << p.stmt() } p.next() // println('nr exprs in block = $exprs.len') - return exprs + return stmts } /* @@ -128,11 +132,11 @@ fn (p mut Parser) check_name() string { return name } -// Implementation of Pratt Precedence -pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { - // null denotation (prefix) - mut node := ast.Expr{} - mut typ := types.void_type +pub fn (p mut Parser) stmt() ast.Stmt { + // `x := ...` + if p.tok.kind == .name && p.peek_tok.kind == .decl_assign { + return p.var_decl() + } match p.tok.kind { .key_module { return p.module_decl() @@ -149,11 +153,34 @@ pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { .key_mut { return p.var_decl() } - .name { - if p.peek_tok.kind == .decl_assign { - return p.var_decl() + else { + expr,_ := p.expr(0) + return ast.ExprStmt{ + expr: expr } } + } +} + +// Implementation of Pratt Precedence +pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { + println('expr at ' + p.tok.str()) + // null denotation (prefix) + mut node := ast.Expr{} + mut typ := types.void_type + match p.tok.kind { + .name { + // `x := ...` + // if p.peek_tok.kind == .decl_assign { + // return p.var_decl() + // } + // name expr + node = ast.Ident{ + name: p.tok.lit + } + typ = types.int_type + p.next() + } .str { node,typ = p.parse_string_literal() } @@ -272,19 +299,19 @@ fn (p mut Parser) parse_number_literal() (ast.Expr,types.Type) { return node,typ } -fn (p mut Parser) module_decl() (ast.Expr,types.Type) { +fn (p mut Parser) module_decl() ast.Stmt { // p.check(.key_module) p.next() - return ast.Expr{},types.void_type + return ast.Module{} } -fn (p mut Parser) import_stmt() (ast.Expr,types.Type) { +fn (p mut Parser) import_stmt() ast.Import { // p.check(.key_import) p.next() - return ast.Expr{},types.void_type + return ast.Import{} } -fn (p mut Parser) fn_decl() (ast.Expr,types.Type) { +fn (p mut Parser) fn_decl() ast.FnDecl { p.check(.key_fn) name := p.tok.lit // println('fn decl $name') @@ -299,31 +326,27 @@ fn (p mut Parser) fn_decl() (ast.Expr,types.Type) { } p.check(.lcbr) // p.check(.rcbr) - exprs := p.parse_block() - mut node := ast.Expr{} - node = ast.FnDecl{ + stmts := p.parse_block() + return ast.FnDecl{ name: name - exprs: exprs + stmts: stmts typ: typ } - return node,types.void_type } -fn (p mut Parser) return_stmt() (ast.Expr,types.Type) { +fn (p mut Parser) return_stmt() ast.Return { // println('return st') p.next() expr,t := p.expr(0) if !types.check(p.return_type, t) { verror('bad ret type') } - mut node := ast.Expr{} - node = ast.Return{ + return ast.Return{ expr: expr } - return node,types.void_type } -fn (p mut Parser) var_decl() (ast.Expr,types.Type) { +fn (p mut Parser) var_decl() ast.VarDecl { is_mut := p.tok.kind == .key_mut // || p.prev_tok == .key_for is_static := p.tok.kind == .key_static if p.tok.kind == .key_mut { @@ -335,8 +358,7 @@ fn (p mut Parser) var_decl() (ast.Expr,types.Type) { // p.fspace() } name := p.tok.lit - p.next() - p.next() + p.read_first_token() expr,t := p.expr(token.lowest_prec) if name in p.table.names { verror('redefinition of `$name`') @@ -344,15 +366,12 @@ fn (p mut Parser) var_decl() (ast.Expr,types.Type) { p.table.names << name // println(p.table.names) // println('added $name') - mut node := ast.Expr{} - // TODO can't return VarDecl{} - node = ast.VarDecl{ + return ast.VarDecl{ name: name expr: expr // p.expr(token.lowest_prec) typ: t - } // , ast.void_type - return node,types.void_type + } } fn verror(s string) { diff --git a/vlib/v/parser/parser_test.v b/vlib/v/parser/parser_test.v index cd02db99e8..5efee39b12 100644 --- a/vlib/v/parser/parser_test.v +++ b/vlib/v/parser/parser_test.v @@ -7,6 +7,9 @@ import ( ) fn test_parse_file() { + if true { + return + } s := ' fn foo() int { f := 23 @@ -24,23 +27,24 @@ x := 10 println(res) } - fn test_parse_expr() { - //if true { return } input := [ - '2 + 3', - '2+2*4', - //'(2+2)*4', - 'x := 10', - 'a := 12', - 'ab := 10 + 3 * 9', - 's := "hi"', - - '1 += 2', - '1.2 + 3.4', - '4 + 4', - '1 + 2 * 5', - /* + // + 'q := 1', + 'q + 777', + '2 + 3', + '2+2*4', + // '(2+2)*4', + 'x := 10', + 'a := 12', + 'ab := 10 + 3 * 9', + 's := "hi"', + '1 += 2', + // 'a += 10', + '1.2 + 3.4', + '4 + 4', + '1 + 2 * 5', + /* '(2 * 3) / 2', '3 + (7 * 6)', '2 ^ 8 * (7 * 6)', @@ -48,26 +52,33 @@ fn test_parse_expr() { '(2) + (17*2-30) * (5)+2 - (8/2)*4', // 8 //'2 + "hi"', */ + ] expecting := [ - '2 + 3', - '2 + 2 * 4', - //'(2 + 2) * 4', - 'int x = 10;', - 'int a = 12;', - 'int ab = 10 + 3 * 9;', - 'string s = tos3("hi");', - '1 += 2', - '1.2 + 3.4', - '4 + 4', - '1 + 2 * 5', + // + 'int q = 1;', + 'q + 777;', + '2 + 3;', + '2 + 2 * 4;', + // '(2 + 2) * 4', + 'int x = 10;', + 'int a = 12;', + 'int ab = 10 + 3 * 9;', + 'string s = tos3("hi");', + '1 += 2;', + // 'a += 10;', + '1.2 + 3.4;', + '4 + 4;', + '1 + 2 * 5;', ] - mut e := []ast.Expr + mut e := []ast.Stmt table := &table.Table{} for s in input { - e << parse_expr(s, table) + e << parse_stmt(s, table) + } + program := ast.Program{ + stmts: e } - program := ast.Program{exprs:e} res := cgen.gen(program) println('========') println(res) @@ -96,4 +107,3 @@ fn test_parse_expr() { println('parsed: $x') println('===================') */ - diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 1166c9a4ad..8f90f56f22 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -5,8 +5,8 @@ module token pub struct Token { pub: - kind TokenKind // the token number/enum; for quick comparisons - lit string // literal representation of the token + kind TokenKind // the token number/enum; for quick comparisons + lit string // literal representation of the token // line_nr int // the line number in the source where the token occured // name_idx int // name table index for O(1) lookup // pos int // the position of the token in scanner text @@ -125,13 +125,10 @@ pub enum TokenKind { const ( assign_tokens = [TokenKind.assign, .plus_assign, .minus_assign, .mult_assign, - .div_assign, .xor_assign, .mod_assign, .or_assign, .and_assign, - .righ_shift_assign, .left_shift_assign] - + .div_assign, .xor_assign, .mod_assign, .or_assign, .and_assign, + .righ_shift_assign, .left_shift_assign] nr_tokens = 141 ) - - // build_keys genereates a map with keywords' string values: // Keywords['return'] == .key_return fn build_keys() map[string]int { @@ -267,9 +264,7 @@ pub fn is_key(key string) bool { } pub fn is_decl(t TokenKind) bool { - return t in [.key_enum, -.key_interface, .key_fn, .key_struct, .key_type, .key_const, .key_import_const, -.key_pub, .eof] + return t in [.key_enum, .key_interface, .key_fn, .key_struct, .key_type, .key_const, .key_import_const, .key_pub, .eof] } fn (t TokenKind) is_assign() bool { @@ -290,16 +285,17 @@ pub fn (t TokenKind) str() string { return 'number' } if t == .chartoken { - return 'char'//'`lit`' + return 'char' // '`lit`' } if t == .str { - return 'str' //"'lit'" + return 'str' // "'lit'" } /* if t < .plus { return lit // string, number etc } */ + return token_str[int(t)] } @@ -307,27 +303,40 @@ pub fn (t Token) str() string { return '$t.kind.str() "$t.lit"' } - // Representation of highest and lowest precedence pub const ( - lowest_prec = 0 + lowest_prec = 0 highest_prec = 7 ) - // Precedence returns a tokens precedence if defined, otherwise lowest_prec pub fn (tok Token) precedence() int { match tok.kind { // `*` | `/` | `%` | `<<` | `>>` | `&` - .mul, .div, .left_shift, .righ_shift, .amp { return 7 } + .mul, .div, .left_shift, .righ_shift, .amp { + return 7 + } // `+` | `-` | `|` | `^` - .plus, .minus, .pipe, .xor { return 6 } - // `==` | `!=` | `<` | `<=` | `>` | `>=` | += - .eq, .ne, .lt, .le, .gt, .ge, .plus_assign { return 5 } + .plus, .minus, .pipe, .xor { + return 6 + } + // `==` | `!=` | `<` | `<=` | `>` | `>=` + .eq, .ne, .lt, .le, .gt, .ge { + return 5 + } // `&&` - .and { return 4 } + .and { + return 4 + } // `||` - .logical_or { return 3 } - else { return lowest_prec } + .logical_or { + return 3 + } + .plus_assign { + return 2 + } + else { + return lowest_prec + } } } @@ -339,37 +348,33 @@ pub fn (tok Token) is_scalar() bool { // is_unary returns true if the token can be in a unary expression pub fn (tok Token) is_unary() bool { return tok.kind in [ - // `+` | `-` | `!` | `~` | `*` | `&` - .plus, .minus, .not, .bit_not, .mul, .amp - ] + // `+` | `-` | `!` | `~` | `*` | `&` + .plus, .minus, .not, .bit_not, .mul, .amp] } // NOTE: do we need this for all tokens (is_left_assoc / is_right_assoc), // or only ones with the same precedence? - // is_left_assoc returns true if the token is left associative pub fn (tok Token) is_left_assoc() bool { return tok.kind in [ - // .number, - // `*` | `/` | `%` - .mul, .div, .mod, - // `^` | `||` | `&` - .xor, .logical_or, .and, - // `,` - .comma - ] + // .number, + // `*` | `/` | `%` + .mul, .div, .mod, + // `^` | `||` | `&` + .xor, .logical_or, .and, + // `,` + .comma] } // is_right_assoc returns true if the token is right associative pub fn (tok Token) is_right_assoc() bool { return tok.kind in [ - // `+` | `-` | `!` | `++` | `--` - .plus, .minus, .not, .inc, .dec, - // `=` | `+=` | `-=` | `*=` | `/=` - .assign, .plus_assign, .minus_assign, .mult_assign, .div_assign, - // `%=` | `>>=` | `<<=` - .mod_assign, .righ_shift_assign, .left_shift_assign, - // `&=` | `^=` | `|=` - .and_assign, .xor_assign, .or_assign - ] + // `+` | `-` | `!` | `++` | `--` + .plus, .minus, .not, .inc, .dec, + // `=` | `+=` | `-=` | `*=` | `/=` + .assign, .plus_assign, .minus_assign, .mult_assign, .div_assign, + // `%=` | `>>=` | `<<=` + .mod_assign, .righ_shift_assign, .left_shift_assign, + // `&=` | `^=` | `|=` + .and_assign, .xor_assign, .or_assign] }