diff --git a/kclvm/evaluator/src/node.rs b/kclvm/evaluator/src/node.rs index 7e0a43f95..9dd43367e 100644 --- a/kclvm/evaluator/src/node.rs +++ b/kclvm/evaluator/src/node.rs @@ -285,6 +285,8 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { ctx: Rc::new(RefCell::new(SchemaEvalContext::new_with_node( schema_stmt.clone(), Index::from_raw_parts(self.frames.borrow().len(), 0), + SchemaEvalContext::get_parent_schema(self, &schema_stmt.parent_name), + SchemaEvalContext::get_mixin_schemas(self, &schema_stmt.mixins), ))), body, check, diff --git a/kclvm/evaluator/src/schema.rs b/kclvm/evaluator/src/schema.rs index 5a9ae3a7e..5e09eada5 100644 --- a/kclvm/evaluator/src/schema.rs +++ b/kclvm/evaluator/src/schema.rs @@ -30,6 +30,8 @@ pub struct SchemaEvalContext { pub node: Rc, pub scope: Option, pub index: Index, + pub parent: Option, + pub mixins: Vec, pub value: ValueRef, pub config: ValueRef, pub config_meta: ValueRef, @@ -39,11 +41,18 @@ pub struct SchemaEvalContext { impl SchemaEvalContext { #[inline] - pub fn new_with_node(node: ast::SchemaStmt, index: Index) -> Self { + pub fn new_with_node( + node: ast::SchemaStmt, + index: Index, + parent: Option, + mixins: Vec, + ) -> Self { Self { node: Rc::new(node), scope: None, index, + parent, + mixins, value: ValueRef::dict(None), config: ValueRef::dict(None), config_meta: ValueRef::dict(None), @@ -58,6 +67,8 @@ impl SchemaEvalContext { Rc::new(RefCell::new(Self { node: self.node.clone(), index: self.index, + parent: self.parent, + mixins: self.mixins.clone(), scope: None, value: ValueRef::dict(None), config, @@ -99,28 +110,17 @@ impl SchemaEvalContext { self.is_sub_schema = true; } - /// Update parent schema and mixin schema information + /// Update parent schema and mixin schema information in the current scope. pub fn get_parent_schema( s: &Evaluator, - ctx: &SchemaEvalContext, - ) -> Option<(Index, SchemaEvalContextRef)> { - if let Some(parent_name) = &ctx.node.parent_name { + parent: &Option>>, + ) -> Option { + if let Some(parent) = parent { let func = s - .walk_identifier_with_ctx(&parent_name.node, &ast::ExprContext::Load, None) + .walk_identifier_with_ctx(&parent.node, &ast::ExprContext::Load, None) .expect(kcl_error::RUNTIME_ERROR_MSG); if let Some(index) = func.try_get_proxy() { - let frame = { - let frames = s.frames.borrow(); - frames - .get(index) - .expect(kcl_error::INTERNAL_ERROR_MSG) - .clone() - }; - if let Proxy::Schema(schema) = &frame.proxy { - Some((index, schema.ctx.clone())) - } else { - None - } + Some(index) } else { None } @@ -132,10 +132,10 @@ impl SchemaEvalContext { /// Update parent schema and mixin schema information pub fn get_mixin_schemas( s: &Evaluator, - ctx: &SchemaEvalContext, - ) -> Vec<(Index, SchemaEvalContextRef)> { + mixins: &[Box>], + ) -> Vec { let mut results = vec![]; - for mixin in &ctx.node.mixins { + for mixin in mixins { let func = s .walk_identifier_with_ctx(&mixin.node, &ast::ExprContext::Load, None) .expect(kcl_error::RUNTIME_ERROR_MSG); @@ -147,8 +147,8 @@ impl SchemaEvalContext { .expect(kcl_error::INTERNAL_ERROR_MSG) .clone() }; - if let Proxy::Schema(schema) = &frame.proxy { - results.push((index, schema.ctx.clone())) + if let Proxy::Schema(_) = &frame.proxy { + results.push(index); } } } @@ -164,8 +164,17 @@ impl SchemaEvalContext { } } } - if let Some((_, parent)) = SchemaEvalContext::get_parent_schema(s, &ctx.borrow()) { - return SchemaEvalContext::has_attr(s, &parent, name); + if let Some(index) = ctx.borrow().parent { + let frame = { + let frames = s.frames.borrow(); + frames + .get(index) + .expect(kcl_error::INTERNAL_ERROR_MSG) + .clone() + }; + if let Proxy::Schema(schema) = &frame.proxy { + return SchemaEvalContext::has_attr(s, &schema.ctx, name); + } } false } @@ -175,8 +184,18 @@ impl SchemaEvalContext { if ctx.borrow().node.index_signature.is_some() { return true; } - if let Some((_, parent)) = SchemaEvalContext::get_parent_schema(s, &ctx.borrow()) { - return SchemaEvalContext::has_index_signature(s, &parent); + + if let Some(index) = ctx.borrow().parent { + let frame = { + let frames = s.frames.borrow(); + frames + .get(index) + .expect(kcl_error::INTERNAL_ERROR_MSG) + .clone() + }; + if let Proxy::Schema(schema) = &frame.proxy { + return SchemaEvalContext::has_index_signature(s, &schema.ctx); + } } false } @@ -199,18 +218,29 @@ impl SchemaEvalContext { } } - /// Init the lazy scope used to + /// Init the lazy scope used to cache the lazy evaluation result. pub fn init_lazy_scope(&mut self, s: &Evaluator, index: Option) { // TODO: cache the lazy scope cross different schema instances. let mut setters = IndexMap::new(); // Parent schema setters - if let Some((idx, parent)) = SchemaEvalContext::get_parent_schema(s, self) { - { - let mut parent = parent.borrow_mut(); + if let Some(idx) = self.parent { + let frame = { + let frames = s.frames.borrow(); + frames + .get(idx) + .expect(kcl_error::INTERNAL_ERROR_MSG) + .clone() + }; + if let Proxy::Schema(schema) = &frame.proxy { + let mut parent = schema.ctx.borrow_mut(); parent.init_lazy_scope(s, Some(idx)); - } - if let Some(scope) = &parent.borrow().scope { - merge_variables_and_setters(&mut self.value, &mut setters, &scope.borrow().setters); + if let Some(scope) = &parent.scope { + merge_variables_and_setters( + &mut self.value, + &mut setters, + &scope.borrow().setters, + ); + } } } // Self setters @@ -220,13 +250,24 @@ impl SchemaEvalContext { &s.emit_setters(&self.node.body, index), ); // Mixin schema setters - for (idx, mixin) in SchemaEvalContext::get_mixin_schemas(s, self) { - { - let mut mixin = mixin.borrow_mut(); - mixin.init_lazy_scope(s, Some(idx)); - } - if let Some(scope) = &mixin.borrow().scope { - merge_variables_and_setters(&mut self.value, &mut setters, &scope.borrow().setters); + for idx in &self.mixins { + let frame = { + let frames = s.frames.borrow(); + frames + .get(*idx) + .expect(kcl_error::INTERNAL_ERROR_MSG) + .clone() + }; + if let Proxy::Schema(schema) = &frame.proxy { + let mut mixin = schema.ctx.borrow_mut(); + mixin.init_lazy_scope(s, Some(*idx)); + if let Some(scope) = &mixin.scope { + merge_variables_and_setters( + &mut self.value, + &mut setters, + &scope.borrow().setters, + ); + } } } self.scope = Some(Rc::new(RefCell::new(LazyEvalScope { diff --git a/kclvm/evaluator/src/ty.rs b/kclvm/evaluator/src/ty.rs index 6136ced8a..0e7e1e824 100644 --- a/kclvm/evaluator/src/ty.rs +++ b/kclvm/evaluator/src/ty.rs @@ -157,7 +157,7 @@ pub fn convert_collection_value(s: &Evaluator, value: &ValueRef, tpe: &str) -> V .clone() }; let schema = if let Proxy::Schema(caller) = &frame.proxy { - // Try convert the config to schema, if failed, return the config + // Try convert the config to schema, if failed, return the config directly. if !SchemaEvalContext::is_fit_config(s, &caller.ctx, value) { return value.clone(); } diff --git a/test/grammar/import/import_with_complex_types_0/kcl.mod b/test/grammar/import/import_with_complex_types_0/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_with_complex_types_0/main.k b/test/grammar/import/import_with_complex_types_0/main.k new file mode 100644 index 000000000..f9a886111 --- /dev/null +++ b/test/grammar/import/import_with_complex_types_0/main.k @@ -0,0 +1,11 @@ +import types + +hosts0: {str:types.Host} = { + foo: {host: "foo.example.net"} + bar: {host: "bar.example.net"} +} + +hosts1: {str:types.HostPort} = { + foo: {host: "foo.example.net", port: 80} + bar: {host: "bar.example.net", port: 80} +} diff --git a/test/grammar/import/import_with_complex_types_0/types/host.k b/test/grammar/import/import_with_complex_types_0/types/host.k new file mode 100644 index 000000000..67182e876 --- /dev/null +++ b/test/grammar/import/import_with_complex_types_0/types/host.k @@ -0,0 +1,5 @@ +schema Host: + host: str + +schema HostPort(Host): + port: int diff --git a/test/grammar/import/import_with_complex_types_1/kcl.mod b/test/grammar/import/import_with_complex_types_1/kcl.mod new file mode 100644 index 000000000..e69de29bb diff --git a/test/grammar/import/import_with_complex_types_1/main.k b/test/grammar/import/import_with_complex_types_1/main.k new file mode 100644 index 000000000..cd4bbee3d --- /dev/null +++ b/test/grammar/import/import_with_complex_types_1/main.k @@ -0,0 +1,11 @@ +import types + +hosts0: [types.Host] = [ + {host: "foo.example.net"} + {host: "bar.example.net"} +] + +hosts1: [types.HostPort] = [ + {host: "foo.example.net", port: 80} + {host: "bar.example.net", port: 80} +] diff --git a/test/grammar/import/import_with_complex_types_1/types/host.k b/test/grammar/import/import_with_complex_types_1/types/host.k new file mode 100644 index 000000000..67182e876 --- /dev/null +++ b/test/grammar/import/import_with_complex_types_1/types/host.k @@ -0,0 +1,5 @@ +schema Host: + host: str + +schema HostPort(Host): + port: int