v: support dump(expr) (#9160)

pull/9168/head
Delyan Angelov 2021-03-06 19:09:28 +02:00 committed by GitHub
parent 747507dd1d
commit 849cde245c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 219 additions and 53 deletions

View File

@ -204,7 +204,7 @@ fn (mut f MDFile) parse_line(lnumber int, line string) {
} }
} }
fn (mut f MDFile) dump() { fn (mut f MDFile) debug() {
for e in f.examples { for e in f.examples {
eprintln('f.path: $f.path | example: $e') eprintln('f.path: $f.path | example: $e')
} }

View File

@ -0,0 +1,10 @@
fn factorial(n u32) u32 {
if dump(n <= 1) {
return dump(1)
}
return dump(n * factorial(n - 1))
}
fn main() {
println(factorial(5))
}

View File

@ -11,10 +11,10 @@ pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral |
CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall |
ComptimeSelector | ConcatExpr | EnumVal | FloatLiteral | GoExpr | Ident | IfExpr | ComptimeSelector | ConcatExpr | DumpExpr | EnumVal | FloatLiteral | GoExpr | Ident |
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
MatchExpr | None | OffsetOf | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | MapInit | MatchExpr | None | OffsetOf | OrExpr | ParExpr | PostfixExpr | PrefixExpr |
SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
StructInit | Type | TypeOf | UnsafeExpr StructInit | Type | TypeOf | UnsafeExpr
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt | pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
@ -1114,6 +1114,15 @@ pub mut:
expr_type table.Type expr_type table.Type
} }
pub struct DumpExpr {
pub:
expr Expr
pos token.Position
pub mut:
expr_type table.Type
cname string // filled in the checker
}
pub struct Comment { pub struct Comment {
pub: pub:
text string text string
@ -1240,7 +1249,7 @@ pub fn (expr Expr) position() token.Position {
return expr.decl.pos return expr.decl.pos
} }
ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit,
CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector, EnumVal, FloatLiteral, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector, EnumVal, DumpExpr, FloatLiteral,
GoExpr, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr, GoExpr, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr,
None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr,
SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr

View File

@ -3716,6 +3716,17 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
ast.ConcatExpr { ast.ConcatExpr {
return c.concat_expr(mut node) return c.concat_expr(mut node)
} }
ast.DumpExpr {
node.expr_type = c.expr(node.expr)
if node.expr_type.idx() == table.void_type_idx {
c.error('dump expression can not be void', node.expr.position())
return table.void_type
}
tsym := c.table.get_type_symbol(node.expr_type)
c.table.dumps[int(node.expr_type)] = tsym.cname
node.cname = tsym.cname
return node.expr_type
}
ast.EnumVal { ast.EnumVal {
return c.enum_val(mut node) return c.enum_val(mut node)
} }

View File

@ -0,0 +1,5 @@
vlib/v/checker/tests/dump_of_void_expr.vv:3:6: error: dump expression can not be void
1 | fn abc() {}
2 |
3 | dump(abc())
| ~~~~~

View File

@ -0,0 +1,3 @@
fn abc() {}
dump(abc())

View File

@ -900,6 +900,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
ast.CTempVar { ast.CTempVar {
eprintln('ast.CTempVar of $node.orig.str() should be generated/used only in cgen') eprintln('ast.CTempVar of $node.orig.str() should be generated/used only in cgen')
} }
ast.DumpExpr {
f.write('dump(')
f.expr(node.expr)
f.write(')')
}
ast.AnonFn { ast.AnonFn {
f.fn_decl(node.decl) f.fn_decl(node.decl)
} }

View File

@ -266,6 +266,8 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
g.definitions.writeln('int _v_type_idx_${typ.cname}() { return $idx; };') g.definitions.writeln('int _v_type_idx_${typ.cname}() { return $idx; };')
} }
} }
//
g.dump_expr_definitions()
// v files are finished, what remains is pure C code // v files are finished, what remains is pure C code
g.gen_vlines_reset() g.gen_vlines_reset()
if g.pref.build_mode != .build_module { if g.pref.build_mode != .build_module {
@ -2698,6 +2700,9 @@ fn (mut g Gen) expr(node ast.Expr) {
g.write("L'$node.val'") g.write("L'$node.val'")
} }
} }
ast.DumpExpr {
g.dump_expr(node)
}
ast.AtExpr { ast.AtExpr {
g.comp_at(node) g.comp_at(node)
} }
@ -2965,7 +2970,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
g.write('(*(') g.write('(*(')
} }
if sym.kind == .array_fixed { if sym.kind == .array_fixed {
assert node.field_name == 'len' if node.field_name != 'len' {
g.error('field_name should be `len`', node.pos)
}
info := sym.info as table.ArrayFixed info := sym.info as table.ArrayFixed
g.write('$info.size') g.write('$info.size')
return return

View File

@ -0,0 +1,39 @@
module c
import v.ast
fn (mut g Gen) dump_expr(node ast.DumpExpr) {
sexpr := ctoslit(node.expr.str())
fpath := cestring(g.file.path)
line := node.pos.line_nr + 1
if 'nop_dump' in g.pref.compile_defines {
g.expr(node.expr)
return
}
g.write(' _v_dump_expr_${node.cname}(${ctoslit(fpath)}, $line, $sexpr, ')
g.expr(node.expr)
g.write(' )')
}
fn (mut g Gen) dump_expr_definitions() {
if g.pref.build_mode == .build_module {
for _, cname in g.table.dumps {
g.definitions.writeln('$cname _v_dump_expr_${cname}(string fpath, int line, string sexpr, $cname x);')
}
} else {
for dump_type, cname in g.table.dumps {
to_string_fn_name := g.gen_str_for_type(dump_type)
g.definitions.writeln('$cname _v_dump_expr_${cname}(string fpath, int line, string sexpr, $cname x) {')
g.definitions.writeln('\teprint(${ctoslit('[')});')
g.definitions.writeln('\teprint(fpath);')
g.definitions.writeln('\teprint(${ctoslit(':')});')
g.definitions.writeln('\teprint(int_str(line));')
g.definitions.writeln('\teprint(${ctoslit('] ')});')
g.definitions.writeln('\teprint(sexpr);')
g.definitions.writeln('\teprint(${ctoslit(': ')});')
g.definitions.writeln('\teprintln(${to_string_fn_name}(x));')
g.definitions.writeln('\treturn x;')
g.definitions.writeln('}')
}
}
}

View File

@ -17,12 +17,12 @@ const (
'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', 'Number', 'String', 'Boolean', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', 'Number', 'String', 'Boolean',
'Array', 'Map'] 'Array', 'Map']
// used to generate type structs // used to generate type structs
v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', 'int_literal', v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64',
'float_literal', 'size_t', 'bool', 'string', 'map', 'array'] 'int_literal', 'float_literal', 'size_t', 'bool', 'string', 'map', 'array']
shallow_equatables = [table.Kind.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .int_literal, shallow_equatables = [table.Kind.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64,
.float_literal, .size_t, .bool, .string] .int_literal, .float_literal, .size_t, .bool, .string]
tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t',
'\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t'] '\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t', '\t\t\t\t\t\t\t\t\t']
) )
struct Namespace { struct Namespace {
@ -115,7 +115,7 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
mut out := g.hashes() + g.definitions.str() mut out := g.hashes() + g.definitions.str()
// equality check for js objects // equality check for js objects
// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js') // TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js')
//unsafe { // unsafe {
// mut eq_fn := $embed_file('fast_deep_equal.js') // mut eq_fn := $embed_file('fast_deep_equal.js')
// out += eq_fn.data().vstring() // out += eq_fn.data().vstring()
//} //}
@ -447,6 +447,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
ast.CTempVar { ast.CTempVar {
g.write('/* ast.CTempVar: node.name */') g.write('/* ast.CTempVar: node.name */')
} }
ast.DumpExpr {
g.write('/* ast.DumpExpr: $node.expr */')
}
ast.AnonFn { ast.AnonFn {
g.gen_fn_decl(node.decl) g.gen_fn_decl(node.decl)
} }
@ -689,10 +692,12 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
} else { } else {
g.write(' $op ') g.write(' $op ')
// TODO: Multiple types?? // TODO: Multiple types??
should_cast := g.table.type_kind(stmt.left_types.first()) in shallow_equatables should_cast := g.table.type_kind(stmt.left_types.first()) in js.shallow_equatables
if should_cast { if should_cast {
g.cast_stack << stmt.left_types.first() g.cast_stack << stmt.left_types.first()
if g.file.mod.name == 'builtin' { g.write('new ') } if g.file.mod.name == 'builtin' {
g.write('new ')
}
g.write('${g.typ(stmt.left_types.first())}(') g.write('${g.typ(stmt.left_types.first())}(')
} }
g.expr(val) g.expr(val)
@ -845,7 +850,9 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
} }
g.fn_args(args, it.is_variadic) g.fn_args(args, it.is_variadic)
if it.is_method { if it.is_method {
if args.len > 0 { g.write(', ') } if args.len > 0 {
g.write(', ')
}
g.write('${it.params[0].name} = this') g.write('${it.params[0].name} = this')
} }
g.writeln(') {') g.writeln(') {')
@ -926,13 +933,23 @@ fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) {
if val !in ['', '_'] { if val !in ['', '_'] {
g.write('\tconst $val = ') g.write('\tconst $val = ')
if it.kind == .string { if it.kind == .string {
if g.file.mod.name == 'builtin' { g.write('new ') } if g.file.mod.name == 'builtin' {
g.write('new ')
}
g.write('byte(') g.write('byte(')
} }
g.expr(it.cond) g.expr(it.cond)
g.write(if it.kind == .array { '.arr' } else if it.kind == .string { '.str' } else { '.val' }) g.write(if it.kind == .array {
'.arr'
} else if it.kind == .string {
'.str'
} else {
'.val'
})
g.write('[$i]') g.write('[$i]')
if it.kind == .string { g.write(')') } if it.kind == .string {
g.write(')')
}
g.writeln(';') g.writeln(';')
} }
g.stmts(it.stmts) g.stmts(it.stmts)
@ -1369,11 +1386,13 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
r_sym := g.table.get_type_symbol(it.right_type) r_sym := g.table.get_type_symbol(it.right_type)
is_not := it.op in [.not_in, .not_is, .ne] is_not := it.op in [.not_in, .not_is, .ne]
if is_not { g.write('!(') } if is_not {
g.write('!(')
}
if it.op == .eq || it.op == .ne { if it.op == .eq || it.op == .ne {
// Shallow equatables // Shallow equatables
if l_sym.kind in shallow_equatables && r_sym.kind in shallow_equatables { if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables {
g.expr(it.left) g.expr(it.left)
g.write('.eq(') g.write('.eq(')
g.expr(it.right) g.expr(it.right)
@ -1404,7 +1423,9 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
'.includes(' '.includes('
}) })
g.expr(it.left) g.expr(it.left)
if l_sym.kind == .string { g.write('.str') } if l_sym.kind == .string {
g.write('.str')
}
g.write(')') g.write(')')
} else if it.op in [.key_is, .not_is] { // foo is Foo } else if it.op in [.key_is, .not_is] { // foo is Foo
g.expr(it.left) g.expr(it.left)
@ -1423,7 +1444,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
g.cast_stack << greater_typ g.cast_stack << greater_typ
} }
g.expr(it.left) g.expr(it.left)
g.write(' ${it.op} ') g.write(' $it.op ')
g.expr(it.right) g.expr(it.right)
if is_arithmetic && needs_cast { if is_arithmetic && needs_cast {
@ -1432,7 +1453,9 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
} }
} }
if is_not { g.write(')') } if is_not {
g.write(')')
}
} }
fn (mut g JsGen) greater_typ(left table.Type, right table.Type) table.Type { fn (mut g JsGen) greater_typ(left table.Type, right table.Type) table.Type {
@ -1560,7 +1583,7 @@ fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) {
if g.file.mod.name == 'builtin' { if g.file.mod.name == 'builtin' {
g.write('new ') g.write('new ')
} }
g.write("string(") g.write('string(')
} }
g.write("'$text'") g.write("'$text'")
if should_cast { if should_cast {
@ -1651,7 +1674,7 @@ fn (mut g JsGen) gen_integer_literal_expr(it ast.IntegerLiteral) {
// TODO: call.language always seems to be "v", parser bug? // TODO: call.language always seems to be "v", parser bug?
if g.call_stack.len > 0 { if g.call_stack.len > 0 {
call := g.call_stack[g.call_stack.len - 1] call := g.call_stack[g.call_stack.len - 1]
//if call.language == .js { // if call.language == .js {
for t in call.args { for t in call.args {
if t.expr is ast.IntegerLiteral { if t.expr is ast.IntegerLiteral {
if t.expr == it { if t.expr == it {
@ -1685,7 +1708,7 @@ fn (mut g JsGen) gen_float_literal_expr(it ast.FloatLiteral) {
// TODO: call.language always seems to be "v", parser bug? // TODO: call.language always seems to be "v", parser bug?
if g.call_stack.len > 0 { if g.call_stack.len > 0 {
call := g.call_stack[g.call_stack.len - 1] call := g.call_stack[g.call_stack.len - 1]
//if call.language == .js { // if call.language == .js {
for i, t in call.args { for i, t in call.args {
if t.expr is ast.FloatLiteral { if t.expr is ast.FloatLiteral {
if t.expr == it { if t.expr == it {

View File

@ -180,6 +180,9 @@ fn (mut w Walker) expr(node ast.Expr) {
w.stmts(node.vweb_tmpl.stmts) w.stmts(node.vweb_tmpl.stmts)
} }
} }
ast.DumpExpr {
w.expr(node.expr)
}
ast.GoExpr { ast.GoExpr {
w.expr(node.go_stmt.call_expr) w.expr(node.go_stmt.call_expr)
} }

View File

@ -670,6 +670,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
p.label_names << name p.label_names << name
p.next() p.next()
if p.tok.kind == .key_for { if p.tok.kind == .key_for {
for_pos := p.tok.position()
mut stmt := p.stmt(is_top_level) mut stmt := p.stmt(is_top_level)
match mut stmt { match mut stmt {
ast.ForStmt { ast.ForStmt {
@ -685,7 +686,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
return stmt return stmt
} }
else { else {
assert false p.error_with_pos('unknown kind of For statement', for_pos)
} }
} }
} }
@ -1031,7 +1032,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
if node !is ast.CallExpr && (is_top_level || p.tok.kind != .rcbr) if node !is ast.CallExpr && (is_top_level || p.tok.kind != .rcbr)
&& node !is ast.PostfixExpr && !(node is ast.InfixExpr && node !is ast.PostfixExpr && !(node is ast.InfixExpr
&& (node as ast.InfixExpr).op in [.left_shift, .arrow]) && node !is ast.ComptimeCall && (node as ast.InfixExpr).op in [.left_shift, .arrow]) && node !is ast.ComptimeCall
&& node !is ast.SelectorExpr { && node !is ast.SelectorExpr && node !is ast.DumpExpr {
p.error_with_pos('expression evaluated but not used', node.position()) p.error_with_pos('expression evaluated but not used', node.position())
return ast.Stmt{} return ast.Stmt{}
} }

View File

@ -214,6 +214,17 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
pos: spos.extend(p.tok.position()) pos: spos.extend(p.tok.position())
} }
} }
.key_dump {
spos := p.tok.position()
p.next()
p.check(.lpar)
expr := p.expr(0)
p.check(.rpar)
node = ast.DumpExpr{
expr: expr
pos: spos.extend(p.tok.position())
}
}
.key_offsetof { .key_offsetof {
pos := p.tok.position() pos := p.tok.position()
p.next() // __offsetof p.next() // __offsetof

View File

@ -12,6 +12,7 @@ pub mut:
types []TypeSymbol types []TypeSymbol
type_idxs map[string]int type_idxs map[string]int
fns map[string]Fn fns map[string]Fn
dumps map[int]string // needed for efficiently generating all _v_dump_expr_TNAME() functions
imports []string // List of all imports imports []string // List of all imports
modules []string // Topologically sorted list of all modules registered by the application modules []string // Topologically sorted list of all modules registered by the application
cflags []cflag.CFlag cflags []cflag.CFlag

View File

@ -0,0 +1,7 @@
[vlib/v/tests/inout/dump_expression.vv:2] 1: 1
[vlib/v/tests/inout/dump_expression.vv:7] 'a': a
[vlib/v/tests/inout/dump_expression.vv:20] point: Point{
x: 1
y: 2
z: 3
}

View File

@ -0,0 +1,29 @@
fn dump_of_int() {
x := dump(1) + 1
assert x == 2
}
fn dump_of_string() {
x := dump('a') + 'b'
assert x == 'ab'
}
struct Point {
mut:
x int
y int
z int
}
fn dump_of_struct() {
point := Point{1, 2, 3}
mut x := dump(point)
x.x += 100
assert x == Point{101, 2, 3}
}
fn main() {
dump_of_int()
dump_of_string()
dump_of_struct()
}

View File

@ -117,6 +117,7 @@ pub enum Kind {
key_true key_true
key_type key_type
key_typeof key_typeof
key_dump
key_orelse key_orelse
key_union key_union
key_pub key_pub
@ -274,6 +275,7 @@ fn build_token_str() []string {
s[Kind.key_import] = 'import' s[Kind.key_import] = 'import'
s[Kind.key_unsafe] = 'unsafe' s[Kind.key_unsafe] = 'unsafe'
s[Kind.key_typeof] = 'typeof' s[Kind.key_typeof] = 'typeof'
s[Kind.key_dump] = 'dump'
s[Kind.key_enum] = 'enum' s[Kind.key_enum] = 'enum'
s[Kind.key_interface] = 'interface' s[Kind.key_interface] = 'interface'
s[Kind.key_pub] = 'pub' s[Kind.key_pub] = 'pub'

View File

@ -42,7 +42,7 @@ pub fn new_mod_file_cacher() &ModFileCacher {
return &ModFileCacher{} return &ModFileCacher{}
} }
pub fn (mcache &ModFileCacher) dump() { pub fn (mcache &ModFileCacher) debug() {
$if debug { $if debug {
eprintln('ModFileCacher DUMP:') eprintln('ModFileCacher DUMP:')
eprintln(' ModFileCacher.cache:') eprintln(' ModFileCacher.cache:')