Skip to content

Commit 1675fbe

Browse files
authored
fix: fix eval get val from schema lambda (#1872)
fix: fix evaluator. lambda in schema field and same key with config scope Signed-off-by: he1pa <[email protected]>
1 parent a3b0af6 commit 1675fbe

File tree

6 files changed

+197
-6
lines changed

6 files changed

+197
-6
lines changed

kclvm/compiler/src/codegen/llvm/context.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,9 +2279,47 @@ impl<'ctx> LLVMCodeGenContext<'ctx> {
22792279
{
22802280
match self.resolve_variable_level(name) {
22812281
// Closure variable or local variables
2282-
Some(level) if level > GLOBAL_LEVEL => self.get_variable(name),
2282+
Some(level) if level > GLOBAL_LEVEL => {
2283+
let ctx_stack = self.ctx_stack.borrow();
2284+
let mut result = self.undefined_value();
2285+
let mut found = false;
2286+
for i in 0..ctx_stack.len() {
2287+
let index = ctx_stack.len() - i - 1;
2288+
match &ctx_stack[index] {
2289+
crate::LambdaOrSchemaEvalContext::Schema(_) => {
2290+
let res = self.get_variable_in_schema_or_rule(name);
2291+
2292+
if !res.is_undefined() {
2293+
result = res;
2294+
found = true;
2295+
break;
2296+
}
2297+
}
2298+
crate::LambdaOrSchemaEvalContext::Lambda(_) => {
2299+
let current_pkgpath = self.current_pkgpath();
2300+
let res = self.get_variable_in_pkgpath_from_last_scope(
2301+
name,
2302+
&current_pkgpath,
2303+
);
2304+
if !res.is_undefined() {
2305+
result = res;
2306+
found = true;
2307+
break;
2308+
}
2309+
}
2310+
}
2311+
}
2312+
if found {
2313+
result
2314+
} else {
2315+
// Not found variable in the scope, maybe lambda closures captured in other package scopes.
2316+
self.last_lambda_ctx()
2317+
.map(|ctx| ctx.closure.get(name).cloned().unwrap_or(result.clone()))
2318+
.unwrap_or(result)
2319+
}
2320+
}
22832321
// Schema closure or global variables
2284-
_ => self.get_variable_in_schema(name),
2322+
_ => self.get_variable_in_schema_or_rule(name),
22852323
}
22862324
}
22872325
}

kclvm/evaluator/src/context.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
proxy::{Frame, Proxy},
1212
rule::RuleCaller,
1313
schema::SchemaCaller,
14-
EvalContext, Evaluator,
14+
EvalContext, Evaluator, LambdaOrSchemaEvalContext,
1515
};
1616

1717
impl<'ctx> Evaluator<'ctx> {
@@ -69,7 +69,10 @@ impl<'ctx> Evaluator<'ctx> {
6969
// The scope cover is [lambda.ctx.level, self.scope_level()]
7070
self.push_scope_cover(lambda_ctx.level, level);
7171
}
72-
self.lambda_stack.borrow_mut().push(lambda_ctx);
72+
self.lambda_stack.borrow_mut().push(lambda_ctx.clone());
73+
self.ctx_stack
74+
.borrow_mut()
75+
.push(LambdaOrSchemaEvalContext::Lambda(lambda_ctx));
7376
}
7477

