mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
eval: add host API, for passing and receiving values, to/from code, ran by the eval.Eval instances (#17426)
This commit is contained in:
@ -88,6 +88,18 @@ pub fn new_builder(pref_ &pref.Preferences) Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut b Builder) interpret_text(code string, v_files []string) ! {
|
||||||
|
b.parsed_files = parser.parse_files(v_files, b.table, b.pref)
|
||||||
|
b.parsed_files << parser.parse_text(code, '', b.table, .skip_comments, b.pref)
|
||||||
|
b.parse_imports()
|
||||||
|
|
||||||
|
if b.pref.only_check_syntax {
|
||||||
|
return error_with_code('stop_after_parser', 7001)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.middle_stages()!
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut b Builder) front_stages(v_files []string) ! {
|
pub fn (mut b Builder) front_stages(v_files []string) ! {
|
||||||
mut timers := util.get_timers()
|
mut timers := util.get_timers()
|
||||||
util.timing_start('PARSE')
|
util.timing_start('PARSE')
|
||||||
|
@ -16,7 +16,6 @@ pub fn interpret_v(mut b builder.Builder) {
|
|||||||
files << b.get_user_files()
|
files << b.get_user_files()
|
||||||
b.set_module_lookup_paths()
|
b.set_module_lookup_paths()
|
||||||
b.front_and_middle_stages(files) or { return }
|
b.front_and_middle_stages(files) or { return }
|
||||||
|
|
||||||
util.timing_start('INTERPRET')
|
util.timing_start('INTERPRET')
|
||||||
mut e := eval.new_eval(b.table, b.pref)
|
mut e := eval.new_eval(b.table, b.pref)
|
||||||
e.eval(mut b.parsed_files)
|
e.eval(mut b.parsed_files)
|
||||||
|
@ -6,6 +6,7 @@ module eval
|
|||||||
import v.ast
|
import v.ast
|
||||||
import v.pref
|
import v.pref
|
||||||
import v.util
|
import v.util
|
||||||
|
import v.builder
|
||||||
|
|
||||||
pub fn new_eval(table &ast.Table, pref_ &pref.Preferences) Eval {
|
pub fn new_eval(table &ast.Table, pref_ &pref.Preferences) Eval {
|
||||||
return Eval{
|
return Eval{
|
||||||
@ -14,6 +15,38 @@ pub fn new_eval(table &ast.Table, pref_ &pref.Preferences) Eval {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Host API
|
||||||
|
|
||||||
|
pub fn create() Eval {
|
||||||
|
t := ast.new_table()
|
||||||
|
mut p, _ := pref.parse_args([], ['interpret', ''])
|
||||||
|
p.is_script = true
|
||||||
|
return new_eval(t, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut e Eval) push_val(val Object) {
|
||||||
|
e.stack_vals << val
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut e Eval) add_file(filepath string) {
|
||||||
|
e.user_files << filepath
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut e Eval) run(expression string, args ...Object) ![]Object {
|
||||||
|
mut prepend := 'fn host_pop() voidptr { return 0 }\n'
|
||||||
|
mut b := builder.new_builder(e.pref)
|
||||||
|
e.table = b.table
|
||||||
|
|
||||||
|
mut files := b.get_builtin_files()
|
||||||
|
files << e.user_files
|
||||||
|
b.set_module_lookup_paths()
|
||||||
|
|
||||||
|
b.interpret_text(prepend + expression, files)!
|
||||||
|
e.register_symbols(mut b.parsed_files)
|
||||||
|
e.run_func(e.mods['main']['main'] or { ast.FnDecl{} } as ast.FnDecl, ...args)
|
||||||
|
return e.return_values
|
||||||
|
}
|
||||||
|
|
||||||
// const/global is `Object`
|
// const/global is `Object`
|
||||||
type Symbol = Object | ast.EmptyStmt | ast.FnDecl
|
type Symbol = Object | ast.EmptyStmt | ast.FnDecl
|
||||||
|
|
||||||
@ -25,7 +58,9 @@ pub mut:
|
|||||||
future_register_consts map[string]map[string]map[string]ast.ConstField // mod:file:name:field
|
future_register_consts map[string]map[string]map[string]ast.ConstField // mod:file:name:field
|
||||||
local_vars map[string]Var
|
local_vars map[string]Var
|
||||||
local_vars_stack []map[string]Var
|
local_vars_stack []map[string]Var
|
||||||
scope_idx int // this is increased when e.open_scope() is called, decreased when e.close_scope() (and all variables with that scope level deleted)
|
stack_vals []Object // host stack popped by host_pop() on interpreted code
|
||||||
|
user_files []string // user additional files
|
||||||
|
scope_idx int // this is increased when e.open_scope() is called, decreased when e.close_scope() (and all variables with that scope level deleted)
|
||||||
returning bool
|
returning bool
|
||||||
return_values []Object
|
return_values []Object
|
||||||
cur_mod string
|
cur_mod string
|
||||||
@ -60,9 +95,10 @@ pub fn (mut e Eval) run_func(func ast.FnDecl, _args ...Object) {
|
|||||||
e.cur_file = old_file
|
e.cur_file = old_file
|
||||||
e.back_trace.pop()
|
e.back_trace.pop()
|
||||||
}
|
}
|
||||||
|
is_main := func.name == 'main.main'
|
||||||
//
|
//
|
||||||
mut args := _args.clone()
|
mut args := _args.clone()
|
||||||
if func.params.len != args.len && !func.is_variadic {
|
if !is_main && func.params.len != args.len && !func.is_variadic {
|
||||||
e.error('mismatched parameter length for ${func.name}: got `${args.len}`, expected `${func.params.len}`')
|
e.error('mismatched parameter length for ${func.name}: got `${args.len}`, expected `${func.params.len}`')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,10 +130,13 @@ pub fn (mut e Eval) run_func(func ast.FnDecl, _args ...Object) {
|
|||||||
e.open_scope()
|
e.open_scope()
|
||||||
// have to do this because of cgen error
|
// have to do this because of cgen error
|
||||||
args__ := if func.is_method { args[1..] } else { args }
|
args__ := if func.is_method { args[1..] } else { args }
|
||||||
for i, arg in args__ {
|
if !is_main {
|
||||||
e.local_vars[(func.params[i]).name] = Var{
|
for i, arg in args__ {
|
||||||
val: arg
|
var_name := (func.params[i]).name
|
||||||
scope_idx: e.scope_idx
|
e.local_vars[var_name] = Var{
|
||||||
|
val: arg
|
||||||
|
scope_idx: e.scope_idx
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if func.is_method {
|
if func.is_method {
|
||||||
@ -180,7 +219,6 @@ pub fn (mut e Eval) register_symbol(stmt ast.Stmt, mod string, file string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast.ExprStmt {
|
ast.ExprStmt {
|
||||||
println('expr')
|
|
||||||
x := stmt.expr
|
x := stmt.expr
|
||||||
match x {
|
match x {
|
||||||
ast.IfExpr {
|
ast.IfExpr {
|
||||||
@ -190,7 +228,8 @@ pub fn (mut e Eval) register_symbol(stmt ast.Stmt, mod string, file string) {
|
|||||||
for i, branch in x.branches {
|
for i, branch in x.branches {
|
||||||
mut do_if := false
|
mut do_if := false
|
||||||
println('branch:${branch}')
|
println('branch:${branch}')
|
||||||
match branch.cond {
|
cond := branch.cond
|
||||||
|
match cond {
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
match (branch.cond as ast.Ident).name {
|
match (branch.cond as ast.Ident).name {
|
||||||
'windows' {
|
'windows' {
|
||||||
|
@ -29,6 +29,9 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
|
|||||||
if expr.name == 'int' {
|
if expr.name == 'int' {
|
||||||
e.error('methods not supported')
|
e.error('methods not supported')
|
||||||
}
|
}
|
||||||
|
if expr.name == 'main.host_pop' {
|
||||||
|
return e.stack_vals.pop()
|
||||||
|
}
|
||||||
mut args := expr.args.map(e.expr(it.expr, it.typ))
|
mut args := expr.args.map(e.expr(it.expr, it.typ))
|
||||||
if expr.is_method {
|
if expr.is_method {
|
||||||
args.prepend(e.expr(expr.left, expr.receiver_type))
|
args.prepend(e.expr(expr.left, expr.receiver_type))
|
||||||
|
41
vlib/v/eval/tests/push_val_test.v
Normal file
41
vlib/v/eval/tests/push_val_test.v
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import v.eval
|
||||||
|
|
||||||
|
fn test_pushval() {
|
||||||
|
mut e := eval.create()
|
||||||
|
|
||||||
|
e.push_val(i64(2))
|
||||||
|
e.push_val(f64(2.2))
|
||||||
|
e.push_val('calc')
|
||||||
|
|
||||||
|
ret := e.run('println("\${host_pop()}: \${i64(host_pop())+i64(host_pop())}")')!
|
||||||
|
assert ret == []
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ret() {
|
||||||
|
mut e := eval.create()
|
||||||
|
|
||||||
|
e.push_val(i64(2))
|
||||||
|
e.push_val(f64(2.2))
|
||||||
|
e.push_val('calc')
|
||||||
|
|
||||||
|
ret := e.run('fn test() string { return "\${host_pop()}: \${i64(host_pop())+i64(host_pop())}" } test()')!
|
||||||
|
assert ret[0].string() == 'calc: 4'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_reuse_stack() {
|
||||||
|
mut e := eval.create()
|
||||||
|
|
||||||
|
e.push_val(i64(1))
|
||||||
|
e.push_val(i64(2))
|
||||||
|
|
||||||
|
e.push_val(i64(3))
|
||||||
|
e.push_val(i64(4))
|
||||||
|
|
||||||
|
code := 'fn calc(x int, y int) int { return x + y } calc(host_pop(), host_pop())'
|
||||||
|
|
||||||
|
ret := e.run(code)!
|
||||||
|
assert ret[0].string() == '7'
|
||||||
|
|
||||||
|
ret2 := e.run(code)!
|
||||||
|
assert ret2[0].string() == '3'
|
||||||
|
}
|
@ -866,11 +866,13 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||||||
res.path = args[command_pos + 1]
|
res.path = args[command_pos + 1]
|
||||||
res.run_args = args[command_pos + 2..]
|
res.run_args = args[command_pos + 2..]
|
||||||
|
|
||||||
must_exist(res.path)
|
if res.path != '' {
|
||||||
if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path)
|
must_exist(res.path)
|
||||||
&& os.is_file(res.path + '.v') {
|
if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path)
|
||||||
eprintln('It looks like you wanted to run "${res.path}.v", so we went ahead and did that since "${res.path}" is an executable.')
|
&& os.is_file(res.path + '.v') {
|
||||||
res.path += '.v'
|
eprintln('It looks like you wanted to run "${res.path}.v", so we went ahead and did that since "${res.path}" is an executable.')
|
||||||
|
res.path += '.v'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if command == 'build-module' {
|
if command == 'build-module' {
|
||||||
|
Reference in New Issue
Block a user