diff --git a/kclvm/Cargo.lock b/kclvm/Cargo.lock index 7eb1ce6ab..db882c1e1 100644 --- a/kclvm/Cargo.lock +++ b/kclvm/Cargo.lock @@ -1724,6 +1724,7 @@ name = "kclvm-evaluator" version = "0.8.3" dependencies = [ "anyhow", + "generational-arena", "indexmap 1.9.3", "insta", "kclvm-ast", @@ -1863,6 +1864,7 @@ dependencies = [ "bstr", "chrono", "fancy-regex", + "generational-arena", "glob", "indexmap 1.9.3", "itertools", diff --git a/kclvm/evaluator/Cargo.toml b/kclvm/evaluator/Cargo.toml index 761627a8d..c3332cc5f 100644 --- a/kclvm/evaluator/Cargo.toml +++ b/kclvm/evaluator/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] indexmap = "1.0" anyhow = "1.0" +generational-arena = "0.2.9" kclvm-ast = {path = "../ast"} kclvm-sema = {path = "../sema"} kclvm-runtime = {path = "../runtime"} diff --git a/kclvm/evaluator/src/context.rs b/kclvm/evaluator/src/context.rs index d2b763264..52cb4d34a 100644 --- a/kclvm/evaluator/src/context.rs +++ b/kclvm/evaluator/src/context.rs @@ -1,14 +1,16 @@ -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; +use generational_arena::Index; use indexmap::IndexMap; use kclvm_error::Handler; use kclvm_runtime::ValueRef; -use crate::{error as kcl_error, function::FunctionValue, scope::Scope, Evaluator, GLOBAL_LEVEL}; +use crate::{error as kcl_error, func::FunctionProxy, scope::Scope, Evaluator, GLOBAL_LEVEL}; -#[derive(Debug)] pub struct EvaluatorContext { - pub functions: Vec, pub imported: HashSet, pub lambda_stack: Vec, pub schema_stack: Vec<()>, @@ -42,7 +44,6 @@ pub struct EvaluatorContext { impl Default for EvaluatorContext { fn default() -> Self { Self { - functions: Default::default(), imported: Default::default(), lambda_stack: vec![GLOBAL_LEVEL], schema_stack: Default::default(), @@ -206,4 +207,10 @@ impl<'ctx> Evaluator<'ctx> { pub(crate) fn pop_pkgpath(&self) { self.ctx.borrow_mut().pkgpath_stack.pop(); } + + /// Append a function into the scope + #[inline] + pub fn add_function(&self, function: FunctionProxy) -> Index { + self.functions.borrow_mut().insert(Arc::new(function)) + } } diff --git a/kclvm/evaluator/src/func.rs b/kclvm/evaluator/src/func.rs new file mode 100644 index 000000000..7b6e09fb6 --- /dev/null +++ b/kclvm/evaluator/src/func.rs @@ -0,0 +1,58 @@ +use std::fmt::Debug; +use std::sync::Arc; + +use generational_arena::Index; +use kclvm_ast::ast; +use kclvm_runtime::ValueRef; + +use crate::error as kcl_error; +use crate::Evaluator; + +pub type FunctionHandler = + Arc ValueRef>; + +/// Proxy functions represent the saved functions of the runtime itself, +/// rather than executing KCL defined functions or plugin functions. +#[derive(Clone)] +pub struct FunctionProxy { + lambda_expr: ast::LambdaExpr, + inner: FunctionHandler, +} + +impl Debug for FunctionProxy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ptr_value = Arc::as_ptr(&self.inner); + f.debug_struct("FunctionProxy") + .field("inner", &format!("{ptr_value:p}")) + .finish() + } +} + +impl FunctionProxy { + #[inline] + pub fn new(lambda_expr: ast::LambdaExpr, proxy: FunctionHandler) -> Self { + Self { + lambda_expr, + inner: proxy, + } + } +} + +impl<'ctx> Evaluator<'ctx> { + #[inline] + pub(crate) fn invoke_proxy_function( + &self, + proxy_index: Index, + args: &ValueRef, + kwargs: &ValueRef, + ) -> ValueRef { + let proxy = { + let functions = self.functions.borrow(); + functions + .get(proxy_index) + .expect(kcl_error::INTERNAL_ERROR_MSG) + .clone() + }; + (proxy.inner)(self, &proxy.lambda_expr, args, kwargs) + } +} diff --git a/kclvm/evaluator/src/function.rs b/kclvm/evaluator/src/function.rs deleted file mode 100644 index 87fea54cb..000000000 --- a/kclvm/evaluator/src/function.rs +++ /dev/null @@ -1,15 +0,0 @@ -use kclvm_runtime::{Context, ValueRef}; - -pub type FuncPtr = fn(&mut Context, &ValueRef, &ValueRef) -> ValueRef; - -#[derive(Debug)] -pub struct FunctionValue { - inner: FuncPtr, -} - -impl FunctionValue { - #[inline] - pub fn get_fn_ptr(&self) -> u64 { - self.inner as u64 - } -} diff --git a/kclvm/evaluator/src/lib.rs b/kclvm/evaluator/src/lib.rs index c37c69248..4f0c0edea 100644 --- a/kclvm/evaluator/src/lib.rs +++ b/kclvm/evaluator/src/lib.rs @@ -6,7 +6,7 @@ mod tests; pub(crate) mod calculation; pub(crate) mod context; pub(crate) mod error; -pub(crate) mod function; +pub(crate) mod func; pub(crate) mod module; pub(crate) mod node; pub(crate) mod scope; @@ -15,9 +15,10 @@ pub(crate) mod value; extern crate kclvm_error; use context::EvaluatorContext; -use kclvm_ast::walker::TypedResultWalker; -use std::cell::RefCell; +use func::FunctionProxy; +use generational_arena::Arena; use std::str; +use std::{cell::RefCell, sync::Arc}; use crate::error as kcl_error; use anyhow::Result; @@ -39,28 +40,35 @@ pub struct Evaluator<'ctx> { pub program: &'ctx ast::Program, pub ctx: RefCell, pub runtime_ctx: RefCell, + pub functions: RefCell>>, } impl<'ctx> Evaluator<'ctx> { - /// New aa Evaluator using the LLVM Context and AST Program + /// New aa Evaluator using the AST program + #[inline] pub fn new(program: &'ctx ast::Program) -> Evaluator<'ctx> { + Self::new_with_runtime_ctx(program, Context::new()) + } + + /// New aa Evaluator using the AST program and runtime context + #[inline] + pub fn new_with_runtime_ctx( + program: &'ctx ast::Program, + runtime_ctx: Context, + ) -> Evaluator<'ctx> { Evaluator { ctx: RefCell::new(EvaluatorContext::default()), - runtime_ctx: RefCell::new(Context::new()), + runtime_ctx: RefCell::new(runtime_ctx), program, + functions: RefCell::new(Arena::new()), } } - /// Generate LLVM IR of ast module. + /// Evaluate the program pub fn run(self: &Evaluator<'ctx>) -> Result<(String, String)> { - self.init_scope(kclvm_ast::MAIN_PKG); - for module in self - .program - .pkgs - .get(kclvm_ast::MAIN_PKG) - .unwrap_or(&vec![]) - { - self.walk_module(module)?; + if let Some(modules) = self.program.pkgs.get(kclvm_ast::MAIN_PKG) { + self.init_scope(kclvm_ast::MAIN_PKG); + self.compile_ast_modules(modules) } Ok(self.plan_globals_to_string()) } diff --git a/kclvm/evaluator/src/node.rs b/kclvm/evaluator/src/node.rs index 70763abcf..cdd838dee 100644 --- a/kclvm/evaluator/src/node.rs +++ b/kclvm/evaluator/src/node.rs @@ -1,5 +1,7 @@ // Copyright The KCL Authors. All rights reserved. +use std::sync::Arc; + use anyhow::Ok; use kclvm_ast::ast::{self, CallExpr, ConfigEntry, NodeRef}; use kclvm_ast::walker::TypedResultWalker; @@ -8,10 +10,11 @@ use kclvm_runtime::walker::walk_value_mut; use kclvm_runtime::{type_pack_and_check, ApiFunc, RuntimeErrorType, ValueRef, PKG_PATH_PREFIX}; use kclvm_sema::{builtin, plugin}; +use crate::func::FunctionProxy; use crate::{error as kcl_error, GLOBAL_LEVEL, INNER_LEVEL}; use crate::{EvalResult, Evaluator}; -/// Impl TypedResultWalker for Evaluator to visit AST nodes to emit LLVM IR. +/// Impl TypedResultWalker for Evaluator to visit AST nodes to evaluate the result. impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { type Result = EvalResult; @@ -259,7 +262,7 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { if let Some(modules) = self.program.pkgs.get(&import_stmt.path.node) { self.push_pkgpath(&pkgpath); self.init_scope(&pkgpath); - self.compile_ast_modules(&modules); + self.compile_ast_modules(modules); self.pop_pkgpath(); } } @@ -549,7 +552,7 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { self.list_append(&mut list_value, &value); } let mut dict_value = self.dict_value(); - // kwargs + // keyword arguments for keyword in &call_expr.keywords { let name = &keyword.node.arg.node.names[0]; let value = if let Some(value) = &keyword.node.value { @@ -565,16 +568,20 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { -1, ); } - let pkgpath = self.current_pkgpath(); - let is_in_schema = self.is_in_schema() || self.is_in_schema_expr(); - Ok(invoke_function( - &func, - &mut list_value, - &dict_value, - &mut self.runtime_ctx.borrow_mut(), - &pkgpath, - is_in_schema, - )) + if let Some(proxy) = func.try_get_proxy() { + Ok(self.invoke_proxy_function(proxy, &list_value, &dict_value)) + } else { + let pkgpath = self.current_pkgpath(); + let is_in_schema = self.is_in_schema() || self.is_in_schema_expr(); + Ok(invoke_function( + &func, + &mut list_value, + &dict_value, + &mut self.runtime_ctx.borrow_mut(), + &pkgpath, + is_in_schema, + )) + } } fn walk_subscript(&self, subscript: &'ctx ast::Subscript) -> Self::Result { @@ -659,12 +666,10 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { }; } then_value + } else if let Some(orelse) = &list_if_item_expr.orelse { + self.walk_expr(orelse).expect(kcl_error::COMPILE_ERROR_MSG) } else { - if let Some(orelse) = &list_if_item_expr.orelse { - self.walk_expr(orelse).expect(kcl_error::COMPILE_ERROR_MSG) - } else { - self.none_value() - } + self.none_value() }) } @@ -719,12 +724,10 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { let is_truth = self.value_is_truthy(&cond); Ok(if is_truth { self.walk_config_entries(&config_if_entry_expr.items)? + } else if let Some(orelse) = &config_if_entry_expr.orelse { + self.walk_expr(orelse).expect(kcl_error::COMPILE_ERROR_MSG) } else { - if let Some(orelse) = &config_if_entry_expr.orelse { - self.walk_expr(orelse).expect(kcl_error::COMPILE_ERROR_MSG) - } else { - self.none_value() - } + self.none_value() }) } @@ -785,12 +788,27 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { todo!() } - fn walk_lambda_expr(&self, _lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result { - let _pkgpath = &self.current_pkgpath(); - // Higher-order lambda requires capturing the current lambda closure variable - // as well as the closure of a more external scope. - let _last_closure_map = self.get_current_inner_scope_variable_map(); - todo!() + fn walk_lambda_expr(&self, lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result { + // Push the current lambda scope level in the lambda stack. + self.push_lambda(self.scope_level() + 1); + let func = + |s: &Evaluator, lambda_expr: &ast::LambdaExpr, args: &ValueRef, kwargs: &ValueRef| { + s.enter_scope(); + // Evaluate arguments and keyword arguments and store values to local variables. + s.walk_arguments(&lambda_expr.args, args, kwargs); + let result = s + .walk_stmts(&lambda_expr.body) + .expect(kcl_error::COMPILE_ERROR_MSG); + s.leave_scope(); + result + }; + // Pop lambda + self.pop_lambda(); + let func = Arc::new(func); + let func_proxy = FunctionProxy::new(lambda_expr.clone(), func.clone()); + // Add function to the global state + let index = self.add_function(func_proxy); + Ok(self.proxy_function_value(index)) } fn walk_keyword(&self, _keyword: &'ctx ast::Keyword) -> Self::Result { @@ -988,8 +1006,9 @@ impl<'ctx> Evaluator<'ctx> { } else if self.is_in_lambda() { let value = right_value.clone().expect(kcl_error::INTERNAL_ERROR_MSG); // If variable exists in the scope and update it, if not, add it to the scope. - if !self.store_variable_in_current_scope(name, value) { - todo!() + if !self.store_variable_in_current_scope(name, value.clone()) { + self.add_variable(name, self.undefined_value()); + self.store_variable(name, value); } } else { let is_local_var = self.is_local_var(name); @@ -1073,11 +1092,55 @@ impl<'ctx> Evaluator<'ctx> { pub fn walk_arguments( &self, - _arguments: &'ctx Option>, - _args: ValueRef, - _kwargs: ValueRef, + arguments: &'ctx Option>, + args: &ValueRef, + kwargs: &ValueRef, ) { - todo!() + // Arguments names and defaults + let (arg_names, arg_defaults) = if let Some(args) = &arguments { + let names = &args.node.args; + let defaults = &args.node.defaults; + ( + names.iter().map(|identifier| &identifier.node).collect(), + defaults.iter().collect(), + ) + } else { + (vec![], vec![]) + }; + // Default parameter values + for (arg_name, value) in arg_names.iter().zip(arg_defaults.iter()) { + let arg_value = if let Some(value) = value { + self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG) + } else { + self.none_value() + }; + // Arguments are immutable, so we place them in different scopes. + self.store_argument_in_current_scope(&arg_name.get_name()); + self.walk_identifier_with_ctx(arg_name, &ast::ExprContext::Store, Some(arg_value)) + .expect(kcl_error::COMPILE_ERROR_MSG); + } + let argument_len = args.len(); + for (i, arg_name) in arg_names.iter().enumerate() { + // Positional arguments + let is_in_range = i < argument_len; + if is_in_range { + let arg_value = match args.list_get_option(i as isize) { + Some(v) => v, + None => self.undefined_value(), + }; + self.store_variable(&arg_name.names[0].node, arg_value); + } else { + break; + } + } + // Keyword arguments + for arg_name in arg_names.iter() { + let name = &arg_name.names[0].node; + if let Some(arg) = kwargs.dict_get_value(name) { + // Find argument name in the scope + self.store_variable(&arg_name.names[0].node, arg); + } + } } pub fn walk_generator( @@ -1152,7 +1215,7 @@ impl<'ctx> Evaluator<'ctx> { .expect(kcl_error::COMPILE_ERROR_MSG); let key = self.walk_expr(elt).expect(kcl_error::COMPILE_ERROR_MSG); let op = op.expect(kcl_error::INTERNAL_ERROR_MSG); - self.dict_insert(collection_value, &key.as_str(), &value, &op, -1); + self.dict_insert(collection_value, &key.as_str(), &value, op, -1); } } } else { diff --git a/kclvm/evaluator/src/scope.rs b/kclvm/evaluator/src/scope.rs index 7fab17487..dc1d1f44b 100644 --- a/kclvm/evaluator/src/scope.rs +++ b/kclvm/evaluator/src/scope.rs @@ -2,7 +2,7 @@ use crate::error as kcl_error; use indexmap::{IndexMap, IndexSet}; use kclvm_ast::ast; use kclvm_error::Position; -use kclvm_runtime::{ValueRef, _kclvm_get_fn_ptr_by_name, MAIN_PKG_PATH, PKG_PATH_PREFIX}; +use kclvm_runtime::{ValueRef, _kclvm_get_fn_ptr_by_name, MAIN_PKG_PATH}; use kclvm_sema::{builtin, pkgpath_without_prefix, plugin}; use crate::{EvalResult, Evaluator, GLOBAL_LEVEL}; @@ -23,8 +23,6 @@ pub struct Scope { pub schema_scalar_idx: usize, /// Scope normal variables pub variables: IndexMap, - /// Scope closures referenced by internal scope. - pub closures: IndexMap, /// Potential arguments in the current scope, such as schema/lambda arguments. pub arguments: IndexSet, /// Schema self denotes the scope that is belonged to a schema. @@ -77,7 +75,7 @@ impl<'ctx> Evaluator<'ctx> { let function_name = format!("{}_{}", builtin::KCL_BUILTIN_FUNCTION_MANGLE_PREFIX, symbol); let function_ptr = _kclvm_get_fn_ptr_by_name(&function_name); - self.add_variable(symbol, self._function_value_with_ptr(function_ptr)); + self.add_variable(symbol, self.function_value_with_ptr(function_ptr)); } self.enter_scope(); } @@ -153,6 +151,19 @@ impl<'ctx> Evaluator<'ctx> { } } + /// Store the argument named `name` in the current scope. + pub(crate) fn store_argument_in_current_scope(&self, name: &str) { + // Find argument name in the scope + let current_pkgpath = self.current_pkgpath(); + let mut ctx = self.ctx.borrow_mut(); + let pkg_scopes = &mut ctx.pkg_scopes; + let msg = format!("pkgpath {} is not found", current_pkgpath); + let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); + let index = scopes.len() - 1; + let arguments_mut = &mut scopes[index].arguments; + arguments_mut.insert(name.to_string()); + } + /// Store the variable named `name` with `value` from the current scope, return false when not found pub fn store_variable_in_current_scope(&self, name: &str, value: ValueRef) -> bool { // Find argument name in the scope @@ -163,7 +174,8 @@ impl<'ctx> Evaluator<'ctx> { let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); let index = scopes.len() - 1; let variables = &mut scopes[index].variables; - if let Some(_) = variables.get(&name.to_string()) { + // If exists and update it + if variables.get(&name.to_string()).is_some() { variables.insert(name.to_string(), value); return true; } @@ -181,6 +193,7 @@ impl<'ctx> Evaluator<'ctx> { for i in 0..scopes.len() { let index = scopes.len() - i - 1; let variables = &mut scopes[index].variables; + // If exists and update it if variables.get(&name.to_string()).is_some() { variables.insert(name.to_string(), value); return true; @@ -315,12 +328,6 @@ impl<'ctx> Evaluator<'ctx> { // System module if builtin::STANDARD_SYSTEM_MODULE_NAMES_WITH_AT.contains(&pkgpath.as_str()) { let pkgpath = &pkgpath[1..]; - let _mangle_func_name = format!( - "{}{}_{}", - builtin::KCL_SYSTEM_MODULE_MANGLE_PREFIX, - pkgpath_without_prefix!(pkgpath), - name - ); let value = if pkgpath == builtin::system_module::UNITS && builtin::system_module::UNITS_FIELD_NAMES.contains(&name) { @@ -332,15 +339,21 @@ impl<'ctx> Evaluator<'ctx> { self.float_value(value_float) } } else { - todo!() + let func_name = format!( + "{}{}_{}", + builtin::KCL_SYSTEM_MODULE_MANGLE_PREFIX, + pkgpath, + name + ); + let function_ptr = _kclvm_get_fn_ptr_by_name(&func_name); + self.function_value_with_ptr(function_ptr) }; Ok(value) } // Plugin pkgpath else if pkgpath.starts_with(plugin::PLUGIN_PREFIX_WITH_AT) { - let _null_fn_ptr = 0; + // Strip the @kcl_plugin to kcl_plugin. let name = format!("{}.{}", &pkgpath[1..], name); - let _none_value = self.none_value(); return Ok(ValueRef::func( 0, 0, @@ -404,55 +417,6 @@ impl<'ctx> Evaluator<'ctx> { } } - /// Get closure map in the current inner scope. - pub(crate) fn get_current_inner_scope_variable_map(&self) -> ValueRef { - let var_map = { - let last_lambda_scope = self.last_lambda_scope(); - // Get variable map in the current scope. - let pkgpath = self.current_pkgpath(); - let pkgpath = if !pkgpath.starts_with(PKG_PATH_PREFIX) && pkgpath != MAIN_PKG_PATH { - format!("{}{}", PKG_PATH_PREFIX, pkgpath) - } else { - pkgpath - }; - let ctx = self.ctx.borrow(); - let pkg_scopes = &ctx.pkg_scopes; - let scopes = pkg_scopes - .get(&pkgpath) - .unwrap_or_else(|| panic!("package {} is not found", pkgpath)); - let current_scope = scopes.len() - 1; - // Get last closure map. - - if current_scope >= last_lambda_scope && last_lambda_scope > 0 { - let _variables = &scopes[last_lambda_scope].variables; - // todo: lambda closure in the lambda. - let ptr: Option = None; - let var_map = match ptr { - Some(ptr) => ptr.clone(), - None => self.dict_value(), - }; - // Get variable map including schema in the current scope. - for i in last_lambda_scope..current_scope + 1 { - let variables = &scopes - .get(i) - .expect(kcl_error::INTERNAL_ERROR_MSG) - .variables; - for (_key, _ptr) in variables { - todo!() - } - } - var_map - } else { - self.dict_value() - } - }; - // Capture schema `self` closure. - if self.is_in_schema() { - todo!() - } - var_map - } - /// Load value from name. pub fn load_value(&self, pkgpath: &str, names: &[&str]) -> EvalResult { if names.is_empty() { diff --git a/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_0.snap b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_0.snap new file mode 100644 index 000000000..8ab678285 --- /dev/null +++ b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_0.snap @@ -0,0 +1,6 @@ +--- +source: evaluator/src/tests.rs +expression: "format!(\"{}\", evaluator.run().unwrap().1)" +--- +a: 2 +b: 4 diff --git a/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_1.snap b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_1.snap new file mode 100644 index 000000000..8ab678285 --- /dev/null +++ b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_1.snap @@ -0,0 +1,6 @@ +--- +source: evaluator/src/tests.rs +expression: "format!(\"{}\", evaluator.run().unwrap().1)" +--- +a: 2 +b: 4 diff --git a/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_2.snap b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_2.snap new file mode 100644 index 000000000..e4e42ff0f --- /dev/null +++ b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_2.snap @@ -0,0 +1,7 @@ +--- +source: evaluator/src/tests.rs +expression: "format!(\"{}\", evaluator.run().unwrap().1)" +--- +a: 2 +b: 3 +c: 2 diff --git a/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_3.snap b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_3.snap new file mode 100644 index 000000000..05da1a0b2 --- /dev/null +++ b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_3.snap @@ -0,0 +1,5 @@ +--- +source: evaluator/src/tests.rs +expression: "format!(\"{}\", evaluator.run().unwrap().1)" +--- +x: 2 diff --git a/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_4.snap b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_4.snap new file mode 100644 index 000000000..536a7cb4e --- /dev/null +++ b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_4.snap @@ -0,0 +1,5 @@ +--- +source: evaluator/src/tests.rs +expression: "format!(\"{}\", evaluator.run().unwrap().1)" +--- +x: 6 diff --git a/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_5.snap b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_5.snap new file mode 100644 index 000000000..2301a537b --- /dev/null +++ b/kclvm/evaluator/src/snapshots/kclvm_evaluator__tests__lambda_5.snap @@ -0,0 +1,6 @@ +--- +source: evaluator/src/tests.rs +expression: "format!(\"{}\", evaluator.run().unwrap().1)" +--- +x: + value: 4 diff --git a/kclvm/evaluator/src/tests.rs b/kclvm/evaluator/src/tests.rs index 2401bf962..a018149ac 100644 --- a/kclvm/evaluator/src/tests.rs +++ b/kclvm/evaluator/src/tests.rs @@ -241,3 +241,49 @@ b = "${a: #json}" evaluator_snapshot! {literal_2, r#"a = 1Mi b = 2K "#} + +evaluator_snapshot! {lambda_0, r#"f = lambda x {x * 2} +a = f(1) +b = f(2) +"#} +evaluator_snapshot! {lambda_1, r#"a = lambda x {x * 2}(1) +b = lambda x {x * 2}(2) +"#} +evaluator_snapshot! {lambda_2, r#"import math +a = math.log(10) +b = len("abc") +c = len([1, 2]) +"#} +evaluator_snapshot! {lambda_3, r#" +x = lambda { + a = 1 + lambda { + a + 1 + }() +}() +"#} +evaluator_snapshot! {lambda_4, r#" +x = lambda { + a = 1 + b = 2 + lambda x { + a + b + x + }(3) +}() +"#} +evaluator_snapshot! {lambda_5, r#" +func = lambda config: {str:} { + x = 1 + lambda { + y = 1 + lambda { + z = 1 + lambda { + {value = x + y + z + config.key} + }() + }() + }() +} + +x = func({key = 1}) +"#} diff --git a/kclvm/evaluator/src/value.rs b/kclvm/evaluator/src/value.rs index 59749fd92..416c9f0bb 100644 --- a/kclvm/evaluator/src/value.rs +++ b/kclvm/evaluator/src/value.rs @@ -1,8 +1,9 @@ /* Value methods */ -use kclvm_runtime::{ValueRef, _kclvm_get_fn_ptr_by_name}; +use generational_arena::Index; +use kclvm_runtime::ValueRef; -use crate::{function::FunctionValue, Evaluator}; +use crate::Evaluator; impl<'ctx> Evaluator<'ctx> { /// Construct a 64-bit int value using i64 @@ -63,28 +64,16 @@ impl<'ctx> Evaluator<'ctx> { pub(crate) fn unit_value(&self, v: f64, raw: i64, unit: &str) -> ValueRef { ValueRef::unit(v, raw, unit) } + /// Construct a function value using a native function. - pub(crate) fn _function_value(&self, function: FunctionValue) -> ValueRef { - ValueRef::func( - function.get_fn_ptr() as u64, - 0, - self.list_value(), - "", - "", - false, - ) + #[inline] + pub(crate) fn proxy_function_value(&self, proxy: Index) -> ValueRef { + ValueRef::proxy_func(proxy) } + /// Construct a function value using a native function. - pub(crate) fn _function_value_with_ptr(&self, function_ptr: u64) -> ValueRef { + #[inline] + pub(crate) fn function_value_with_ptr(&self, function_ptr: u64) -> ValueRef { ValueRef::func(function_ptr, 0, self.list_value(), "", "", false) } - /// Construct a closure function value with the closure variable. - pub(crate) fn _closure_value(&self, function: FunctionValue, closure: ValueRef) -> ValueRef { - ValueRef::func(function.get_fn_ptr() as u64, 0, closure, "", "", false) - } - /// Construct a builtin function value using the function name. - pub(crate) fn _builtin_function_value(&self, name: &str) -> ValueRef { - let func = _kclvm_get_fn_ptr_by_name(name); - ValueRef::func(func, 0, self.list_value(), "", "", false) - } } diff --git a/kclvm/runtime/Cargo.toml b/kclvm/runtime/Cargo.toml index d5479fc7b..cc0fe715b 100644 --- a/kclvm/runtime/Cargo.toml +++ b/kclvm/runtime/Cargo.toml @@ -9,6 +9,7 @@ serde_json = {package = "serde_json", version = "= 1.0.86"} serde = { version = "1", features = ["derive"] } serde_yaml = "0.9.32" +generational-arena = "0.2.9" base64 = "0.13.0" libc = "0.2.112" itertools = "0.10.3" diff --git a/kclvm/runtime/src/api/kclvm.rs b/kclvm/runtime/src/api/kclvm.rs index 798987525..a67c0445e 100644 --- a/kclvm/runtime/src/api/kclvm.rs +++ b/kclvm/runtime/src/api/kclvm.rs @@ -1,6 +1,7 @@ //! Copyright The KCL Authors. All rights reserved. use crate::{new_mut_ptr, IndexMap, PlanOptions}; +use generational_arena::Index; use indexmap::IndexSet; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -264,6 +265,9 @@ pub struct FuncValue { pub name: String, pub runtime_type: String, pub is_external: bool, + /// Proxy functions represent the saved functions of the runtime itself, + /// rather than executing KCL defined functions or plugin functions. + pub proxy: Option, } #[allow(non_snake_case)] @@ -402,12 +406,6 @@ impl Context { } } -#[derive(PartialEq, Eq, Clone, Default, Debug)] -pub struct FuncHandler { - pub namespace: String, - pub fn_pointer: u64, -} - #[repr(C)] #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub enum Kind { diff --git a/kclvm/runtime/src/context/api.rs b/kclvm/runtime/src/context/api.rs index bad2fc56e..2b3253c4c 100644 --- a/kclvm/runtime/src/context/api.rs +++ b/kclvm/runtime/src/context/api.rs @@ -143,7 +143,7 @@ pub unsafe extern "C" fn kclvm_context_set_kcl_line_col( #[no_mangle] #[runtime_fn] pub unsafe extern "C" fn kclvm_scope_new() -> *mut kclvm_eval_scope_t { - Box::into_raw(Box::new(LazyEvalScope::default())) + Box::into_raw(Box::default()) } #[no_mangle] diff --git a/kclvm/runtime/src/file/mod.rs b/kclvm/runtime/src/file/mod.rs index 1536f40a1..15f681602 100644 --- a/kclvm/runtime/src/file/mod.rs +++ b/kclvm/runtime/src/file/mod.rs @@ -15,7 +15,7 @@ pub extern "C" fn kclvm_file_read( let kwargs = ptr_as_ref(kwargs); let ctx = mut_ptr_as_ref(ctx); - if let Some(x) = get_call_args_str(&args, &kwargs, 0, Some("filepath")) { + if let Some(x) = get_call_args_str(args, kwargs, 0, Some("filepath")) { let contents = fs::read_to_string(&x) .unwrap_or_else(|e| panic!("failed to access the file '{}': {}", x, e)); @@ -37,7 +37,7 @@ pub extern "C" fn kclvm_file_glob( let kwargs = ptr_as_ref(kwargs); let ctx = mut_ptr_as_ref(ctx); - let pattern = get_call_args_str(&args, &kwargs, 0, Some("pattern")) + let pattern = get_call_args_str(args, kwargs, 0, Some("pattern")) .expect("glob() takes exactly one argument (0 given)"); let mut matched_paths = vec![]; @@ -89,7 +89,7 @@ pub extern "C" fn kclvm_file_exists( let kwargs = ptr_as_ref(kwargs); let ctx = mut_ptr_as_ref(ctx); - if let Some(path) = get_call_args_str(&args, &kwargs, 0, Some("filepath")) { + if let Some(path) = get_call_args_str(args, kwargs, 0, Some("filepath")) { let exist = Path::new(&path).exists(); return ValueRef::bool(exist).into_raw(ctx); } @@ -110,7 +110,7 @@ pub extern "C" fn kclvm_file_abs( let kwargs = ptr_as_ref(kwargs); let ctx = mut_ptr_as_ref(ctx); - if let Some(path) = get_call_args_str(&args, &kwargs, 0, Some("filepath")) { + if let Some(path) = get_call_args_str(args, kwargs, 0, Some("filepath")) { if let Ok(abs_path) = Path::new(&path).canonicalize() { return ValueRef::str(abs_path.to_str().unwrap()).into_raw(ctx); } else { diff --git a/kclvm/runtime/src/value/val.rs b/kclvm/runtime/src/value/val.rs index 7371ced5c..cd6dd3c42 100644 --- a/kclvm/runtime/src/value/val.rs +++ b/kclvm/runtime/src/value/val.rs @@ -1,5 +1,7 @@ //! Copyright The KCL Authors. All rights reserved. +use generational_arena::Index; + use crate::*; impl ValueRef { @@ -81,6 +83,20 @@ impl ValueRef { name: name.to_string(), runtime_type: runtime_type.to_string(), is_external, + proxy: None, + }))) + } + + /// New a proxy function with function index in the function list. + pub fn proxy_func(proxy: Index) -> Self { + Self::from(Value::func_value(Box::new(FuncValue { + fn_ptr: 0, + check_fn_ptr: 0, + closure: ValueRef::undefined(), + name: "".to_string(), + runtime_type: "".to_string(), + is_external: false, + proxy: Some(proxy), }))) } } diff --git a/kclvm/runtime/src/value/val_clone.rs b/kclvm/runtime/src/value/val_clone.rs index dd9865b5c..c4dd47e01 100644 --- a/kclvm/runtime/src/value/val_clone.rs +++ b/kclvm/runtime/src/value/val_clone.rs @@ -29,6 +29,7 @@ impl ValueRef { name: v.name.clone(), runtime_type: v.runtime_type.clone(), is_external: v.is_external, + proxy: v.proxy.clone(), })))), }, Value::bool_value(ref v) => ValueRef { diff --git a/kclvm/runtime/src/value/val_func.rs b/kclvm/runtime/src/value/val_func.rs index b84d84c0d..a743a2fcc 100644 --- a/kclvm/runtime/src/value/val_func.rs +++ b/kclvm/runtime/src/value/val_func.rs @@ -1,10 +1,22 @@ use std::{mem::transmute_copy, os::raw::c_char}; +use generational_arena::Index; + use crate::{ kclvm_plugin_invoke, ptr_as_ref, schema_config_meta, BacktraceFrame, Context, SchemaTypeFunc, ValueRef, }; +impl ValueRef { + /// Try get the proxy function index + pub fn try_get_proxy(&self) -> Option { + match &*self.rc.borrow() { + crate::Value::func_value(func) => func.proxy.clone(), + _ => None, + } + } +} + /// Invoke functions with arguments and keyword arguments. pub fn invoke_function( func: &ValueRef, diff --git a/kclvm/runtime/src/value/val_schema.rs b/kclvm/runtime/src/value/val_schema.rs index 3cd44c330..27f61c1e8 100644 --- a/kclvm/runtime/src/value/val_schema.rs +++ b/kclvm/runtime/src/value/val_schema.rs @@ -182,12 +182,9 @@ impl ValueRef { /// Set the schema instance value with arguments and keyword arguments. pub fn set_schema_args(&mut self, args: &ValueRef, kwargs: &ValueRef) { - match &mut *self.rc.borrow_mut() { - Value::schema_value(ref mut schema) => { - schema.args = args.clone(); - schema.kwargs = kwargs.clone(); - } - _ => {} + if let Value::schema_value(ref mut schema) = &mut *self.rc.borrow_mut() { + schema.args = args.clone(); + schema.kwargs = kwargs.clone(); } } diff --git a/test/grammar/lambda/closure_11/main.k b/test/grammar/lambda/closure_11/main.k new file mode 100644 index 000000000..5c7e6ff4c --- /dev/null +++ b/test/grammar/lambda/closure_11/main.k @@ -0,0 +1,5 @@ +b = 1 +# Use (i) to prevent the schema expr parse conflict +data = [lambda x, y=(i), z=(b) { + x + y + i +}(i) for i in [1, 2, 3]] diff --git a/test/grammar/lambda/closure_11/stdout.golden b/test/grammar/lambda/closure_11/stdout.golden new file mode 100644 index 000000000..901401250 --- /dev/null +++ b/test/grammar/lambda/closure_11/stdout.golden @@ -0,0 +1,5 @@ +b: 1 +data: +- 3 +- 6 +- 9