From d84376c12acec04e3394e363651ef0ed5555180c Mon Sep 17 00:00:00 2001 From: peefy Date: Tue, 9 Apr 2024 21:37:49 +0800 Subject: [PATCH] feat: impl schema instances in all package path Signed-off-by: peefy --- kclvm/evaluator/src/schema.rs | 15 ++-- kclvm/runtime/src/api/kclvm.rs | 11 ++- kclvm/runtime/src/value/api.rs | 87 +++++++++++-------- kclvm/sema/src/resolver/attr.rs | 11 ++- .../schema/instances/complex/complex_5/main.k | 5 ++ .../instances/complex/complex_5/pkg/pkg.k | 12 +++ .../instances/complex/complex_5/stdout.golden | 23 +++++ 7 files changed, 117 insertions(+), 47 deletions(-) create mode 100644 test/grammar/schema/instances/complex/complex_5/main.k create mode 100644 test/grammar/schema/instances/complex/complex_5/pkg/pkg.k create mode 100644 test/grammar/schema/instances/complex/complex_5/stdout.golden diff --git a/kclvm/evaluator/src/schema.rs b/kclvm/evaluator/src/schema.rs index d5da2505f..1d18340fd 100644 --- a/kclvm/evaluator/src/schema.rs +++ b/kclvm/evaluator/src/schema.rs @@ -6,7 +6,7 @@ use generational_arena::Index; use indexmap::IndexMap; use kclvm_ast::ast; use kclvm_ast::walker::TypedResultWalker; -use kclvm_runtime::{schema_runtime_type, ConfigEntryOperationKind, ValueRef, MAIN_PKG_PATH}; +use kclvm_runtime::{schema_runtime_type, ConfigEntryOperationKind, ValueRef}; use crate::lazy::{merge_setters, LazyEvalScope, LazyEvalScopeRef}; use crate::proxy::{call_schema_body, call_schema_check}; @@ -490,14 +490,19 @@ pub(crate) fn schema_with_config( // avoid unexpected non idempotent calls. For example, I instantiated a MySchema in pkg1, // but the length of the list returned by calling the instances method in other packages // is uncertain. - if instance_pkgpath.is_empty() || instance_pkgpath == MAIN_PKG_PATH { + { let mut ctx = s.runtime_ctx.borrow_mut(); // Record schema instance in the context if !ctx.instances.contains_key(&runtime_type) { - ctx.instances.insert(runtime_type.clone(), vec![]); + ctx.instances + .insert(runtime_type.clone(), IndexMap::default()); + } + let pkg_instance_map = ctx.instances.get_mut(&runtime_type).unwrap(); + if !pkg_instance_map.contains_key(&instance_pkgpath) { + pkg_instance_map.insert(instance_pkgpath.clone(), vec![]); } - ctx.instances - .get_mut(&runtime_type) + pkg_instance_map + .get_mut(&instance_pkgpath) .unwrap() .push(schema_dict.clone()); } diff --git a/kclvm/runtime/src/api/kclvm.rs b/kclvm/runtime/src/api/kclvm.rs index a67c0445e..976ad1476 100644 --- a/kclvm/runtime/src/api/kclvm.rs +++ b/kclvm/runtime/src/api/kclvm.rs @@ -236,9 +236,13 @@ pub struct DictValue { #[derive(PartialEq, Clone, Default, Debug)] pub struct SchemaValue { + /// Schema name without the package path prefix. pub name: String, + /// Schema instance package path, note is it not the schema definition package path. pub pkgpath: String, + /// Schema values. pub config: Box, + /// Schema instance config keys e.g., "a" in `MySchema {a = "foo"}` pub config_keys: Vec, /// schema config meta information including filename, line and column. pub config_meta: ValueRef, @@ -338,8 +342,9 @@ pub struct Context { pub imported_pkgpath: HashSet, /// Runtime arguments for the option function. pub app_args: HashMap, - /// All schema instances - pub instances: HashMap>, + /// All schema instances, the first key is the schema runtime type and + /// the second key is the schema instance package path + pub instances: IndexMap>>, /// All schema types pub all_schemas: HashMap, /// Import graph @@ -396,7 +401,7 @@ impl BacktraceFrame { impl Context { pub fn new() -> Self { Context { - instances: HashMap::new(), + instances: IndexMap::default(), panic_info: PanicInfo { kcl_func: "kclvm_main".to_string(), ..Default::default() diff --git a/kclvm/runtime/src/value/api.rs b/kclvm/runtime/src/value/api.rs index 351e42c3f..85f71b9fc 100644 --- a/kclvm/runtime/src/value/api.rs +++ b/kclvm/runtime/src/value/api.rs @@ -290,15 +290,18 @@ pub unsafe extern "C" fn kclvm_value_schema_with_config( Some(args.clone()), Some(kwargs.clone()), ); - if record_instance.is_truthy() - && (instance_pkgpath.is_empty() || instance_pkgpath == MAIN_PKG_PATH) - { + if record_instance.is_truthy() { // Record schema instance in the context if !ctx.instances.contains_key(&runtime_type) { - ctx.instances.insert(runtime_type.clone(), vec![]); + ctx.instances + .insert(runtime_type.clone(), IndexMap::default()); + } + let pkg_instance_map = ctx.instances.get_mut(&runtime_type).unwrap(); + if !pkg_instance_map.contains_key(&instance_pkgpath) { + pkg_instance_map.insert(instance_pkgpath.clone(), vec![]); } - ctx.instances - .get_mut(&runtime_type) + pkg_instance_map + .get_mut(&instance_pkgpath) .unwrap() .push(schema_dict.clone()); } @@ -2060,44 +2063,29 @@ pub unsafe extern "C" fn kclvm_schema_instances( let kwargs = ptr_as_ref(kwargs); if let Some(val) = args.pop_arg_first() { let function = val.as_function(); - let main_pkg = args.arg_0().or_else(|| kwargs.kwarg("main_pkg")); - let main_pkg = if let Some(v) = main_pkg { + let full_pkg = args.arg_0().or_else(|| kwargs.kwarg("full_pkg")); + let full_pkg = if let Some(v) = full_pkg { v.is_truthy() } else { - true + false }; let runtime_type = &function.runtime_type; if ctx_ref.instances.contains_key(runtime_type) { let mut list = ValueRef::list(None); - for v in ctx_ref.instances.get(runtime_type).unwrap() { - if v.is_schema() { - let schema = v.as_schema(); - if main_pkg { - if schema.pkgpath == MAIN_PKG_PATH { - list.list_append(v) - } - } else { - list.list_append(v) - } - } else if v.is_dict() { - let runtime_type = v - .get_potential_schema_type() - .unwrap_or(runtime_type.to_string()); - let names: Vec<&str> = runtime_type.rsplit('.').collect(); - let name = names[0]; - let pkgpath = names[1]; - let v = v.dict_to_schema( - name, - pkgpath, - &[], - &ValueRef::dict(None), - &ValueRef::dict(None), - None, - None, - ); - list.list_append(&v); + let instance_map = ctx_ref.instances.get(runtime_type).unwrap(); + if full_pkg { + for (_, v_list) in instance_map { + collect_schema_instances(&mut list, &v_list, runtime_type) } - } + } else { + // Get the schema instances only located at the main package. + if let Some(v_list) = instance_map.get(MAIN_PKG_PATH) { + collect_schema_instances(&mut list, &v_list, runtime_type) + } + if let Some(v_list) = instance_map.get("") { + collect_schema_instances(&mut list, &v_list, runtime_type) + } + }; list.into_raw(ctx_ref) } else { kclvm_value_List(ctx) @@ -2107,6 +2095,31 @@ pub unsafe extern "C" fn kclvm_schema_instances( } } +fn collect_schema_instances(list: &mut ValueRef, v_list: &[ValueRef], runtime_type: &str) { + for v in v_list { + if v.is_schema() { + list.list_append(v) + } else if v.is_dict() { + let runtime_type = v + .get_potential_schema_type() + .unwrap_or(runtime_type.to_string()); + let names: Vec<&str> = runtime_type.rsplit('.').collect(); + let name = names[0]; + let pkgpath = names[1]; + let v = v.dict_to_schema( + name, + pkgpath, + &[], + &ValueRef::dict(None), + &ValueRef::dict(None), + None, + None, + ); + list.list_append(&v); + } + } +} + #[no_mangle] #[runtime_fn] pub unsafe extern "C" fn kclvm_schema_value_check( diff --git a/kclvm/sema/src/resolver/attr.rs b/kclvm/sema/src/resolver/attr.rs index 25d2dff38..4ace00539 100644 --- a/kclvm/sema/src/resolver/attr.rs +++ b/kclvm/sema/src/resolver/attr.rs @@ -4,7 +4,9 @@ use crate::builtin::system_module::{get_system_module_members, UNITS, UNITS_NUMB use crate::builtin::{get_system_member_function_ty, STRING_MEMBER_FUNCTIONS}; use crate::resolver::Resolver; use crate::ty::TypeKind::Schema; -use crate::ty::{DictType, ModuleKind, Type, TypeKind, TypeRef, SCHEMA_MEMBER_FUNCTIONS}; +use crate::ty::{ + DictType, ModuleKind, Parameter, Type, TypeKind, TypeRef, SCHEMA_MEMBER_FUNCTIONS, +}; use kclvm_error::diagnostic::Range; use kclvm_error::*; @@ -72,7 +74,12 @@ impl<'ctx> Resolver<'ctx> { Arc::new(Type::function( Some(obj.clone()), Type::list_ref(self.any_ty()), - &[], + &[Parameter { + name: "full_pkg".to_string(), + ty: Type::bool_ref(), + // Default value is False + has_default: true, + }], "", false, None, diff --git a/test/grammar/schema/instances/complex/complex_5/main.k b/test/grammar/schema/instances/complex/complex_5/main.k new file mode 100644 index 000000000..c7ae89a01 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_5/main.k @@ -0,0 +1,5 @@ +import pkg + +x0 = pkg.Person {} +all_main_persons = pkg.Person.instances() +all_persons = pkg.Person.instances(True) diff --git a/test/grammar/schema/instances/complex/complex_5/pkg/pkg.k b/test/grammar/schema/instances/complex/complex_5/pkg/pkg.k new file mode 100644 index 000000000..51ea3f748 --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_5/pkg/pkg.k @@ -0,0 +1,12 @@ +schema Data: + id: int = 1 + +schema Person: + name: str = "kcl" + age: int = 1 + data: Data = Data {} + +x0 = Person {} +x1 = Person { + age = 101 +} diff --git a/test/grammar/schema/instances/complex/complex_5/stdout.golden b/test/grammar/schema/instances/complex/complex_5/stdout.golden new file mode 100644 index 000000000..d358727ca --- /dev/null +++ b/test/grammar/schema/instances/complex/complex_5/stdout.golden @@ -0,0 +1,23 @@ +x0: + name: kcl + age: 1 + data: + id: 1 +all_main_persons: +- name: kcl + age: 1 + data: + id: 1 +all_persons: +- name: kcl + age: 1 + data: + id: 1 +- name: kcl + age: 101 + data: + id: 1 +- name: kcl + age: 1 + data: + id: 1