From 1bc2d9681949d5ea45c4a01f8ce48a669cecc814 Mon Sep 17 00:00:00 2001 From: peefy Date: Thu, 20 Jun 2024 16:22:56 +0800 Subject: [PATCH] feat: optimize if stmt lazy eval Signed-off-by: peefy --- kclvm/compiler/src/codegen/llvm/backtrack.rs | 19 +++ kclvm/compiler/src/codegen/llvm/context.rs | 13 ++ kclvm/compiler/src/codegen/llvm/module.rs | 136 +++++++++++------- kclvm/compiler/src/codegen/llvm/node.rs | 38 +++-- kclvm/compiler/src/codegen/llvm/schema.rs | 132 ++++++++++------- kclvm/evaluator/src/context.rs | 23 ++- kclvm/evaluator/src/lazy.rs | 58 +++++--- kclvm/evaluator/src/node.rs | 15 ++ .../schema/irrelevant_order/if_stmt_0/main.k | 9 ++ .../irrelevant_order/if_stmt_0/stdout.golden | 2 + .../schema/irrelevant_order/if_stmt_10/main.k | 13 ++ .../irrelevant_order/if_stmt_10/stdout.golden | 3 + .../schema/irrelevant_order/if_stmt_7/main.k | 6 + .../irrelevant_order/if_stmt_7/stdout.golden | 1 + .../schema/irrelevant_order/if_stmt_8/main.k | 9 ++ .../irrelevant_order/if_stmt_8/stdout.golden | 2 + .../schema/irrelevant_order/if_stmt_9/main.k | 5 + .../irrelevant_order/if_stmt_9/stdout.golden | 2 + .../schema/stmt_block/stmt_block_0/main.k | 3 +- 19 files changed, 362 insertions(+), 127 deletions(-) create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_10/main.k create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_10/stdout.golden create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_7/main.k create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_7/stdout.golden create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_8/main.k create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_8/stdout.golden create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_9/main.k create mode 100644 test/grammar/schema/irrelevant_order/if_stmt_9/stdout.golden diff --git a/kclvm/compiler/src/codegen/llvm/backtrack.rs b/kclvm/compiler/src/codegen/llvm/backtrack.rs index ed3107962..937ea29dc 100644 --- a/kclvm/compiler/src/codegen/llvm/backtrack.rs +++ b/kclvm/compiler/src/codegen/llvm/backtrack.rs @@ -1,6 +1,7 @@ // Copyright The KCL Authors. All rights reserved. use super::context::LLVMCodeGenContext; +use crate::codegen::llvm::context::BacktrackKind; use crate::codegen::traits::BuilderMethods; use inkwell::values::BasicValueEnum; @@ -22,4 +23,22 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } false } + + #[inline] + pub(crate) fn is_backtrack_only_if(&self) -> bool { + if let Some(backtrack_meta) = self.backtrack_meta.borrow_mut().as_ref() { + matches!(backtrack_meta.kind, BacktrackKind::If) + } else { + false + } + } + + #[inline] + pub(crate) fn is_backtrack_only_or_else(&self) -> bool { + if let Some(backtrack_meta) = self.backtrack_meta.borrow_mut().as_ref() { + matches!(backtrack_meta.kind, BacktrackKind::OrElse) + } else { + false + } + } } diff --git a/kclvm/compiler/src/codegen/llvm/context.rs b/kclvm/compiler/src/codegen/llvm/context.rs index f1a60bac4..1b4eaf091 100644 --- a/kclvm/compiler/src/codegen/llvm/context.rs +++ b/kclvm/compiler/src/codegen/llvm/context.rs @@ -69,12 +69,25 @@ pub struct Scope<'ctx> { pub arguments: RefCell>, } +/// Setter kind. +/// - If it is a normal kind, traverse all statements in the setter. +/// - If it is an if type, only traverse the if statement in the if stmt, skipping the else stmt. +/// - If it is an orelse type, only traverse the else statement, and make conditional judgments based on the inverse of the if stmt's cond. +#[derive(Default, Debug, Clone, PartialEq)] +pub enum BacktrackKind { + #[default] + Normal, + If, + OrElse, +} + /// Schema or Global internal order independent computation backtracking meta information. pub struct BacktrackMeta { pub target: String, pub level: usize, pub count: usize, pub stop: bool, + pub kind: BacktrackKind, } /// The LLVM code generator diff --git a/kclvm/compiler/src/codegen/llvm/module.rs b/kclvm/compiler/src/codegen/llvm/module.rs index 6a8146ac0..424695e53 100644 --- a/kclvm/compiler/src/codegen/llvm/module.rs +++ b/kclvm/compiler/src/codegen/llvm/module.rs @@ -9,6 +9,7 @@ use kclvm_runtime::ApiFunc; use kclvm_sema::pkgpath_without_prefix; use super::context::{BacktrackMeta, LLVMCodeGenContext}; +use crate::codegen::llvm::context::BacktrackKind; use crate::codegen::traits::{BuilderMethods, ProgramCodeGen, ValueMethods}; use crate::codegen::{error as kcl_error, ENTRY_NAME}; use crate::value; @@ -64,7 +65,8 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { self.br(global_setter_block); self.builder.position_at_end(global_setter_block); let mut place_holder_map: IndexMap>> = IndexMap::new(); - let mut body_map: IndexMap>> = IndexMap::new(); + let mut body_map: IndexMap, BacktrackKind)>> = + IndexMap::new(); let pkgpath = &self.current_pkgpath(); // Setter function name format: "$set..$" self.emit_global_setters( @@ -83,7 +85,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } let stmt_list = body_map.get(k).expect(kcl_error::INTERNAL_ERROR_MSG); let mut if_level = 0; - for (attr_func, stmt) in functions.iter().zip(stmt_list) { + for (attr_func, (stmt, kind)) in functions.iter().zip(stmt_list) { let function = *attr_func; let name = function .get_name() @@ -102,6 +104,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { level: if_level, count: 0, stop: false, + kind: kind.clone(), }); } else { if_level = 0; @@ -207,48 +210,51 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { pkgpath: &str, is_in_if: bool, place_holder_map: &mut IndexMap>>, - body_map: &mut IndexMap>>, + body_map: &mut IndexMap, BacktrackKind)>>, in_if_names: &mut Vec, ) { - let add_stmt = - |name: &str, - stmt: &'ctx ast::Node, - place_holder_map: &mut IndexMap>>, - body_map: &mut IndexMap>>| { - // The function form e.g., $set.__main__.a(&Context, &LazyScope, &ValueRef, &ValueRef) - let var_key = format!("{}.{name}", pkgpath_without_prefix!(pkgpath)); - let function = - self.add_setter_function(&format!("{}.{}", value::GLOBAL_SETTER, var_key)); - let lambda_fn_ptr = self.builder.build_bitcast( - function.as_global_value().as_pointer_value(), - self.context.i64_type().ptr_type(AddressSpace::default()), - "", - ); - if !place_holder_map.contains_key(name) { - place_holder_map.insert(name.to_string(), vec![]); - } - let name_vec = place_holder_map - .get_mut(name) - .expect(kcl_error::INTERNAL_ERROR_MSG); - name_vec.push(function); - self.build_void_call( - &ApiFunc::kclvm_scope_add_setter.name(), - &[ - self.current_runtime_ctx_ptr(), - self.current_scope_ptr(), - self.native_global_string(pkgpath, "").into(), - self.native_global_string(name, "").into(), - lambda_fn_ptr, - ], - ); - let key = format!("{}.{name}", pkgpath_without_prefix!(pkgpath)); - self.setter_keys.borrow_mut().insert(key); - if !body_map.contains_key(name) { - body_map.insert(name.to_string(), vec![]); - } - let body_vec = body_map.get_mut(name).expect(kcl_error::INTERNAL_ERROR_MSG); - body_vec.push(stmt); - }; + let add_stmt = |name: &str, + stmt: &'ctx ast::Node, + kind: BacktrackKind, + place_holder_map: &mut IndexMap>>, + body_map: &mut IndexMap< + String, + Vec<(&'ctx ast::Node, BacktrackKind)>, + >| { + // The function form e.g., $set.__main__.a(&Context, &LazyScope, &ValueRef, &ValueRef) + let var_key = format!("{}.{name}", pkgpath_without_prefix!(pkgpath)); + let function = + self.add_setter_function(&format!("{}.{}", value::GLOBAL_SETTER, var_key)); + let lambda_fn_ptr = self.builder.build_bitcast( + function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::default()), + "", + ); + if !place_holder_map.contains_key(name) { + place_holder_map.insert(name.to_string(), vec![]); + } + let name_vec = place_holder_map + .get_mut(name) + .expect(kcl_error::INTERNAL_ERROR_MSG); + name_vec.push(function); + self.build_void_call( + &ApiFunc::kclvm_scope_add_setter.name(), + &[ + self.current_runtime_ctx_ptr(), + self.current_scope_ptr(), + self.native_global_string(pkgpath, "").into(), + self.native_global_string(name, "").into(), + lambda_fn_ptr, + ], + ); + let key = format!("{}.{name}", pkgpath_without_prefix!(pkgpath)); + self.setter_keys.borrow_mut().insert(key); + if !body_map.contains_key(name) { + body_map.insert(name.to_string(), vec![]); + } + let body_vec = body_map.get_mut(name).expect(kcl_error::INTERNAL_ERROR_MSG); + body_vec.push((stmt, kind)); + }; for stmt in body { match &stmt.node { ast::Stmt::Unification(unification_stmt) => { @@ -256,7 +262,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } ast::Stmt::Assign(assign_stmt) => { @@ -265,7 +277,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } } @@ -275,7 +293,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } ast::Stmt::If(if_stmt) => { @@ -294,10 +318,10 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } } else { for name in &names { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt(name, stmt, BacktrackKind::If, place_holder_map, body_map); } - names.clear(); } + names.clear(); self.emit_global_setters( &if_stmt.orelse, pkgpath, @@ -312,17 +336,29 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } } else { for name in &names { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::OrElse, + place_holder_map, + body_map, + ); } - names.clear(); } + names.clear(); } ast::Stmt::SchemaAttr(schema_attr) => { let name = schema_attr.name.node.as_str(); if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } _ => {} diff --git a/kclvm/compiler/src/codegen/llvm/node.rs b/kclvm/compiler/src/codegen/llvm/node.rs index a4af3bae9..1e2c63afc 100644 --- a/kclvm/compiler/src/codegen/llvm/node.rs +++ b/kclvm/compiler/src/codegen/llvm/node.rs @@ -16,6 +16,7 @@ use kclvm_sema::ty::{ANY_TYPE_STR, STR_TYPE_STR}; use crate::check_backtrack_stop; use crate::codegen::error as kcl_error; +use crate::codegen::llvm::context::BacktrackKind; use crate::codegen::llvm::context::BacktrackMeta; use crate::codegen::llvm::utils; use crate::codegen::traits::*; @@ -262,21 +263,38 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { fn walk_if_stmt(&self, if_stmt: &'ctx ast::IfStmt) -> Self::Result { check_backtrack_stop!(self); - let cond = self - .walk_expr(&if_stmt.cond) - .expect(kcl_error::COMPILE_ERROR_MSG); + let cond = self.walk_expr(&if_stmt.cond)?; let then_block = self.append_block(""); let else_block = self.append_block(""); let end_block = self.append_block(""); let is_truth = self.value_is_truthy(cond); self.cond_br(is_truth, then_block, else_block); self.builder.position_at_end(then_block); - self.walk_stmts(&if_stmt.body) - .expect(kcl_error::COMPILE_ERROR_MSG); + // Is backtrack only orelse stmt? + if self.is_backtrack_only_or_else() { + self.ok_result()?; + self.br(end_block); + self.builder.position_at_end(else_block); + self.walk_stmts(&if_stmt.orelse)?; + self.br(end_block); + self.builder.position_at_end(end_block); + return Ok(self.none_value()); + } + // Is backtrack only if stmt? + if self.is_backtrack_only_if() { + self.walk_stmts(&if_stmt.body)?; + self.br(end_block); + self.builder.position_at_end(else_block); + self.ok_result()?; + self.br(end_block); + self.builder.position_at_end(end_block); + return Ok(self.none_value()); + } + // Normal full if stmt. + self.walk_stmts(&if_stmt.body)?; self.br(end_block); self.builder.position_at_end(else_block); - self.walk_stmts(&if_stmt.orelse) - .expect(kcl_error::COMPILE_ERROR_MSG); + self.walk_stmts(&if_stmt.orelse)?; self.br(end_block); self.builder.position_at_end(end_block); Ok(self.none_value()) @@ -432,7 +450,8 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { pkgpath_without_prefix!(runtime_type), )); let mut place_holder_map: HashMap>> = HashMap::new(); - let mut body_map: HashMap>> = HashMap::new(); + let mut body_map: HashMap, BacktrackKind)>> = + HashMap::new(); // Enter the function self.push_function(function); // Lambda function body @@ -887,7 +906,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { } let stmt_list = body_map.get(k).expect(kcl_error::INTERNAL_ERROR_MSG); let mut if_level = 0; - for (attr_func, stmt) in functions.iter().zip(stmt_list) { + for (attr_func, (stmt, kind)) in functions.iter().zip(stmt_list) { let function = *attr_func; let name = function .get_name() @@ -944,6 +963,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { level: if_level, count: 0, stop: false, + kind: kind.clone(), }); } else { if_level = 0; diff --git a/kclvm/compiler/src/codegen/llvm/schema.rs b/kclvm/compiler/src/codegen/llvm/schema.rs index e7ed89095..a3b18d6ee 100644 --- a/kclvm/compiler/src/codegen/llvm/schema.rs +++ b/kclvm/compiler/src/codegen/llvm/schema.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; use std::str; use super::context::LLVMCodeGenContext; +use crate::codegen::llvm::context::BacktrackKind; use crate::codegen::traits::{ BuilderMethods, DerivedTypeMethods, DerivedValueCalculationMethods, ProgramCodeGen, ValueMethods, @@ -26,53 +27,56 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { runtime_type: &str, is_in_if: bool, place_holder_map: &mut HashMap>>, - body_map: &mut HashMap>>, + body_map: &mut HashMap, BacktrackKind)>>, in_if_names: &mut Vec, ) { let schema_value = self .get_variable(value::SCHEMA_SELF_NAME) .expect(kcl_error::INTERNAL_ERROR_MSG); let value = self.undefined_value(); - let add_stmt = - |name: &str, - stmt: &'ctx ast::Node, - place_holder_map: &mut HashMap>>, - body_map: &mut HashMap>>| { - let function = self.add_function(&format!( - "{}.{}.{}", - value::SCHEMA_ATTR_NAME, - pkgpath_without_prefix!(runtime_type), - name - )); - let lambda_fn_ptr = self.builder.build_bitcast( - function.as_global_value().as_pointer_value(), - self.context.i64_type().ptr_type(AddressSpace::default()), - "", - ); - if !place_holder_map.contains_key(name) { - place_holder_map.insert(name.to_string(), vec![]); - } - let name_vec = place_holder_map - .get_mut(name) - .expect(kcl_error::INTERNAL_ERROR_MSG); - name_vec.push(function); - self.default_collection_insert_int_pointer(cal_map, name, lambda_fn_ptr); - self.default_collection_insert_value( - cal_map, - &format!("{}_{}", name, kclvm_runtime::CAL_MAP_RUNTIME_TYPE), - self.string_value(runtime_type), - ); - self.default_collection_insert_value( - cal_map, - &format!("{}_{}", name, kclvm_runtime::CAL_MAP_META_LINE), - self.int_value(stmt.line as i64), - ); - if !body_map.contains_key(name) { - body_map.insert(name.to_string(), vec![]); - } - let body_vec = body_map.get_mut(name).expect(kcl_error::INTERNAL_ERROR_MSG); - body_vec.push(stmt); - }; + let add_stmt = |name: &str, + stmt: &'ctx ast::Node, + kind: BacktrackKind, + place_holder_map: &mut HashMap>>, + body_map: &mut HashMap< + String, + Vec<(&'ctx ast::Node, BacktrackKind)>, + >| { + let function = self.add_function(&format!( + "{}.{}.{}", + value::SCHEMA_ATTR_NAME, + pkgpath_without_prefix!(runtime_type), + name + )); + let lambda_fn_ptr = self.builder.build_bitcast( + function.as_global_value().as_pointer_value(), + self.context.i64_type().ptr_type(AddressSpace::default()), + "", + ); + if !place_holder_map.contains_key(name) { + place_holder_map.insert(name.to_string(), vec![]); + } + let name_vec = place_holder_map + .get_mut(name) + .expect(kcl_error::INTERNAL_ERROR_MSG); + name_vec.push(function); + self.default_collection_insert_int_pointer(cal_map, name, lambda_fn_ptr); + self.default_collection_insert_value( + cal_map, + &format!("{}_{}", name, kclvm_runtime::CAL_MAP_RUNTIME_TYPE), + self.string_value(runtime_type), + ); + self.default_collection_insert_value( + cal_map, + &format!("{}_{}", name, kclvm_runtime::CAL_MAP_META_LINE), + self.int_value(stmt.line as i64), + ); + if !body_map.contains_key(name) { + body_map.insert(name.to_string(), vec![]); + } + let body_vec = body_map.get_mut(name).expect(kcl_error::INTERNAL_ERROR_MSG); + body_vec.push((stmt, kind)); + }; if let Some(index_signature) = index_signature { self.default_collection_insert_value( cal_map, @@ -89,7 +93,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } ast::Stmt::Assign(assign_stmt) => { @@ -99,7 +109,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } } @@ -110,7 +126,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } ast::Stmt::If(if_stmt) => { @@ -131,10 +153,10 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } } else { for name in &names { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt(name, stmt, BacktrackKind::If, place_holder_map, body_map); } - names.clear(); } + names.clear(); self.emit_left_identifiers( &if_stmt.orelse, &None, @@ -151,10 +173,16 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } } else { for name in &names { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::OrElse, + place_holder_map, + body_map, + ); } - names.clear(); } + names.clear(); } ast::Stmt::SchemaAttr(schema_attr) => { let name = schema_attr.name.node.as_str(); @@ -162,7 +190,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if is_in_if { in_if_names.push(name.to_string()); } else { - add_stmt(name, stmt, place_holder_map, body_map); + add_stmt( + name, + stmt, + BacktrackKind::Normal, + place_holder_map, + body_map, + ); } } _ => {} diff --git a/kclvm/evaluator/src/context.rs b/kclvm/evaluator/src/context.rs index b02e35238..6fef0d73c 100644 --- a/kclvm/evaluator/src/context.rs +++ b/kclvm/evaluator/src/context.rs @@ -7,7 +7,7 @@ use kclvm_runtime::{BacktraceFrame, MAIN_PKG_PATH}; use crate::{ error as kcl_error, func::{FunctionCaller, FunctionEvalContextRef}, - lazy::{BacktrackMeta, Setter}, + lazy::{BacktrackMeta, Setter, SetterKind}, proxy::{Frame, Proxy}, rule::RuleCaller, schema::SchemaCaller, @@ -247,19 +247,40 @@ impl<'ctx> Evaluator<'ctx> { } } + #[inline] pub(crate) fn push_backtrack_meta(&self, setter: &Setter) { let meta = &mut self.backtrack_meta.borrow_mut(); meta.push(BacktrackMeta { stopped: setter.stopped.clone(), is_break: false, + kind: setter.kind.clone(), }); } + #[inline] pub(crate) fn pop_backtrack_meta(&self) { let meta = &mut self.backtrack_meta.borrow_mut(); meta.pop(); } + #[inline] + pub(crate) fn is_backtrack_only_if(&self) -> bool { + let meta = &mut self.backtrack_meta.borrow_mut(); + match meta.last().map(|m| matches!(m.kind, SetterKind::If)) { + Some(r) => r, + None => false, + } + } + + #[inline] + pub(crate) fn is_backtrack_only_or_else(&self) -> bool { + let meta = &mut self.backtrack_meta.borrow_mut(); + match meta.last().map(|m| matches!(m.kind, SetterKind::OrElse)) { + Some(r) => r, + None => false, + } + } + pub(crate) fn push_scope_cover(&self, start: usize, stop: usize) { self.scope_covers.borrow_mut().push((start, stop)); } diff --git a/kclvm/evaluator/src/lazy.rs b/kclvm/evaluator/src/lazy.rs index 66c4910a2..e911a1a72 100644 --- a/kclvm/evaluator/src/lazy.rs +++ b/kclvm/evaluator/src/lazy.rs @@ -78,6 +78,18 @@ impl LazyEvalScope { } } +/// Setter kind. +/// - If it is a normal kind, traverse all statements in the setter. +/// - If it is an if type, only traverse the if statement in the if stmt, skipping the else stmt. +/// - If it is an orelse type, only traverse the else statement, and make conditional judgments based on the inverse of the if stmt's cond. +#[derive(PartialEq, Clone, Default, Debug)] +pub enum SetterKind { + #[default] + Normal, + If, + OrElse, +} + /// Setter function definition. #[derive(PartialEq, Clone, Default, Debug)] pub struct Setter { @@ -87,6 +99,11 @@ pub struct Setter { pub stmt: usize, /// If the statement is a if statement, stop the backtrack process at the stopped statement index. pub stopped: Option, + /// Setter kind. + /// - If it is a normal kind, traverse all statements in the setter. + /// - If it is an if type, only traverse the if statement in the if stmt, skipping the else stmt. + /// - If it is an orelse type, only traverse the else statement, and make conditional judgments based on the inverse of the if stmt's cond. + pub kind: SetterKind, } /// Merge setters and set the value with default undefined value. @@ -133,6 +150,7 @@ pub(crate) fn merge_setters( pub struct BacktrackMeta { pub stopped: Option, pub is_break: bool, + pub kind: SetterKind, } impl<'ctx> Evaluator<'ctx> { @@ -166,7 +184,8 @@ impl<'ctx> Evaluator<'ctx> { let add_stmt = |name: &str, i: usize, stopped: Option, - body_map: &mut IndexMap>| { + body_map: &mut IndexMap>, + kind: SetterKind| { if !body_map.contains_key(name) { body_map.insert(name.to_string(), vec![]); } @@ -175,6 +194,7 @@ impl<'ctx> Evaluator<'ctx> { index, stmt: i, stopped, + kind, }); }; for (i, stmt) in body.iter().enumerate() { @@ -184,7 +204,7 @@ impl<'ctx> Evaluator<'ctx> { if is_in_if { in_if_names.push((name.to_string(), stmt.id.clone())); } else { - add_stmt(name, i, None, body_map); + add_stmt(name, i, None, body_map, SetterKind::Normal); } } ast::Stmt::Assign(assign_stmt) => { @@ -193,7 +213,7 @@ impl<'ctx> Evaluator<'ctx> { if is_in_if { in_if_names.push((name.to_string(), stmt.id.clone())); } else { - add_stmt(name, i, None, body_map); + add_stmt(name, i, None, body_map, SetterKind::Normal); } } } @@ -203,41 +223,45 @@ impl<'ctx> Evaluator<'ctx> { if is_in_if { in_if_names.push((name.to_string(), stmt.id.clone())); } else { - add_stmt(name, i, None, body_map); + add_stmt(name, i, None, body_map, SetterKind::Normal); } } ast::Stmt::If(if_stmt) => { - let mut names: Vec<(String, AstIndex)> = vec![]; - self.emit_setters_with(&if_stmt.body, body_map, true, &mut names, index); + let mut if_names: Vec<(String, AstIndex)> = vec![]; + self.emit_setters_with(&if_stmt.body, body_map, true, &mut if_names, index); if is_in_if { - for (name, id) in &names { + for (name, id) in &if_names { in_if_names.push((name.to_string(), id.clone())); } } else { - for (name, id) in &names { - add_stmt(name, i, Some(id.clone()), body_map); + for (name, id) in &if_names { + add_stmt(name, i, Some(id.clone()), body_map, SetterKind::If); } } - - names.clear(); - self.emit_setters_with(&if_stmt.orelse, body_map, true, &mut names, index); + let mut or_else_names: Vec<(String, AstIndex)> = vec![]; + self.emit_setters_with( + &if_stmt.orelse, + body_map, + true, + &mut or_else_names, + index, + ); if is_in_if { - for (name, id) in &names { + for (name, id) in &or_else_names { in_if_names.push((name.to_string(), id.clone())); } } else { - for (name, id) in &names { - add_stmt(name, i, Some(id.clone()), body_map); + for (name, id) in &or_else_names { + add_stmt(name, i, Some(id.clone()), body_map, SetterKind::OrElse); } } - names.clear(); } ast::Stmt::SchemaAttr(schema_attr) => { let name = schema_attr.name.node.as_str(); if is_in_if { in_if_names.push((name.to_string(), stmt.id.clone())); } else { - add_stmt(name, i, None, body_map); + add_stmt(name, i, None, body_map, SetterKind::Normal); } } _ => {} diff --git a/kclvm/evaluator/src/node.rs b/kclvm/evaluator/src/node.rs index 01f8c2a7b..7e0a43f95 100644 --- a/kclvm/evaluator/src/node.rs +++ b/kclvm/evaluator/src/node.rs @@ -229,6 +229,21 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { fn walk_if_stmt(&self, if_stmt: &'ctx ast::IfStmt) -> Self::Result { let cond = self.walk_expr(&if_stmt.cond)?; let is_truth = self.value_is_truthy(&cond); + // Is backtrack only orelse stmt? + if self.is_backtrack_only_or_else() { + if !is_truth { + self.walk_stmts(&if_stmt.orelse)?; + } + return self.ok_result(); + } + // Is backtrack only if stmt? + if self.is_backtrack_only_if() { + if is_truth { + self.walk_stmts(&if_stmt.body)?; + } + return self.ok_result(); + } + // Normal full if stmt. if is_truth { self.walk_stmts(&if_stmt.body)?; } else { diff --git a/test/grammar/schema/irrelevant_order/if_stmt_0/main.k b/test/grammar/schema/irrelevant_order/if_stmt_0/main.k index 47577f15a..5297600d3 100644 --- a/test/grammar/schema/irrelevant_order/if_stmt_0/main.k +++ b/test/grammar/schema/irrelevant_order/if_stmt_0/main.k @@ -11,3 +11,12 @@ schema Person: person = Person { name = "Overwrite" } + +name: str = _name +age: int = _age +if True: + _name = "Alice" +if False: + _age = 10 +else: + _age = 18 diff --git a/test/grammar/schema/irrelevant_order/if_stmt_0/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_0/stdout.golden index 7e7205956..9178de890 100644 --- a/test/grammar/schema/irrelevant_order/if_stmt_0/stdout.golden +++ b/test/grammar/schema/irrelevant_order/if_stmt_0/stdout.golden @@ -1,3 +1,5 @@ person: name: Overwrite age: 18 +name: Alice +age: 18 diff --git a/test/grammar/schema/irrelevant_order/if_stmt_10/main.k b/test/grammar/schema/irrelevant_order/if_stmt_10/main.k new file mode 100644 index 000000000..eb9bacc5a --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_10/main.k @@ -0,0 +1,13 @@ +schema Name: + _a = 1 + _b = 1 + if True: + _a += 1 + _b += 1 + elif False: + _a += 1 + _b += 1 + a = _a + b = _b + +name = Name{} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_10/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_10/stdout.golden new file mode 100644 index 000000000..5da033819 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_10/stdout.golden @@ -0,0 +1,3 @@ +name: + a: 2 + b: 2 diff --git a/test/grammar/schema/irrelevant_order/if_stmt_7/main.k b/test/grammar/schema/irrelevant_order/if_stmt_7/main.k new file mode 100644 index 000000000..a4ca0aecd --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_7/main.k @@ -0,0 +1,6 @@ +_a = 1 +if True: + _a += 1 +elif False: + _a += 1 +a = _a diff --git a/test/grammar/schema/irrelevant_order/if_stmt_7/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_7/stdout.golden new file mode 100644 index 000000000..9dfc208df --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_7/stdout.golden @@ -0,0 +1 @@ +a: 2 diff --git a/test/grammar/schema/irrelevant_order/if_stmt_8/main.k b/test/grammar/schema/irrelevant_order/if_stmt_8/main.k new file mode 100644 index 000000000..77a9ba1fe --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_8/main.k @@ -0,0 +1,9 @@ +schema Name: + _a = 1 + if True: + _a += 1 + elif False: + _a += 1 + a = _a + +name = Name{} diff --git a/test/grammar/schema/irrelevant_order/if_stmt_8/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_8/stdout.golden new file mode 100644 index 000000000..e727a4e4f --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_8/stdout.golden @@ -0,0 +1,2 @@ +name: + a: 2 diff --git a/test/grammar/schema/irrelevant_order/if_stmt_9/main.k b/test/grammar/schema/irrelevant_order/if_stmt_9/main.k new file mode 100644 index 000000000..17f237ff9 --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_9/main.k @@ -0,0 +1,5 @@ +_a = 1 +if True: + print() + _a += 1 +a = _a diff --git a/test/grammar/schema/irrelevant_order/if_stmt_9/stdout.golden b/test/grammar/schema/irrelevant_order/if_stmt_9/stdout.golden new file mode 100644 index 000000000..f393f6aec --- /dev/null +++ b/test/grammar/schema/irrelevant_order/if_stmt_9/stdout.golden @@ -0,0 +1,2 @@ + +a: 2 \ No newline at end of file diff --git a/test/grammar/schema/stmt_block/stmt_block_0/main.k b/test/grammar/schema/stmt_block/stmt_block_0/main.k index 5ebd90ef7..884f13d98 100644 --- a/test/grammar/schema/stmt_block/stmt_block_0/main.k +++ b/test/grammar/schema/stmt_block/stmt_block_0/main.k @@ -1,13 +1,14 @@ schema Person: firstName: str = "John" lastName: str - name: str = _name if len(lastName) > 0: _name = firstName + " " + lastName else: _name = firstName + name: str = _name + JohnDoe = Person { "lastName": "Doe" }