From 7420d496942e40d8274201ca11c09ed62d1d061d Mon Sep 17 00:00:00 2001 From: peefy Date: Thu, 10 Oct 2024 13:12:51 +0800 Subject: [PATCH] fix: lambda this closure value and config Signed-off-by: peefy --- kclvm/evaluator/src/context.rs | 2 +- kclvm/evaluator/src/func.rs | 27 +++++++++++++++++-- kclvm/evaluator/src/lib.rs | 18 +++++++++++++ kclvm/evaluator/src/node.rs | 12 +++++++-- kclvm/evaluator/src/rule.rs | 17 +++++++++--- kclvm/evaluator/src/schema.rs | 17 ++++++++++++ test/grammar/lambda/in_schema_11/main.k | 20 ++++++++++++++ .../grammar/lambda/in_schema_11/stdout.golden | 7 +++++ 8 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 test/grammar/lambda/in_schema_11/main.k create mode 100644 test/grammar/lambda/in_schema_11/stdout.golden diff --git a/kclvm/evaluator/src/context.rs b/kclvm/evaluator/src/context.rs index 6aab5f6a8..2efa39898 100644 --- a/kclvm/evaluator/src/context.rs +++ b/kclvm/evaluator/src/context.rs @@ -61,7 +61,7 @@ impl<'ctx> Evaluator<'ctx> { ) { // Capture function schema this reference. if let Some(this) = &lambda_ctx.this { - self.push_schema(this.clone()); + self.push_schema(this.eval_ctx()); } // Inner scope function calling. // Note the minimum lambda.ctx.level is 2 for the top level lambda definitions. diff --git a/kclvm/evaluator/src/func.rs b/kclvm/evaluator/src/func.rs index f6e9ef65e..47d2b26a4 100644 --- a/kclvm/evaluator/src/func.rs +++ b/kclvm/evaluator/src/func.rs @@ -23,14 +23,37 @@ pub type FunctionEvalContextRef = Arc; pub struct FunctionEvalContext { /// AST node. pub node: ast::LambdaExpr, - /// Captured schema or rule value. - pub this: Option, + /// Captured schema or rule eval context. + pub this: Option, /// Captured closure local variables. pub closure: ClosureMap, /// The scope level of the function definition. pub level: usize, } +#[derive(Clone)] +pub struct FunctionEvalThis { + pub ctx: EvalContext, + pub value: ValueRef, + pub config: ValueRef, +} + +impl FunctionEvalThis { + #[inline] + pub fn eval_ctx(&self) -> EvalContext { + match &self.ctx { + EvalContext::Schema(schema_ctx) => EvalContext::Schema( + schema_ctx + .borrow() + .new_with_value(&self.value, &self.config), + ), + EvalContext::Rule(rule_ctx) => { + EvalContext::Rule(rule_ctx.borrow().new_with_value(&self.value, &self.config)) + } + } + } +} + /// Proxy functions represent the saved functions of the runtime itself, /// rather than executing KCL defined functions or plugin functions. #[derive(Clone)] diff --git a/kclvm/evaluator/src/lib.rs b/kclvm/evaluator/src/lib.rs index 5f5196fb1..036317fb7 100644 --- a/kclvm/evaluator/src/lib.rs +++ b/kclvm/evaluator/src/lib.rs @@ -96,6 +96,24 @@ pub enum EvalContext { Rule(RuleEvalContextRef), } +impl EvalContext { + #[inline] + pub fn value(&self) -> ValueRef { + match self { + EvalContext::Schema(schema) => schema.borrow().value.clone(), + EvalContext::Rule(rule) => rule.borrow().value.clone(), + } + } + + #[inline] + pub fn config(&self) -> ValueRef { + match self { + EvalContext::Schema(schema) => schema.borrow().config.clone(), + EvalContext::Rule(rule) => rule.borrow().config.clone(), + } + } +} + impl<'ctx> Evaluator<'ctx> { /// New aa Evaluator using the AST program #[inline] diff --git a/kclvm/evaluator/src/node.rs b/kclvm/evaluator/src/node.rs index 68dffc920..be176ce61 100644 --- a/kclvm/evaluator/src/node.rs +++ b/kclvm/evaluator/src/node.rs @@ -16,7 +16,7 @@ use kclvm_sema::{builtin, pkgpath_without_prefix, plugin}; use scopeguard::defer; use crate::error::INTERNAL_ERROR_MSG; -use crate::func::{func_body, FunctionCaller, FunctionEvalContext}; +use crate::func::{func_body, FunctionCaller, FunctionEvalContext, FunctionEvalThis}; use crate::lazy::Setter; use crate::proxy::Proxy; use crate::rule::{rule_body, rule_check, RuleCaller, RuleEvalContext}; @@ -949,7 +949,15 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { let proxy = FunctionCaller::new( FunctionEvalContext { node: lambda_expr.clone(), - this: self.schema_stack.borrow().last().cloned(), + this: self + .schema_stack + .borrow() + .last() + .map(|ctx| FunctionEvalThis { + ctx: ctx.clone(), + value: ctx.value(), + config: ctx.config(), + }), closure: self.get_current_closure_map(), level: self.scope_level() + 1, }, diff --git a/kclvm/evaluator/src/rule.rs b/kclvm/evaluator/src/rule.rs index 56d1e39a3..756ead23b 100644 --- a/kclvm/evaluator/src/rule.rs +++ b/kclvm/evaluator/src/rule.rs @@ -8,7 +8,6 @@ use kclvm_runtime::ValueRef; use scopeguard::defer; use crate::error as kcl_error; -use crate::lazy::LazyEvalScope; use crate::proxy::{call_rule_check, call_schema_body_from_rule}; use crate::Evaluator; @@ -23,7 +22,6 @@ pub type RuleEvalContextRef = Rc>; #[derive(Clone, Debug)] pub struct RuleEvalContext { pub node: Rc, - pub scope: LazyEvalScope, pub value: ValueRef, pub config: ValueRef, pub config_meta: ValueRef, @@ -36,7 +34,6 @@ impl RuleEvalContext { pub fn new_with_node(node: ast::RuleStmt) -> Self { RuleEvalContext { node: Rc::new(node), - scope: LazyEvalScope::default(), value: ValueRef::dict(None), config: ValueRef::dict(None), config_meta: ValueRef::dict(None), @@ -45,6 +42,19 @@ impl RuleEvalContext { } } + /// New a rule evaluation context with schema value and config. + #[inline] + pub fn new_with_value(&self, value: &ValueRef, config: &ValueRef) -> RuleEvalContextRef { + Rc::new(RefCell::new(Self { + node: self.node.clone(), + value: value.clone(), + config: config.clone(), + config_meta: ValueRef::dict(None), + optional_mapping: ValueRef::dict(None), + is_sub_schema: true, + })) + } + /// Reset rule evaluation context state. pub fn reset(&mut self) { self.value = ValueRef::dict(None); @@ -59,7 +69,6 @@ impl RuleEvalContext { pub fn snapshot(&self, config: ValueRef, config_meta: ValueRef) -> RuleEvalContextRef { Rc::new(RefCell::new(Self { node: self.node.clone(), - scope: LazyEvalScope::default(), value: ValueRef::dict(None), config, config_meta, diff --git a/kclvm/evaluator/src/schema.rs b/kclvm/evaluator/src/schema.rs index bddbc84c6..1ab0baee5 100644 --- a/kclvm/evaluator/src/schema.rs +++ b/kclvm/evaluator/src/schema.rs @@ -79,6 +79,23 @@ impl SchemaEvalContext { })) } + /// New a schema evaluation context with schema value and config. + #[inline] + pub fn new_with_value(&self, value: &ValueRef, config: &ValueRef) -> SchemaEvalContextRef { + Rc::new(RefCell::new(Self { + node: self.node.clone(), + index: self.index, + parent: self.parent, + mixins: self.mixins.clone(), + scope: None, + value: value.clone(), + config: config.clone(), + config_meta: ValueRef::dict(None), + optional_mapping: ValueRef::dict(None), + is_sub_schema: true, + })) + } + /// Pass value references from other schema eval context. /// Note that do not change the schema node. pub fn set_info_with_schema(&mut self, other: &SchemaEvalContext) { diff --git a/test/grammar/lambda/in_schema_11/main.k b/test/grammar/lambda/in_schema_11/main.k new file mode 100644 index 000000000..17dd6d27e --- /dev/null +++ b/test/grammar/lambda/in_schema_11/main.k @@ -0,0 +1,20 @@ +protocol StringProtocol: + s: str + +mixin StringMixin for StringProtocol: + add = lambda pref: str { + pref + s + } + +schema String: + mixin [StringMixin] + s: str + add: (str) -> str + +s1 = String { s: "hello" } +s2 = String { s: "world" } + +output = { + s1_add: s1.add("foo ") + s2_add: s2.add("bar ") +} diff --git a/test/grammar/lambda/in_schema_11/stdout.golden b/test/grammar/lambda/in_schema_11/stdout.golden new file mode 100644 index 000000000..805dc3246 --- /dev/null +++ b/test/grammar/lambda/in_schema_11/stdout.golden @@ -0,0 +1,7 @@ +s1: + s: hello +s2: + s: world +output: + s1_add: foo hello + s2_add: bar world