1
0
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:
Felipe Pena 2023-02-28 19:42:19 -03:00 committed by GitHub
parent acfd21e4e0
commit 5f4b34ef12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 14 deletions

View File

@ -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')

View File

@ -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)

View File

@ -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' {

View File

@ -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))

View 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'
}

View File

@ -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' {