v: support dump(expr) (#9160)
parent
747507dd1d
commit
849cde245c
|
@ -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 {
|
||||
eprintln('f.path: $f.path | example: $e')
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -11,10 +11,10 @@ pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
|
|||
|
||||
pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral |
|
||||
CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall |
|
||||
ComptimeSelector | ConcatExpr | EnumVal | FloatLiteral | GoExpr | Ident | IfExpr |
|
||||
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit |
|
||||
MatchExpr | None | OffsetOf | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr |
|
||||
SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
|
||||
ComptimeSelector | ConcatExpr | DumpExpr | EnumVal | FloatLiteral | GoExpr | Ident |
|
||||
IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
|
||||
MapInit | MatchExpr | None | OffsetOf | OrExpr | ParExpr | PostfixExpr | PrefixExpr |
|
||||
RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
|
||||
StructInit | Type | TypeOf | UnsafeExpr
|
||||
|
||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
|
||||
|
@ -1114,6 +1114,15 @@ pub mut:
|
|||
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:
|
||||
text string
|
||||
|
@ -1240,7 +1249,7 @@ pub fn (expr Expr) position() token.Position {
|
|||
return expr.decl.pos
|
||||
}
|
||||
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,
|
||||
None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr,
|
||||
SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr
|
||||
|
|
|
@ -3716,6 +3716,17 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
|||
ast.ConcatExpr {
|
||||
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 {
|
||||
return c.enum_val(mut node)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
| ~~~~~
|
|
@ -0,0 +1,3 @@
|
|||
fn abc() {}
|
||||
|
||||
dump(abc())
|
|
@ -900,6 +900,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||
ast.CTempVar {
|
||||
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 {
|
||||
f.fn_decl(node.decl)
|
||||
}
|
||||
|
|
|
@ -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.dump_expr_definitions()
|
||||
// v files are finished, what remains is pure C code
|
||||
g.gen_vlines_reset()
|
||||
if g.pref.build_mode != .build_module {
|
||||
|
@ -2698,6 +2700,9 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||
g.write("L'$node.val'")
|
||||
}
|
||||
}
|
||||
ast.DumpExpr {
|
||||
g.dump_expr(node)
|
||||
}
|
||||
ast.AtExpr {
|
||||
g.comp_at(node)
|
||||
}
|
||||
|
@ -2965,7 +2970,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
|||
g.write('(*(')
|
||||
}
|
||||
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
|
||||
g.write('$info.size')
|
||||
return
|
||||
|
|
|
@ -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('}')
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,12 +17,12 @@ const (
|
|||
'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', 'Number', 'String', 'Boolean',
|
||||
'Array', 'Map']
|
||||
// used to generate type structs
|
||||
v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', '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,
|
||||
.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',
|
||||
'\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']
|
||||
v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64',
|
||||
'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, .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', '\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 {
|
||||
|
@ -115,7 +115,7 @@ pub fn gen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
|||
mut out := g.hashes() + g.definitions.str()
|
||||
// equality check for js objects
|
||||
// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js')
|
||||
//unsafe {
|
||||
// unsafe {
|
||||
// mut eq_fn := $embed_file('fast_deep_equal.js')
|
||||
// out += eq_fn.data().vstring()
|
||||
//}
|
||||
|
@ -447,6 +447,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
|||
ast.CTempVar {
|
||||
g.write('/* ast.CTempVar: node.name */')
|
||||
}
|
||||
ast.DumpExpr {
|
||||
g.write('/* ast.DumpExpr: $node.expr */')
|
||||
}
|
||||
ast.AnonFn {
|
||||
g.gen_fn_decl(node.decl)
|
||||
}
|
||||
|
@ -689,10 +692,12 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) {
|
|||
} else {
|
||||
g.write(' $op ')
|
||||
// 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 {
|
||||
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.expr(val)
|
||||
|
@ -845,7 +850,9 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl) {
|
|||
}
|
||||
g.fn_args(args, it.is_variadic)
|
||||
if it.is_method {
|
||||
if args.len > 0 { g.write(', ') }
|
||||
if args.len > 0 {
|
||||
g.write(', ')
|
||||
}
|
||||
g.write('${it.params[0].name} = this')
|
||||
}
|
||||
g.writeln(') {')
|
||||
|
@ -926,13 +933,23 @@ fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) {
|
|||
if val !in ['', '_'] {
|
||||
g.write('\tconst $val = ')
|
||||
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.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]')
|
||||
if it.kind == .string { g.write(')') }
|
||||
if it.kind == .string {
|
||||
g.write(')')
|
||||
}
|
||||
g.writeln(';')
|
||||
}
|
||||
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)
|
||||
|
||||
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 {
|
||||
// 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.write('.eq(')
|
||||
g.expr(it.right)
|
||||
|
@ -1404,7 +1423,9 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
|
|||
'.includes('
|
||||
})
|
||||
g.expr(it.left)
|
||||
if l_sym.kind == .string { g.write('.str') }
|
||||
if l_sym.kind == .string {
|
||||
g.write('.str')
|
||||
}
|
||||
g.write(')')
|
||||
} else if it.op in [.key_is, .not_is] { // foo is Foo
|
||||
g.expr(it.left)
|
||||
|
@ -1423,7 +1444,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) {
|
|||
g.cast_stack << greater_typ
|
||||
}
|
||||
g.expr(it.left)
|
||||
g.write(' ${it.op} ')
|
||||
g.write(' $it.op ')
|
||||
g.expr(it.right)
|
||||
|
||||
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 {
|
||||
|
@ -1560,7 +1583,7 @@ fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) {
|
|||
if g.file.mod.name == 'builtin' {
|
||||
g.write('new ')
|
||||
}
|
||||
g.write("string(")
|
||||
g.write('string(')
|
||||
}
|
||||
g.write("'$text'")
|
||||
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?
|
||||
if g.call_stack.len > 0 {
|
||||
call := g.call_stack[g.call_stack.len - 1]
|
||||
//if call.language == .js {
|
||||
// if call.language == .js {
|
||||
for t in call.args {
|
||||
if t.expr is ast.IntegerLiteral {
|
||||
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?
|
||||
if g.call_stack.len > 0 {
|
||||
call := g.call_stack[g.call_stack.len - 1]
|
||||
//if call.language == .js {
|
||||
// if call.language == .js {
|
||||
for i, t in call.args {
|
||||
if t.expr is ast.FloatLiteral {
|
||||
if t.expr == it {
|
||||
|
|
|
@ -180,6 +180,9 @@ fn (mut w Walker) expr(node ast.Expr) {
|
|||
w.stmts(node.vweb_tmpl.stmts)
|
||||
}
|
||||
}
|
||||
ast.DumpExpr {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
ast.GoExpr {
|
||||
w.expr(node.go_stmt.call_expr)
|
||||
}
|
||||
|
|
|
@ -670,6 +670,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
|||
p.label_names << name
|
||||
p.next()
|
||||
if p.tok.kind == .key_for {
|
||||
for_pos := p.tok.position()
|
||||
mut stmt := p.stmt(is_top_level)
|
||||
match mut stmt {
|
||||
ast.ForStmt {
|
||||
|
@ -685,7 +686,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
|||
return stmt
|
||||
}
|
||||
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)
|
||||
&& node !is ast.PostfixExpr && !(node is ast.InfixExpr
|
||||
&& (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())
|
||||
return ast.Stmt{}
|
||||
}
|
||||
|
|
|
@ -214,6 +214,17 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
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 {
|
||||
pos := p.tok.position()
|
||||
p.next() // __offsetof
|
||||
|
|
|
@ -12,6 +12,7 @@ pub mut:
|
|||
types []TypeSymbol
|
||||
type_idxs map[string]int
|
||||
fns map[string]Fn
|
||||
dumps map[int]string // needed for efficiently generating all _v_dump_expr_TNAME() functions
|
||||
imports []string // List of all imports
|
||||
modules []string // Topologically sorted list of all modules registered by the application
|
||||
cflags []cflag.CFlag
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -117,6 +117,7 @@ pub enum Kind {
|
|||
key_true
|
||||
key_type
|
||||
key_typeof
|
||||
key_dump
|
||||
key_orelse
|
||||
key_union
|
||||
key_pub
|
||||
|
@ -274,6 +275,7 @@ fn build_token_str() []string {
|
|||
s[Kind.key_import] = 'import'
|
||||
s[Kind.key_unsafe] = 'unsafe'
|
||||
s[Kind.key_typeof] = 'typeof'
|
||||
s[Kind.key_dump] = 'dump'
|
||||
s[Kind.key_enum] = 'enum'
|
||||
s[Kind.key_interface] = 'interface'
|
||||
s[Kind.key_pub] = 'pub'
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn new_mod_file_cacher() &ModFileCacher {
|
|||
return &ModFileCacher{}
|
||||
}
|
||||
|
||||
pub fn (mcache &ModFileCacher) dump() {
|
||||
pub fn (mcache &ModFileCacher) debug() {
|
||||
$if debug {
|
||||
eprintln('ModFileCacher DUMP:')
|
||||
eprintln(' ModFileCacher.cache:')
|
||||
|
|
Loading…
Reference in New Issue