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:
parent
acfd21e4e0
commit
5f4b34ef12
@ -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) ! {
|
||||
mut timers := util.get_timers()
|
||||
util.timing_start('PARSE')
|
||||
|
@ -16,7 +16,6 @@ pub fn interpret_v(mut b builder.Builder) {
|
||||
files << b.get_user_files()
|
||||
b.set_module_lookup_paths()
|
||||
b.front_and_middle_stages(files) or { return }
|
||||
|
||||
util.timing_start('INTERPRET')
|
||||
mut e := eval.new_eval(b.table, b.pref)
|
||||
e.eval(mut b.parsed_files)
|
||||
|
@ -6,6 +6,7 @@ module eval
|
||||
import v.ast
|
||||
import v.pref
|
||||
import v.util
|
||||
import v.builder
|
||||
|
||||
pub fn new_eval(table &ast.Table, pref_ &pref.Preferences) 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`
|
||||
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
|
||||
local_vars 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
|
||||
return_values []Object
|
||||
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.back_trace.pop()
|
||||
}
|
||||
is_main := func.name == 'main.main'
|
||||
//
|
||||
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}`')
|
||||
}
|
||||
|
||||
@ -94,10 +130,13 @@ pub fn (mut e Eval) run_func(func ast.FnDecl, _args ...Object) {
|
||||
e.open_scope()
|
||||
// have to do this because of cgen error
|
||||
args__ := if func.is_method { args[1..] } else { args }
|
||||
for i, arg in args__ {
|
||||
e.local_vars[(func.params[i]).name] = Var{
|
||||
val: arg
|
||||
scope_idx: e.scope_idx
|
||||
if !is_main {
|
||||
for i, arg in args__ {
|
||||
var_name := (func.params[i]).name
|
||||
e.local_vars[var_name] = Var{
|
||||
val: arg
|
||||
scope_idx: e.scope_idx
|
||||
}
|
||||
}
|
||||
}
|
||||
if func.is_method {
|
||||
@ -180,7 +219,6 @@ pub fn (mut e Eval) register_symbol(stmt ast.Stmt, mod string, file string) {
|
||||
}
|
||||
}
|
||||
ast.ExprStmt {
|
||||
println('expr')
|
||||
x := stmt.expr
|
||||
match x {
|
||||
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 {
|
||||
mut do_if := false
|
||||
println('branch:${branch}')
|
||||
match branch.cond {
|
||||
cond := branch.cond
|
||||
match cond {
|
||||
ast.Ident {
|
||||
match (branch.cond as ast.Ident).name {
|
||||
'windows' {
|
||||
|
@ -29,6 +29,9 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
|
||||
if expr.name == 'int' {
|
||||
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))
|
||||
if expr.is_method {
|
||||
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.run_args = args[command_pos + 2..]
|
||||
|
||||
must_exist(res.path)
|
||||
if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path)
|
||||
&& os.is_file(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 res.path != '' {
|
||||
must_exist(res.path)
|
||||
if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path)
|
||||
&& os.is_file(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' {
|
||||
|
Loading…
Reference in New Issue
Block a user