2024-11-14 17:27:19 -06:00
use std ::collections ::HashMap ;
2024-12-03 16:39:51 +13:00
use async_recursion ::async_recursion ;
2024-10-16 14:33:03 -07:00
use crate ::{
errors ::{ KclError , KclErrorDetails } ,
2024-12-03 16:39:51 +13:00
executor ::{ BodyType , ExecState , ExecutorContext , KclValue , Metadata , StatementKind , TagEngineInfo , TagIdentifier } ,
2024-12-05 17:56:49 +13:00
parsing ::ast ::types ::{
ArrayExpression , ArrayRangeExpression , BinaryExpression , BinaryOperator , BinaryPart , CallExpression ,
2024-12-05 17:59:37 -06:00
CallExpressionKw , Expr , IfExpression , LiteralIdentifier , LiteralValue , MemberExpression , MemberObject , Node ,
ObjectExpression , TagDeclarator , UnaryExpression , UnaryOperator ,
2024-12-05 17:56:49 +13:00
} ,
2024-12-03 16:39:51 +13:00
source_range ::SourceRange ,
2024-11-21 13:10:03 -05:00
std ::{ args ::Arg , FunctionKind } ,
2024-10-16 14:33:03 -07:00
} ;
2024-11-14 17:27:19 -06:00
const FLOAT_TO_INT_MAX_DELTA : f64 = 0.01 ;
2024-10-16 14:33:03 -07:00
impl BinaryPart {
#[ async_recursion ]
pub async fn get_result ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
match self {
BinaryPart ::Literal ( literal ) = > Ok ( literal . into ( ) ) ,
BinaryPart ::Identifier ( identifier ) = > {
let value = exec_state . memory . get ( & identifier . name , identifier . into ( ) ) ? ;
Ok ( value . clone ( ) )
}
BinaryPart ::BinaryExpression ( binary_expression ) = > binary_expression . get_result ( exec_state , ctx ) . await ,
BinaryPart ::CallExpression ( call_expression ) = > call_expression . execute ( exec_state , ctx ) . await ,
2024-12-02 15:23:18 -06:00
BinaryPart ::CallExpressionKw ( call_expression ) = > call_expression . execute ( exec_state , ctx ) . await ,
2024-10-16 14:33:03 -07:00
BinaryPart ::UnaryExpression ( unary_expression ) = > unary_expression . get_result ( exec_state , ctx ) . await ,
BinaryPart ::MemberExpression ( member_expression ) = > member_expression . get_result ( exec_state ) ,
BinaryPart ::IfExpression ( e ) = > e . get_result ( exec_state , ctx ) . await ,
}
}
}
2024-10-30 16:52:17 -04:00
impl Node < MemberExpression > {
2024-10-16 14:33:03 -07:00
pub fn get_result_array ( & self , exec_state : & mut ExecState , index : usize ) -> Result < KclValue , KclError > {
let array = match & self . object {
MemberObject ::MemberExpression ( member_expr ) = > member_expr . get_result ( exec_state ) ? ,
MemberObject ::Identifier ( identifier ) = > {
let value = exec_state . memory . get ( & identifier . name , identifier . into ( ) ) ? ;
value . clone ( )
}
} ;
2024-11-14 17:27:19 -06:00
let KclValue ::Array { value : array , meta : _ } = array else {
return Err ( KclError ::Semantic ( KclErrorDetails {
message : format ! ( " MemberExpression array is not an array: {:?} " , array ) ,
source_ranges : vec ! [ self . clone ( ) . into ( ) ] ,
} ) ) ;
} ;
2024-10-16 14:33:03 -07:00
2024-11-14 17:27:19 -06:00
if let Some ( value ) = array . get ( index ) {
Ok ( value . to_owned ( ) )
2024-10-16 14:33:03 -07:00
} else {
2024-11-14 17:27:19 -06:00
Err ( KclError ::UndefinedValue ( KclErrorDetails {
message : format ! ( " index {} not found in array " , index ) ,
2024-10-16 14:33:03 -07:00
source_ranges : vec ! [ self . clone ( ) . into ( ) ] ,
} ) )
}
}
2024-10-17 16:29:27 -07:00
pub fn get_result ( & self , exec_state : & mut ExecState ) -> Result < KclValue , KclError > {
let property = Property ::try_from ( self . computed , self . property . clone ( ) , exec_state , self . into ( ) ) ? ;
2024-10-16 14:33:03 -07:00
let object = match & self . object {
// TODO: Don't use recursion here, use a loop.
MemberObject ::MemberExpression ( member_expr ) = > member_expr . get_result ( exec_state ) ? ,
MemberObject ::Identifier ( identifier ) = > {
let value = exec_state . memory . get ( & identifier . name , identifier . into ( ) ) ? ;
value . clone ( )
}
} ;
// Check the property and object match -- e.g. ints for arrays, strs for objects.
2024-11-14 17:27:19 -06:00
match ( object , property ) {
( KclValue ::Object { value : map , meta : _ } , Property ::String ( property ) ) = > {
2024-10-16 14:33:03 -07:00
if let Some ( value ) = map . get ( & property ) {
2024-11-14 17:27:19 -06:00
Ok ( value . to_owned ( ) )
2024-10-16 14:33:03 -07:00
} else {
Err ( KclError ::UndefinedValue ( KclErrorDetails {
message : format ! ( " Property '{property}' not found in object " ) ,
source_ranges : vec ! [ self . clone ( ) . into ( ) ] ,
} ) )
}
}
2024-11-14 17:27:19 -06:00
( KclValue ::Object { .. } , p ) = > {
let t = p . type_name ( ) ;
let article = article_for ( t ) ;
Err ( KclError ::Semantic ( KclErrorDetails {
message : format ! (
" Only strings can be used as the property of an object, but you're using {article} {t} " ,
) ,
source_ranges : vec ! [ self . clone ( ) . into ( ) ] ,
} ) )
}
2024-11-25 10:50:43 +13:00
( KclValue ::Array { value : arr , meta : _ } , Property ::UInt ( index ) ) = > {
2024-11-14 17:27:19 -06:00
let value_of_arr = arr . get ( index ) ;
2024-10-16 14:33:03 -07:00
if let Some ( value ) = value_of_arr {
2024-11-14 17:27:19 -06:00
Ok ( value . to_owned ( ) )
2024-10-16 14:33:03 -07:00
} else {
Err ( KclError ::UndefinedValue ( KclErrorDetails {
message : format ! ( " The array doesn't have any item at index {index} " ) ,
source_ranges : vec ! [ self . clone ( ) . into ( ) ] ,
} ) )
}
}
2024-11-14 17:27:19 -06:00
( KclValue ::Array { .. } , p ) = > {
let t = p . type_name ( ) ;
let article = article_for ( t ) ;
Err ( KclError ::Semantic ( KclErrorDetails {
message : format ! (
" Only integers >= 0 can be used as the index of an array, but you're using {article} {t} " ,
) ,
source_ranges : vec ! [ self . clone ( ) . into ( ) ] ,
} ) )
}
( KclValue ::Solid ( solid ) , Property ::String ( prop ) ) if prop = = " sketch " = > Ok ( KclValue ::Sketch {
value : Box ::new ( solid . sketch ) ,
} ) ,
( KclValue ::Sketch { value : sk } , Property ::String ( prop ) ) if prop = = " tags " = > Ok ( KclValue ::Object {
meta : vec ! [ Metadata {
source_range : SourceRange ::from ( self . clone ( ) ) ,
} ] ,
value : sk
. tags
. iter ( )
. map ( | ( k , tag ) | ( k . to_owned ( ) , KclValue ::TagIdentifier ( Box ::new ( tag . to_owned ( ) ) ) ) )
. collect ( ) ,
} ) ,
2024-10-16 14:33:03 -07:00
( being_indexed , _ ) = > {
2024-11-14 17:27:19 -06:00
let t = being_indexed . human_friendly_type ( ) ;
let article = article_for ( t ) ;
2024-10-16 14:33:03 -07:00
Err ( KclError ::Semantic ( KclErrorDetails {
2024-11-14 17:27:19 -06:00
message : format ! (
" Only arrays and objects can be indexed, but you're trying to index {article} {t} "
) ,
2024-10-16 14:33:03 -07:00
source_ranges : vec ! [ self . clone ( ) . into ( ) ] ,
} ) )
}
}
}
}
2024-10-30 16:52:17 -04:00
impl Node < BinaryExpression > {
2024-10-16 14:33:03 -07:00
#[ async_recursion ]
pub async fn get_result ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
2024-11-14 17:27:19 -06:00
let left_value = self . left . get_result ( exec_state , ctx ) . await ? ;
let right_value = self . right . get_result ( exec_state , ctx ) . await ? ;
let mut meta = left_value . metadata ( ) ;
meta . extend ( right_value . metadata ( ) ) ;
2024-10-16 14:33:03 -07:00
// First check if we are doing string concatenation.
if self . operator = = BinaryOperator ::Add {
2024-11-14 17:27:19 -06:00
if let ( KclValue ::String { value : left , meta : _ } , KclValue ::String { value : right , meta : _ } ) =
( & left_value , & right_value )
{
return Ok ( KclValue ::String {
value : format ! ( " {}{} " , left , right ) ,
meta ,
} ) ;
2024-10-16 14:33:03 -07:00
}
}
2024-11-14 17:27:19 -06:00
let left = parse_number_as_f64 ( & left_value , self . left . clone ( ) . into ( ) ) ? ;
let right = parse_number_as_f64 ( & right_value , self . right . clone ( ) . into ( ) ) ? ;
let value = match self . operator {
BinaryOperator ::Add = > KclValue ::Number {
value : left + right ,
meta ,
} ,
BinaryOperator ::Sub = > KclValue ::Number {
value : left - right ,
meta ,
} ,
BinaryOperator ::Mul = > KclValue ::Number {
value : left * right ,
meta ,
} ,
BinaryOperator ::Div = > KclValue ::Number {
value : left / right ,
meta ,
} ,
BinaryOperator ::Mod = > KclValue ::Number {
value : left % right ,
meta ,
} ,
BinaryOperator ::Pow = > KclValue ::Number {
value : left . powf ( right ) ,
meta ,
} ,
BinaryOperator ::Neq = > KclValue ::Bool {
value : left ! = right ,
meta ,
} ,
BinaryOperator ::Gt = > KclValue ::Bool {
value : left > right ,
meta ,
} ,
BinaryOperator ::Gte = > KclValue ::Bool {
value : left > = right ,
meta ,
} ,
BinaryOperator ::Lt = > KclValue ::Bool {
value : left < right ,
meta ,
} ,
BinaryOperator ::Lte = > KclValue ::Bool {
value : left < = right ,
meta ,
} ,
BinaryOperator ::Eq = > KclValue ::Bool {
value : left = = right ,
meta ,
} ,
2024-10-16 14:33:03 -07:00
} ;
2024-11-14 17:27:19 -06:00
Ok ( value )
2024-10-16 14:33:03 -07:00
}
}
2024-10-30 16:52:17 -04:00
impl Node < UnaryExpression > {
2024-10-16 14:33:03 -07:00
pub async fn get_result ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
if self . operator = = UnaryOperator ::Not {
2024-11-14 17:27:19 -06:00
let value = self . argument . get_result ( exec_state , ctx ) . await ? ;
let KclValue ::Bool {
value : bool_value ,
meta : _ ,
} = value
else {
2024-10-16 14:33:03 -07:00
return Err ( KclError ::Semantic ( KclErrorDetails {
2024-11-14 17:27:19 -06:00
message : format ! (
" Cannot apply unary operator ! to non-boolean value: {} " ,
value . human_friendly_type ( )
) ,
2024-10-16 14:33:03 -07:00
source_ranges : vec ! [ self . into ( ) ] ,
} ) ) ;
} ;
2024-11-14 17:27:19 -06:00
let meta = vec! [ Metadata {
source_range : self . into ( ) ,
} ] ;
let negated = KclValue ::Bool {
value : ! bool_value ,
meta ,
} ;
return Ok ( negated ) ;
2024-10-16 14:33:03 -07:00
}
2024-11-14 17:27:19 -06:00
let value = & self . argument . get_result ( exec_state , ctx ) . await ? ;
match value {
KclValue ::Number { value , meta : _ } = > {
let meta = vec! [ Metadata {
source_range : self . into ( ) ,
} ] ;
Ok ( KclValue ::Number { value : - value , meta } )
}
KclValue ::Int { value , meta : _ } = > {
let meta = vec! [ Metadata {
source_range : self . into ( ) ,
} ] ;
Ok ( KclValue ::Number {
value : ( - value ) as f64 ,
meta ,
} )
}
_ = > Err ( KclError ::Semantic ( KclErrorDetails {
message : format ! (
" You can only negate numbers, but this is a {} " ,
value . human_friendly_type ( )
) ,
source_ranges : vec ! [ self . into ( ) ] ,
} ) ) ,
}
2024-10-16 14:33:03 -07:00
}
}
pub ( crate ) async fn execute_pipe_body (
exec_state : & mut ExecState ,
body : & [ Expr ] ,
source_range : SourceRange ,
ctx : & ExecutorContext ,
) -> Result < KclValue , KclError > {
let Some ( ( first , body ) ) = body . split_first ( ) else {
return Err ( KclError ::Semantic ( KclErrorDetails {
message : " Pipe expressions cannot be empty " . to_owned ( ) ,
source_ranges : vec ! [ source_range ] ,
} ) ) ;
} ;
// Evaluate the first element in the pipeline.
// They use the pipe_value from some AST node above this, so that if pipe expression is nested in a larger pipe expression,
// they use the % from the parent. After all, this pipe expression hasn't been executed yet, so it doesn't have any % value
// of its own.
let meta = Metadata {
2024-11-07 11:23:41 -05:00
source_range : SourceRange ::from ( first ) ,
2024-10-16 14:33:03 -07:00
} ;
let output = ctx
. execute_expr ( first , exec_state , & meta , StatementKind ::Expression )
. await ? ;
// Now that we've evaluated the first child expression in the pipeline, following child expressions
// should use the previous child expression for %.
// This means there's no more need for the previous pipe_value from the parent AST node above this one.
let previous_pipe_value = std ::mem ::replace ( & mut exec_state . pipe_value , Some ( output ) ) ;
// Evaluate remaining elements.
let result = inner_execute_pipe_body ( exec_state , body , ctx ) . await ;
// Restore the previous pipe value.
exec_state . pipe_value = previous_pipe_value ;
result
}
/// Execute the tail of a pipe expression. exec_state.pipe_value must be set by
/// the caller.
#[ async_recursion ]
async fn inner_execute_pipe_body (
exec_state : & mut ExecState ,
body : & [ Expr ] ,
ctx : & ExecutorContext ,
) -> Result < KclValue , KclError > {
for expression in body {
match expression {
Expr ::TagDeclarator ( _ ) = > {
return Err ( KclError ::Semantic ( KclErrorDetails {
message : format ! ( " This cannot be in a PipeExpression: {:?} " , expression ) ,
source_ranges : vec ! [ expression . into ( ) ] ,
} ) ) ;
}
Expr ::Literal ( _ )
| Expr ::Identifier ( _ )
| Expr ::BinaryExpression ( _ )
| Expr ::FunctionExpression ( _ )
| Expr ::CallExpression ( _ )
2024-12-02 15:23:18 -06:00
| Expr ::CallExpressionKw ( _ )
2024-10-16 14:33:03 -07:00
| Expr ::PipeExpression ( _ )
| Expr ::PipeSubstitution ( _ )
| Expr ::ArrayExpression ( _ )
| Expr ::ArrayRangeExpression ( _ )
| Expr ::ObjectExpression ( _ )
| Expr ::MemberExpression ( _ )
| Expr ::UnaryExpression ( _ )
| Expr ::IfExpression ( _ )
| Expr ::None ( _ ) = > { }
} ;
let metadata = Metadata {
2024-11-07 11:23:41 -05:00
source_range : SourceRange ::from ( expression ) ,
2024-10-16 14:33:03 -07:00
} ;
let output = ctx
. execute_expr ( expression , exec_state , & metadata , StatementKind ::Expression )
. await ? ;
exec_state . pipe_value = Some ( output ) ;
}
// Safe to unwrap here, because pipe_value always has something pushed in when the `match first` executes.
let final_output = exec_state . pipe_value . take ( ) . unwrap ( ) ;
Ok ( final_output )
}
2024-12-02 15:23:18 -06:00
impl Node < CallExpressionKw > {
2024-12-05 14:27:51 -06:00
#[ async_recursion ]
pub async fn execute ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
let fn_name = & self . callee . name ;
// Build a hashmap from argument labels to the final evaluated values.
let mut fn_args = HashMap ::with_capacity ( self . arguments . len ( ) ) ;
for arg_expr in & self . arguments {
let source_range = SourceRange ::from ( arg_expr . arg . clone ( ) ) ;
let metadata = Metadata { source_range } ;
let value = ctx
. execute_expr ( & arg_expr . arg , exec_state , & metadata , StatementKind ::Expression )
. await ? ;
fn_args . insert ( arg_expr . label . name . clone ( ) , Arg ::new ( value , source_range ) ) ;
}
let fn_args = fn_args ; // remove mutability
// Evaluate the unlabeled first param, if any exists.
let unlabeled = if let Some ( ref arg_expr ) = self . unlabeled {
let source_range = SourceRange ::from ( arg_expr . clone ( ) ) ;
let metadata = Metadata { source_range } ;
let value = ctx
. execute_expr ( arg_expr , exec_state , & metadata , StatementKind ::Expression )
. await ? ;
Some ( Arg ::new ( value , source_range ) )
} else {
None
} ;
let args = crate ::std ::Args ::new_kw ( fn_args , unlabeled , self . into ( ) , ctx . clone ( ) ) ;
match ctx . stdlib . get_either ( fn_name ) {
FunctionKind ::Core ( func ) = > {
// Attempt to call the function.
let mut result = func . std_lib_fn ( ) ( exec_state , args ) . await ? ;
update_memory_for_tags_of_geometry ( & mut result , exec_state ) ? ;
Ok ( result )
}
FunctionKind ::UserDefined = > {
todo! ( " Part of modeling-app#4600: Support keyword arguments for user-defined functions " )
}
}
2024-12-02 15:23:18 -06:00
}
}
2024-10-30 16:52:17 -04:00
impl Node < CallExpression > {
2024-10-16 14:33:03 -07:00
#[ async_recursion ]
pub async fn execute ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
let fn_name = & self . callee . name ;
2024-11-21 13:10:03 -05:00
let mut fn_args : Vec < Arg > = Vec ::with_capacity ( self . arguments . len ( ) ) ;
2024-10-16 14:33:03 -07:00
2024-11-21 13:10:03 -05:00
for arg_expr in & self . arguments {
2024-10-16 14:33:03 -07:00
let metadata = Metadata {
2024-11-21 13:10:03 -05:00
source_range : SourceRange ::from ( arg_expr ) ,
2024-10-16 14:33:03 -07:00
} ;
2024-11-21 13:10:03 -05:00
let value = ctx
. execute_expr ( arg_expr , exec_state , & metadata , StatementKind ::Expression )
2024-10-16 14:33:03 -07:00
. await ? ;
2024-11-21 13:10:03 -05:00
let arg = Arg ::new ( value , SourceRange ::from ( arg_expr ) ) ;
fn_args . push ( arg ) ;
2024-10-16 14:33:03 -07:00
}
2024-12-05 14:27:51 -06:00
match ctx . stdlib . get_either ( fn_name ) {
2024-10-16 14:33:03 -07:00
FunctionKind ::Core ( func ) = > {
// Attempt to call the function.
let args = crate ::std ::Args ::new ( fn_args , self . into ( ) , ctx . clone ( ) ) ;
let mut result = func . std_lib_fn ( ) ( exec_state , args ) . await ? ;
2024-12-05 14:27:51 -06:00
update_memory_for_tags_of_geometry ( & mut result , exec_state ) ? ;
2024-10-16 14:33:03 -07:00
Ok ( result )
}
FunctionKind ::UserDefined = > {
let source_range = SourceRange ::from ( self ) ;
// Clone the function so that we can use a mutable reference to
// exec_state.
let func = exec_state . memory . get ( fn_name , source_range ) ? . clone ( ) ;
let fn_dynamic_state = exec_state . dynamic_state . merge ( & exec_state . memory ) ;
let return_value = {
let previous_dynamic_state = std ::mem ::replace ( & mut exec_state . dynamic_state , fn_dynamic_state ) ;
let result = func . call_fn ( fn_args , exec_state , ctx . clone ( ) ) . await . map_err ( | e | {
// Add the call expression to the source ranges.
2024-12-06 13:57:31 +13:00
// TODO currently ignored by the frontend
2024-10-16 14:33:03 -07:00
e . add_source_ranges ( vec! [ source_range ] )
} ) ;
exec_state . dynamic_state = previous_dynamic_state ;
result ?
} ;
let result = return_value . ok_or_else ( move | | {
let mut source_ranges : Vec < SourceRange > = vec! [ source_range ] ;
// We want to send the source range of the original function.
if let KclValue ::Function { meta , .. } = func {
source_ranges = meta . iter ( ) . map ( | m | m . source_range ) . collect ( ) ;
} ;
KclError ::UndefinedValue ( KclErrorDetails {
message : format ! ( " Result of user-defined function {} is undefined " , fn_name ) ,
source_ranges ,
} )
} ) ? ;
Ok ( result )
}
}
}
}
2024-12-05 14:27:51 -06:00
fn update_memory_for_tags_of_geometry ( result : & mut KclValue , exec_state : & mut ExecState ) -> Result < ( ) , KclError > {
// If the return result is a sketch or solid, we want to update the
// memory for the tags of the group.
// TODO: This could probably be done in a better way, but as of now this was my only idea
// and it works.
match result {
KclValue ::Sketch { value : ref mut sketch } = > {
for ( _ , tag ) in sketch . tags . iter ( ) {
exec_state . memory . update_tag ( & tag . value , tag . clone ( ) ) ? ;
}
}
KclValue ::Solid ( ref mut solid ) = > {
for value in & solid . value {
if let Some ( tag ) = value . get_tag ( ) {
// Get the past tag and update it.
let mut t = if let Some ( t ) = solid . sketch . tags . get ( & tag . name ) {
t . clone ( )
} else {
// It's probably a fillet or a chamfer.
// Initialize it.
TagIdentifier {
value : tag . name . clone ( ) ,
info : Some ( TagEngineInfo {
id : value . get_id ( ) ,
surface : Some ( value . clone ( ) ) ,
path : None ,
sketch : solid . id ,
} ) ,
meta : vec ! [ Metadata {
source_range : tag . clone ( ) . into ( ) ,
} ] ,
}
} ;
let Some ( ref info ) = t . info else {
return Err ( KclError ::Semantic ( KclErrorDetails {
message : format ! ( " Tag {} does not have path info " , tag . name ) ,
source_ranges : vec ! [ tag . into ( ) ] ,
} ) ) ;
} ;
let mut info = info . clone ( ) ;
info . surface = Some ( value . clone ( ) ) ;
info . sketch = solid . id ;
t . info = Some ( info ) ;
exec_state . memory . update_tag ( & tag . name , t . clone ( ) ) ? ;
// update the sketch tags.
solid . sketch . tags . insert ( tag . name . clone ( ) , t ) ;
}
}
// Find the stale sketch in memory and update it.
if let Some ( current_env ) = exec_state
. memory
. environments
. get_mut ( exec_state . memory . current_env . index ( ) )
{
current_env . update_sketch_tags ( & solid . sketch ) ;
}
}
_ = > { }
}
Ok ( ( ) )
}
2024-10-30 16:52:17 -04:00
impl Node < TagDeclarator > {
2024-10-16 14:33:03 -07:00
pub async fn execute ( & self , exec_state : & mut ExecState ) -> Result < KclValue , KclError > {
let memory_item = KclValue ::TagIdentifier ( Box ::new ( TagIdentifier {
value : self . name . clone ( ) ,
info : None ,
meta : vec ! [ Metadata {
source_range : self . into ( ) ,
} ] ,
} ) ) ;
exec_state . memory . add ( & self . name , memory_item . clone ( ) , self . into ( ) ) ? ;
Ok ( self . into ( ) )
}
}
2024-10-30 16:52:17 -04:00
impl Node < ArrayExpression > {
2024-10-16 14:33:03 -07:00
#[ async_recursion ]
pub async fn execute ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
let mut results = Vec ::with_capacity ( self . elements . len ( ) ) ;
for element in & self . elements {
let metadata = Metadata ::from ( element ) ;
// TODO: Carry statement kind here so that we know if we're
// inside a variable declaration.
let value = ctx
. execute_expr ( element , exec_state , & metadata , StatementKind ::Expression )
. await ? ;
2024-11-14 17:27:19 -06:00
results . push ( value ) ;
2024-10-16 14:33:03 -07:00
}
2024-11-14 17:27:19 -06:00
Ok ( KclValue ::Array {
value : results ,
meta : vec ! [ self . into ( ) ] ,
} )
2024-10-16 14:33:03 -07:00
}
}
2024-10-30 16:52:17 -04:00
impl Node < ArrayRangeExpression > {
2024-10-16 14:33:03 -07:00
#[ async_recursion ]
pub async fn execute ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
2024-10-30 16:52:17 -04:00
let metadata = Metadata ::from ( & self . start_element ) ;
2024-10-16 14:33:03 -07:00
let start = ctx
. execute_expr ( & self . start_element , exec_state , & metadata , StatementKind ::Expression )
2024-11-14 17:27:19 -06:00
. await ? ;
let start = start . as_int ( ) . ok_or ( KclError ::Semantic ( KclErrorDetails {
source_ranges : vec ! [ self . into ( ) ] ,
message : format ! ( " Expected int but found {} " , start . human_friendly_type ( ) ) ,
} ) ) ? ;
2024-10-30 16:52:17 -04:00
let metadata = Metadata ::from ( & self . end_element ) ;
2024-10-16 14:33:03 -07:00
let end = ctx
. execute_expr ( & self . end_element , exec_state , & metadata , StatementKind ::Expression )
2024-11-14 17:27:19 -06:00
. await ? ;
let end = end . as_int ( ) . ok_or ( KclError ::Semantic ( KclErrorDetails {
source_ranges : vec ! [ self . into ( ) ] ,
message : format ! ( " Expected int but found {} " , end . human_friendly_type ( ) ) ,
} ) ) ? ;
2024-10-16 14:33:03 -07:00
if end < start {
return Err ( KclError ::Semantic ( KclErrorDetails {
source_ranges : vec ! [ self . into ( ) ] ,
message : format ! ( " Range start is greater than range end: {start} .. {end} " ) ,
} ) ) ;
}
let range : Vec < _ > = if self . end_inclusive {
2024-11-14 17:27:19 -06:00
( start ..= end ) . collect ( )
2024-10-16 14:33:03 -07:00
} else {
2024-11-14 17:27:19 -06:00
( start .. end ) . collect ( )
2024-10-16 14:33:03 -07:00
} ;
2024-11-14 17:27:19 -06:00
let meta = vec! [ Metadata {
source_range : self . into ( ) ,
} ] ;
Ok ( KclValue ::Array {
value : range
. into_iter ( )
. map ( | num | KclValue ::Int {
value : num ,
meta : meta . clone ( ) ,
} )
. collect ( ) ,
meta ,
} )
2024-10-16 14:33:03 -07:00
}
}
2024-10-30 16:52:17 -04:00
impl Node < ObjectExpression > {
2024-10-16 14:33:03 -07:00
#[ async_recursion ]
pub async fn execute ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
2024-11-14 17:27:19 -06:00
let mut object = HashMap ::with_capacity ( self . properties . len ( ) ) ;
2024-10-16 14:33:03 -07:00
for property in & self . properties {
let metadata = Metadata ::from ( & property . value ) ;
let result = ctx
. execute_expr ( & property . value , exec_state , & metadata , StatementKind ::Expression )
. await ? ;
2024-11-14 17:27:19 -06:00
object . insert ( property . key . name . clone ( ) , result ) ;
2024-10-16 14:33:03 -07:00
}
2024-11-14 17:27:19 -06:00
Ok ( KclValue ::Object {
value : object ,
2024-10-16 14:33:03 -07:00
meta : vec ! [ Metadata {
source_range : self . into ( ) ,
} ] ,
2024-11-14 17:27:19 -06:00
} )
2024-10-16 14:33:03 -07:00
}
}
2024-11-14 17:27:19 -06:00
fn article_for ( s : & str ) -> & 'static str {
if s . starts_with ( [ 'a' , 'e' , 'i' , 'o' , 'u' ] ) {
" an "
2024-10-16 14:33:03 -07:00
} else {
2024-11-14 17:27:19 -06:00
" a "
2024-10-16 14:33:03 -07:00
}
}
2024-11-14 17:27:19 -06:00
pub fn parse_number_as_f64 ( v : & KclValue , source_range : SourceRange ) -> Result < f64 , KclError > {
if let KclValue ::Number { value : n , .. } = & v {
Ok ( * n )
} else if let KclValue ::Int { value : n , .. } = & v {
Ok ( * n as f64 )
2024-10-16 14:33:03 -07:00
} else {
2024-11-14 17:27:19 -06:00
let actual_type = v . human_friendly_type ( ) ;
let article = if actual_type . starts_with ( [ 'a' , 'e' , 'i' , 'o' , 'u' ] ) {
" an "
} else {
" a "
} ;
Err ( KclError ::Semantic ( KclErrorDetails {
2024-10-16 14:33:03 -07:00
source_ranges : vec ! [ source_range ] ,
2024-11-14 17:27:19 -06:00
message : format ! ( " Expected a number, but found {article} {actual_type} " , ) ,
2024-10-16 14:33:03 -07:00
} ) )
}
}
2024-10-30 16:52:17 -04:00
impl Node < IfExpression > {
2024-10-17 12:30:44 -07:00
#[ async_recursion ]
pub async fn get_result ( & self , exec_state : & mut ExecState , ctx : & ExecutorContext ) -> Result < KclValue , KclError > {
// Check the `if` branch.
let cond = ctx
. execute_expr ( & self . cond , exec_state , & Metadata ::from ( self ) , StatementKind ::Expression )
. await ?
. get_bool ( ) ? ;
if cond {
let block_result = ctx . inner_execute ( & self . then_val , exec_state , BodyType ::Block ) . await ? ;
// Block must end in an expression, so this has to be Some.
// Enforced by the parser.
// See https://github.com/KittyCAD/modeling-app/issues/4015
return Ok ( block_result . unwrap ( ) ) ;
}
// Check any `else if` branches.
for else_if in & self . else_ifs {
let cond = ctx
. execute_expr (
& else_if . cond ,
exec_state ,
& Metadata ::from ( self ) ,
StatementKind ::Expression ,
)
. await ?
. get_bool ( ) ? ;
if cond {
let block_result = ctx
. inner_execute ( & else_if . then_val , exec_state , BodyType ::Block )
. await ? ;
// Block must end in an expression, so this has to be Some.
// Enforced by the parser.
// See https://github.com/KittyCAD/modeling-app/issues/4015
return Ok ( block_result . unwrap ( ) ) ;
}
}
// Run the final `else` branch.
ctx . inner_execute ( & self . final_else , exec_state , BodyType ::Block )
. await
. map ( | expr | expr . unwrap ( ) )
}
}
2024-10-17 16:29:27 -07:00
#[ derive(Debug) ]
enum Property {
2024-11-25 10:50:43 +13:00
UInt ( usize ) ,
2024-10-17 16:29:27 -07:00
String ( String ) ,
}
impl Property {
fn try_from (
computed : bool ,
value : LiteralIdentifier ,
exec_state : & ExecState ,
sr : SourceRange ,
) -> Result < Self , KclError > {
let property_sr = vec! [ sr ] ;
let property_src : SourceRange = value . clone ( ) . into ( ) ;
match value {
LiteralIdentifier ::Identifier ( identifier ) = > {
2024-10-30 16:52:17 -04:00
let name = & identifier . name ;
2024-10-17 16:29:27 -07:00
if ! computed {
// Treat the property as a literal
Ok ( Property ::String ( name . to_string ( ) ) )
} else {
// Actually evaluate memory to compute the property.
2024-10-30 16:52:17 -04:00
let prop = exec_state . memory . get ( name , property_src ) ? ;
2024-11-14 17:27:19 -06:00
jvalue_to_prop ( prop , property_sr , name )
2024-10-17 16:29:27 -07:00
}
}
LiteralIdentifier ::Literal ( literal ) = > {
let value = literal . value . clone ( ) ;
match value {
2024-11-25 10:50:43 +13:00
LiteralValue ::Number ( x ) = > {
if let Some ( x ) = crate ::try_f64_to_usize ( x ) {
Ok ( Property ::UInt ( x ) )
2024-10-17 16:29:27 -07:00
} else {
Err ( KclError ::Semantic ( KclErrorDetails {
source_ranges : property_sr ,
message : format ! ( " {x} is not a valid index, indices must be whole numbers >= 0 " ) ,
} ) )
}
}
LiteralValue ::String ( s ) = > Ok ( Property ::String ( s ) ) ,
_ = > Err ( KclError ::Semantic ( KclErrorDetails {
source_ranges : vec ! [ sr ] ,
2024-11-25 10:50:43 +13:00
message : " Only strings or numbers (>= 0) can be properties/indexes " . to_owned ( ) ,
2024-10-17 16:29:27 -07:00
} ) ) ,
}
}
}
}
}
2024-11-14 17:27:19 -06:00
fn jvalue_to_prop ( value : & KclValue , property_sr : Vec < SourceRange > , name : & str ) -> Result < Property , KclError > {
2024-10-17 16:29:27 -07:00
let make_err = | message : String | {
Err ::< Property , _ > ( KclError ::Semantic ( KclErrorDetails {
source_ranges : property_sr ,
message ,
} ) )
} ;
match value {
2024-11-14 17:27:19 -06:00
KclValue ::Int { value :num , meta : _ } = > {
let maybe_int : Result < usize , _ > = ( * num ) . try_into ( ) ;
if let Ok ( uint ) = maybe_int {
2024-11-25 10:50:43 +13:00
Ok ( Property ::UInt ( uint ) )
2024-11-14 17:27:19 -06:00
}
else {
make_err ( format! ( " ' {num} ' is negative, so you can't index an array with it " ) )
}
}
KclValue ::Number { value : num , meta :_ } = > {
let num = * num ;
if num < 0.0 {
return make_err ( format! ( " ' {num} ' is negative, so you can't index an array with it " ) )
}
let nearest_int = num . round ( ) ;
let delta = num - nearest_int ;
if delta < FLOAT_TO_INT_MAX_DELTA {
2024-11-25 10:50:43 +13:00
Ok ( Property ::UInt ( nearest_int as usize ) )
2024-10-17 16:29:27 -07:00
} else {
2024-11-14 17:27:19 -06:00
make_err ( format! ( " ' {num} ' is not an integer, so you can't index an array with it " ) )
2024-10-17 16:29:27 -07:00
}
}
2024-11-14 17:27:19 -06:00
KclValue ::String { value : x , meta :_ } = > Ok ( Property ::String ( x . to_owned ( ) ) ) ,
2024-10-17 16:29:27 -07:00
_ = > {
make_err ( format! ( " {name} is not a valid property/index, you can only use a string to get the property of an object, or an int (>= 0) to get an item in an array " ) )
}
}
}
impl Property {
fn type_name ( & self ) -> & 'static str {
match self {
2024-11-25 10:50:43 +13:00
Property ::UInt ( _ ) = > " number " ,
2024-10-17 16:29:27 -07:00
Property ::String ( _ ) = > " string " ,
}
}
}