7578
/// Pop a lambda definition scope.
@@ -82,6 +85,7 @@ impl<'ctx> Evaluator<'ctx> {
8285
level: usize,
8386
) {
8487
self.lambda_stack.borrow_mut().pop();
88+
self.ctx_stack.borrow_mut().pop();
8589
// Inner scope function calling.
8690
if frame_pkgpath == current_pkgpath && level >= lambda_ctx.level {
8791
self.pop_scope_cover();
@@ -104,12 +108,16 @@ impl<'ctx> Evaluator<'ctx> {
104108

105109
#[inline]
106110
pub fn push_schema(&self, v: EvalContext) {
107-
self.schema_stack.borrow_mut().push(v);
111+
self.schema_stack.borrow_mut().push(v.clone());
112+
self.ctx_stack
113+
.borrow_mut()
114+
.push(LambdaOrSchemaEvalContext::Schema(v));
108115
}
109116

110117
#[inline]
111118
pub fn pop_schema(&self) {
112119
self.schema_stack.borrow_mut().pop();
120+
self.ctx_stack.borrow_mut().pop();
113121
}
114122

115123
#[inline]

kclvm/evaluator/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pub struct Evaluator<'ctx> {
7373
pub lambda_stack: RefCell<Vec<FunctionEvalContextRef>>,
7474
/// To judge is in the schema statement.
7575
pub schema_stack: RefCell<Vec<EvalContext>>,
76+
/// Order lambda and schema ctx vec
77+
pub ctx_stack: RefCell<Vec<LambdaOrSchemaEvalContext>>,
7678
/// To judge is in the schema expression.
7779
pub schema_expr_stack: RefCell<Vec<()>>,
7880
/// Import names mapping
@@ -91,6 +93,12 @@ pub struct Evaluator<'ctx> {
9193
pub ast_id: RefCell<AstIndex>,
9294
}
9395

96+
#[derive(Clone)]
97+
pub enum LambdaOrSchemaEvalContext {
98+
Schema(EvalContext),
99+
Lambda(FunctionEvalContextRef),
100+
}
101+
94102
#[derive(Clone)]
95103
pub enum EvalContext {
96104
Schema(SchemaEvalContextRef),
@@ -147,6 +155,7 @@ impl<'ctx> Evaluator<'ctx> {
147155
local_vars: RefCell::new(Default::default()),
148156
backtrack_meta: RefCell::new(Default::default()),
149157
ast_id: RefCell::new(AstIndex::default()),
158+
ctx_stack: RefCell::new(Default::default()),
150159
}
151160
}
152161

kclvm/evaluator/src/scope.rs

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ impl<'ctx> Evaluator<'ctx> {
424424
for i in 0..scopes_len {
425425
let index = scopes_len - i - 1;
426426
let variables = &scopes[index].variables;
427+
427428
if let Some(var) = variables.get(name) {
428429
// Closure vars, 2 denotes the builtin scope and the global scope, here is a closure scope.
429430
result = if let Some(lambda_ctx) = self.last_lambda_ctx() {
@@ -458,6 +459,81 @@ impl<'ctx> Evaluator<'ctx> {
458459
}
459460
}
460461

462+
fn get_variable_in_pkgpath_from_last_scope(&self, name: &str, pkgpath: &str) -> ValueRef {
463+
let pkg_scopes = self.pkg_scopes.borrow();
464+
let pkgpath =
465+
if !pkgpath.starts_with(kclvm_runtime::PKG_PATH_PREFIX) && pkgpath != MAIN_PKG_PATH {
466+
format!("{}{}", kclvm_runtime::PKG_PATH_PREFIX, pkgpath)
467+
} else {
468+
pkgpath.to_string()
469+
};
470+
let mut result = self.undefined_value();
471+
// System module
472+
if builtin::STANDARD_SYSTEM_MODULE_NAMES_WITH_AT.contains(&pkgpath.as_str()) {
473+
let pkgpath = &pkgpath[1..];
474+
475+
if pkgpath == builtin::system_module::UNITS
476+
&& builtin::system_module::UNITS_FIELD_NAMES.contains(&name)
477+
{
478+
let value_float: f64 = kclvm_runtime::f64_unit_value(name);
479+
let value_int: u64 = kclvm_runtime::u64_unit_value(name);
480+
if value_int != 1 {
481+
self.int_value(value_int as i64)
482+
} else {
483+
self.float_value(value_float)
484+
}
485+
} else {
486+
let func_name = format!(
487+
"{}{}_{}",
488+
builtin::KCL_SYSTEM_MODULE_MANGLE_PREFIX,
489+
pkgpath,
490+
name
491+
);
492+
let function_ptr = _kclvm_get_fn_ptr_by_name(&func_name);
493+
self.function_value_with_ptr(function_ptr)
494+
}
495+
}
496+
// Plugin pkgpath
497+
else if pkgpath.starts_with(plugin::PLUGIN_PREFIX_WITH_AT) {
498+
// Strip the @kcl_plugin to kcl_plugin.
499+
let name = format!("{}.{}", &pkgpath[1..], name);
500+
ValueRef::func(0, 0, self.undefined_value(), &name, "", true)
501+
// User pkgpath
502+
} else {
503+
// Global or local variables.
504+
let scopes = pkg_scopes
505+
.get(&pkgpath)
506+
.unwrap_or_else(|| panic!("package {} is not found", pkgpath));
507+
// Scopes 0 is builtin scope, Scopes 1 is the global scope, Scopes 2~ are the local scopes
508+
let scopes_len = scopes.len();
509+
510+
let index = scopes_len - 1;
511+
let variables = &scopes[index].variables;
512+
513+
if let Some(var) = variables.get(name) {
514+
// Closure vars, 2 denotes the builtin scope and the global scope, here is a closure scope.
515+
result = if let Some(lambda_ctx) = self.last_lambda_ctx() {
516+
let last_lambda_scope = lambda_ctx.level;
517+
// Local scope variable or lambda closure variable.
518+
let ignore = if let Some((start, end)) = self.scope_covers.borrow().last() {
519+
*start <= index && index <= *end
520+
} else {
521+
false
522+
};
523+
if index >= last_lambda_scope && !ignore {
524+
var.clone()
525+
} else {
526+
lambda_ctx.closure.get(name).unwrap_or(var).clone()
527+
}
528+
} else {
529+
// Not in the lambda, maybe a local variable.
530+
var.clone()
531+
};
532+
}
533+
result
534+
}
535+
}
536+
461537
/// Get closure map in the current inner scope.
462538
pub(crate) fn get_current_closure_map(&self) -> ClosureMap {
463539
// Get variable map in the current scope.
@@ -571,7 +647,45 @@ impl<'ctx> Evaluator<'ctx> {
571647
{
572648
match self.resolve_variable_level(name) {
573649
// Closure variable or local variables
574-
Some(level) if level > GLOBAL_LEVEL => self.get_variable(name),
650+
Some(level) if level > GLOBAL_LEVEL => {
651+
let ctx_stack = self.ctx_stack.borrow();
652+
let mut result = self.undefined_value();
653+
let mut found = false;
654+
for i in 0..ctx_stack.len() {
655+
let index = ctx_stack.len() - i - 1;
656+
match &ctx_stack[index] {
657+
crate::LambdaOrSchemaEvalContext::Schema(_) => {
658+
let res = self.get_variable_in_schema_or_rule(name);
659+
660+
if !res.is_undefined() {
661+
result = res;
662+
found = true;
663+
break;
664+
}
665+
}
666+
crate::LambdaOrSchemaEvalContext::Lambda(_) => {
667+
let current_pkgpath = self.current_pkgpath();
668+
let res = self.get_variable_in_pkgpath_from_last_scope(
669+
name,
670+
&current_pkgpath,
671+
);
672+
if !res.is_undefined() {
673+
result = res;
674+
found = true;
675+
break;
676+
}
677+
}
678+
}
679+
}
680+
if found {
681+
result
682+
} else {
683+
// Not found variable in the scope, maybe lambda closures captured in other package scopes.
684+
self.last_lambda_ctx()
685+
.map(|ctx| ctx.closure.get(name).cloned().unwrap_or(result.clone()))
686+
.unwrap_or(result)
687+
}
688+
}
575689
// Schema closure or global variables
576690
_ => self.get_variable_in_schema_or_rule(name),
577691
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: evaluator/src/tests.rs
3+
expression: "format! (\"{}\", evaluator.run().unwrap().1)"
4+
---
5+
my_dict:
6+
my_field: 1
7+
x: 2

kclvm/evaluator/src/tests.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,21 @@ func = lambda config: {str:} {
398398
x = func({key = 1})
399399
"#}
400400

401+
evaluator_snapshot! {lambda_6, r#"
402+
schema A:
403+
my_field: int
404+
405+
get_field: () -> int = lambda {
406+
my_field
407+
}
408+
409+
_a = A{my_field = 2}
410+
my_dict = {
411+
my_field = 1
412+
x = _a.get_field()
413+
}
414+
"#}
415+
401416
evaluator_snapshot! {schema_0, r#"
402417
schema Person:
403418
name: str = "Alice"

0 commit comments

Comments
 (0)