diff --git a/src/wasm-lib/.config/nextest.toml b/src/wasm-lib/.config/nextest.toml index fb51a697d9..101bb0a69b 100644 --- a/src/wasm-lib/.config/nextest.toml +++ b/src/wasm-lib/.config/nextest.toml @@ -6,10 +6,10 @@ serial-integration = { max-threads = 4 } [profile.default] -slow-timeout = { period = "60s", terminate-after = 1 } +slow-timeout = { period = "10s", terminate-after = 1 } [profile.ci] -slow-timeout = { period = "120s", terminate-after = 10 } +slow-timeout = { period = "30s", terminate-after = 5 } [[profile.default.overrides]] filter = "test(serial_test_)" @@ -20,3 +20,7 @@ threads-required = 4 filter = "test(serial_test_)" test-group = "serial-integration" threads-required = 4 + +[[profile.default.overrides]] +filter = "test(parser::parser_impl::snapshot_tests)" +slow-timeout = { period = "1s", terminate-after = 5 } diff --git a/src/wasm-lib/kcl/src/ast/types.rs b/src/wasm-lib/kcl/src/ast/types.rs index ab4aebb104..7aa5632df6 100644 --- a/src/wasm-lib/kcl/src/ast/types.rs +++ b/src/wasm-lib/kcl/src/ast/types.rs @@ -11,9 +11,11 @@ use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, R pub use self::literal_value::LiteralValue; use crate::{ + docs::StdLibFn, errors::{KclError, KclErrorDetails}, - executor::{ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal}, + executor::{BodyType, ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal}, parser::PIPE_OPERATOR, + std::{kcl_stdlib::KclStdLibFn, FunctionKind}, }; mod literal_value; @@ -960,8 +962,8 @@ impl CallExpression { fn_args.push(result); } - match ctx.stdlib.get(&self.callee.name) { - Some(func) => { + match ctx.stdlib.get_either(&self.callee.name) { + FunctionKind::Core(func) => { // Attempt to call the function. let args = crate::std::Args::new(fn_args, self.into(), ctx.clone()); let result = func.std_lib_fn()(args).await?; @@ -973,15 +975,54 @@ impl CallExpression { Ok(result) } } - // Must be user-defined then - None => { + FunctionKind::Std(func) => { + let function_expression = func.function(); + if fn_args.len() != function_expression.params.len() { + return Err(KclError::Semantic(KclErrorDetails { + message: format!( + "Expected {} arguments, got {}", + function_expression.params.len(), + fn_args.len(), + ), + source_ranges: vec![(function_expression).into()], + })); + } + + // Add the arguments to the memory. + let mut fn_memory = memory.clone(); + for (index, param) in function_expression.params.iter().enumerate() { + fn_memory.add(¶m.name, fn_args.get(index).unwrap().clone(), param.into())?; + } + + // Call the stdlib function + let p = func.function().clone().body; + let results = crate::executor::execute(p, &mut fn_memory, BodyType::Block, ctx).await?; + let out = results.return_; + let result = out.ok_or_else(|| { + KclError::UndefinedValue(KclErrorDetails { + message: format!("Result of stdlib function {} is undefined", fn_name), + source_ranges: vec![self.into()], + }) + })?; + let result = result.get_value()?; + + if pipe_info.is_in_pipe { + pipe_info.index += 1; + pipe_info.previous_results.push(result); + + execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), ctx).await + } else { + Ok(result) + } + } + FunctionKind::UserDefined => { let func = memory.get(&fn_name, self.into())?; let result = func .call_fn(fn_args, memory.clone(), ctx.clone()) .await? .ok_or_else(|| { KclError::UndefinedValue(KclErrorDetails { - message: format!("Result of function {} is undefined", fn_name), + message: format!("Result of user-defined function {} is undefined", fn_name), source_ranges: vec![self.into()], }) })?; @@ -1056,10 +1097,15 @@ impl CallExpression { #[ts(export)] #[serde(tag = "type")] pub enum Function { - /// A stdlib function. + /// A stdlib function written in Rust (aka core lib). StdLib { /// The function. - func: Box, + func: Box, + }, + /// A stdlib function written in KCL. + StdLibKcl { + /// The function. + func: Box, }, /// A function that is defined in memory. #[default] diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index 7e4648f032..6d3e42808f 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use anyhow::Result; +use async_recursion::async_recursion; use kittycad::types::{Color, ModelingCmd, Point3D}; use parse_display::{Display, FromStr}; use schemars::JsonSchema; @@ -13,7 +14,7 @@ use crate::{ ast::types::{BodyItem, FunctionExpression, Value}, engine::{EngineConnection, EngineManager}, errors::{KclError, KclErrorDetails}, - std::StdLib, + std::{FunctionKind, StdLib}, }; #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] @@ -781,6 +782,7 @@ pub struct ExecutorContext { } /// Execute a AST's program. +#[async_recursion(?Send)] pub async fn execute( program: crate::ast::types::Program, memory: &mut ProgramMemory, @@ -828,27 +830,37 @@ pub async fn execute( } } let _show_fn = Box::new(crate::std::Show); - if let Some(func) = ctx.stdlib.get(&call_expr.callee.name) { - use crate::docs::StdLibFn; - if func.name() == _show_fn.name() { - if options != BodyType::Root { + match ctx.stdlib.get_either(&call_expr.callee.name) { + FunctionKind::Core(func) => { + use crate::docs::StdLibFn; + if func.name() == _show_fn.name() { + if options != BodyType::Root { + return Err(KclError::Semantic(KclErrorDetails { + message: "Cannot call show outside of a root".to_string(), + source_ranges: vec![call_expr.into()], + })); + } + + memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone())); + } + } + FunctionKind::Std(func) => { + let mut newmem = memory.clone(); + let result = execute(func.program().to_owned(), &mut newmem, BodyType::Block, ctx).await?; + memory.return_ = result.return_; + } + FunctionKind::UserDefined => { + if let Some(func) = memory.clone().root.get(&fn_name) { + let result = func.call_fn(args.clone(), memory.clone(), ctx.clone()).await?; + + memory.return_ = result; + } else { return Err(KclError::Semantic(KclErrorDetails { - message: "Cannot call show outside of a root".to_string(), + message: format!("No such name {} defined", fn_name), source_ranges: vec![call_expr.into()], })); } - - memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone())); } - } else if let Some(func) = memory.clone().root.get(&fn_name) { - let result = func.call_fn(args.clone(), memory.clone(), ctx.clone()).await?; - - memory.return_ = result; - } else { - return Err(KclError::Semantic(KclErrorDetails { - message: format!("No such name {} defined", fn_name), - source_ranges: vec![call_expr.into()], - })); } } } diff --git a/src/wasm-lib/kcl/src/parser/parser_impl.rs b/src/wasm-lib/kcl/src/parser/parser_impl.rs index f8f1449cf5..6fc7d566c5 100644 --- a/src/wasm-lib/kcl/src/parser/parser_impl.rs +++ b/src/wasm-lib/kcl/src/parser/parser_impl.rs @@ -1388,7 +1388,7 @@ const mySk1 = startSketchAt([0, 0])"#; let Value::PipeExpression(pipe) = val else { panic!("expected pipe"); }; - let mut noncode = dbg!(pipe.non_code_meta); + let mut noncode = pipe.non_code_meta; assert_eq!(noncode.non_code_nodes.len(), 1); let comment = noncode.non_code_nodes.remove(&0).unwrap().pop().unwrap(); assert_eq!( @@ -2575,8 +2575,7 @@ thing(false) let tokens = crate::token::lexer(test_program); let parser = crate::parser::Parser::new(tokens); let result = parser.ast(); - let e = result.unwrap_err(); - eprintln!("{e:?}") + let _e = result.unwrap_err(); } #[test] diff --git a/src/wasm-lib/kcl/src/parser/snapshots/kcl_lib__parser__parser_impl__snapshot_tests__au.snap b/src/wasm-lib/kcl/src/parser/snapshots/kcl_lib__parser__parser_impl__snapshot_tests__au.snap index ede1bc96fd..e828dab5cd 100644 --- a/src/wasm-lib/kcl/src/parser/snapshots/kcl_lib__parser__parser_impl__snapshot_tests__au.snap +++ b/src/wasm-lib/kcl/src/parser/snapshots/kcl_lib__parser__parser_impl__snapshot_tests__au.snap @@ -4,385 +4,69 @@ expression: actual --- { "start": 0, - "end": 330, + "end": 90, "body": [ { "type": "VariableDeclaration", "type": "VariableDeclaration", "start": 0, - "end": 254, + "end": 74, "declarations": [ { "type": "VariableDeclarator", - "start": 3, - "end": 254, + "start": 6, + "end": 74, "id": { "type": "Identifier", - "start": 3, - "end": 9, - "name": "circle" - }, - "init": { - "type": "FunctionExpression", - "type": "FunctionExpression", - "start": 12, - "end": 254, - "params": [ - { - "type": "Identifier", - "start": 13, - "end": 18, - "name": "plane" - }, - { - "type": "Identifier", - "start": 20, - "end": 26, - "name": "center" - }, - { - "type": "Identifier", - "start": 28, - "end": 34, - "name": "radius" - } - ], - "body": { - "start": 39, - "end": 254, - "body": [ - { - "type": "VariableDeclaration", - "type": "VariableDeclaration", - "start": 43, - "end": 240, - "declarations": [ - { - "type": "VariableDeclarator", - "start": 49, - "end": 240, - "id": { - "type": "Identifier", - "start": 49, - "end": 51, - "name": "sg" - }, - "init": { - "type": "PipeExpression", - "type": "PipeExpression", - "start": 54, - "end": 240, - "body": [ - { - "type": "CallExpression", - "type": "CallExpression", - "start": 54, - "end": 74, - "callee": { - "type": "Identifier", - "start": 54, - "end": 67, - "name": "startSketchOn" - }, - "arguments": [ - { - "type": "Identifier", - "type": "Identifier", - "start": 68, - "end": 73, - "name": "plane" - } - ], - "optional": false - }, - { - "type": "CallExpression", - "type": "CallExpression", - "start": 82, - "end": 132, - "callee": { - "type": "Identifier", - "start": 82, - "end": 96, - "name": "startProfileAt" - }, - "arguments": [ - { - "type": "ArrayExpression", - "type": "ArrayExpression", - "start": 97, - "end": 128, - "elements": [ - { - "type": "BinaryExpression", - "type": "BinaryExpression", - "start": 98, - "end": 116, - "operator": "+", - "left": { - "type": "MemberExpression", - "type": "MemberExpression", - "start": 98, - "end": 107, - "object": { - "type": "Identifier", - "type": "Identifier", - "start": 98, - "end": 104, - "name": "center" - }, - "property": { - "type": "Literal", - "type": "Literal", - "start": 105, - "end": 106, - "value": 0, - "raw": "0" - }, - "computed": false - }, - "right": { - "type": "Identifier", - "type": "Identifier", - "start": 110, - "end": 116, - "name": "radius" - } - }, - { - "type": "MemberExpression", - "type": "MemberExpression", - "start": 118, - "end": 127, - "object": { - "type": "Identifier", - "type": "Identifier", - "start": 118, - "end": 124, - "name": "center" - }, - "property": { - "type": "Literal", - "type": "Literal", - "start": 125, - "end": 126, - "value": 1, - "raw": "1" - }, - "computed": false - } - ] - }, - { - "type": "PipeSubstitution", - "type": "PipeSubstitution", - "start": 130, - "end": 131 - } - ], - "optional": false - }, - { - "type": "CallExpression", - "type": "CallExpression", - "start": 140, - "end": 224, - "callee": { - "type": "Identifier", - "start": 140, - "end": 143, - "name": "arc" - }, - "arguments": [ - { - "type": "ObjectExpression", - "type": "ObjectExpression", - "start": 144, - "end": 220, - "properties": [ - { - "type": "ObjectProperty", - "start": 153, - "end": 167, - "key": { - "type": "Identifier", - "start": 153, - "end": 162, - "name": "angle_end" - }, - "value": { - "type": "Literal", - "type": "Literal", - "start": 164, - "end": 167, - "value": 360, - "raw": "360" - } - }, - { - "type": "ObjectProperty", - "start": 176, - "end": 190, - "key": { - "type": "Identifier", - "start": 176, - "end": 187, - "name": "angle_start" - }, - "value": { - "type": "Literal", - "type": "Literal", - "start": 189, - "end": 190, - "value": 0, - "raw": "0" - } - }, - { - "type": "ObjectProperty", - "start": 199, - "end": 213, - "key": { - "type": "Identifier", - "start": 199, - "end": 205, - "name": "radius" - }, - "value": { - "type": "Identifier", - "type": "Identifier", - "start": 207, - "end": 213, - "name": "radius" - } - } - ] - }, - { - "type": "PipeSubstitution", - "type": "PipeSubstitution", - "start": 222, - "end": 223 - } - ], - "optional": false - }, - { - "type": "CallExpression", - "type": "CallExpression", - "start": 232, - "end": 240, - "callee": { - "type": "Identifier", - "start": 232, - "end": 237, - "name": "close" - }, - "arguments": [ - { - "type": "PipeSubstitution", - "type": "PipeSubstitution", - "start": 238, - "end": 239 - } - ], - "optional": false - } - ], - "nonCodeMeta": { - "nonCodeNodes": {}, - "start": [] - } - } - } - ], - "kind": "const" - }, - { - "type": "ReturnStatement", - "type": "ReturnStatement", - "start": 243, - "end": 252, - "argument": { - "type": "Identifier", - "type": "Identifier", - "start": 250, - "end": 252, - "name": "sg" - } - } - ], - "nonCodeMeta": { - "nonCodeNodes": {}, - "start": [] - } - } - } - } - ], - "kind": "fn" - }, - { - "type": "VariableDeclaration", - "type": "VariableDeclaration", - "start": 256, - "end": 314, - "declarations": [ - { - "type": "VariableDeclarator", - "start": 262, - "end": 314, - "id": { - "type": "Identifier", - "start": 262, - "end": 270, + "start": 6, + "end": 14, "name": "cylinder" }, "init": { "type": "PipeExpression", "type": "PipeExpression", - "start": 273, - "end": 314, + "start": 17, + "end": 74, "body": [ { "type": "CallExpression", "type": "CallExpression", - "start": 273, - "end": 296, + "start": 17, + "end": 56, "callee": { "type": "Identifier", - "start": 273, - "end": 279, - "name": "circle" + "start": 17, + "end": 39, + "name": "unstable_stdlib_circle" }, "arguments": [ { "type": "Literal", "type": "Literal", - "start": 280, - "end": 284, + "start": 40, + "end": 44, "value": "XY", "raw": "'XY'" }, { "type": "ArrayExpression", "type": "ArrayExpression", - "start": 286, - "end": 291, + "start": 46, + "end": 51, "elements": [ { "type": "Literal", "type": "Literal", - "start": 287, - "end": 288, + "start": 47, + "end": 48, "value": 0, "raw": "0" }, { "type": "Literal", "type": "Literal", - "start": 289, - "end": 290, + "start": 49, + "end": 50, "value": 0, "raw": "0" } @@ -391,8 +75,8 @@ expression: actual { "type": "Literal", "type": "Literal", - "start": 293, - "end": 295, + "start": 53, + "end": 55, "value": 22, "raw": "22" } @@ -402,28 +86,28 @@ expression: actual { "type": "CallExpression", "type": "CallExpression", - "start": 300, - "end": 314, + "start": 60, + "end": 74, "callee": { "type": "Identifier", - "start": 300, - "end": 307, + "start": 60, + "end": 67, "name": "extrude" }, "arguments": [ { "type": "Literal", "type": "Literal", - "start": 308, - "end": 310, + "start": 68, + "end": 70, "value": 14, "raw": "14" }, { "type": "PipeSubstitution", "type": "PipeSubstitution", - "start": 312, - "end": 313 + "start": 72, + "end": 73 } ], "optional": false @@ -441,25 +125,25 @@ expression: actual { "type": "ExpressionStatement", "type": "ExpressionStatement", - "start": 315, - "end": 329, + "start": 75, + "end": 89, "expression": { "type": "CallExpression", "type": "CallExpression", - "start": 315, - "end": 329, + "start": 75, + "end": 89, "callee": { "type": "Identifier", - "start": 315, - "end": 319, + "start": 75, + "end": 79, "name": "show" }, "arguments": [ { "type": "Identifier", "type": "Identifier", - "start": 320, - "end": 328, + "start": 80, + "end": 88, "name": "cylinder" } ], @@ -468,18 +152,7 @@ expression: actual } ], "nonCodeMeta": { - "nonCodeNodes": { - "0": [ - { - "type": "NonCodeNode", - "start": 254, - "end": 256, - "value": { - "type": "newLine" - } - } - ] - }, + "nonCodeNodes": {}, "start": [] } } diff --git a/src/wasm-lib/kcl/src/std/kcl_stdlib.rs b/src/wasm-lib/kcl/src/std/kcl_stdlib.rs new file mode 100644 index 0000000000..0eee747cfe --- /dev/null +++ b/src/wasm-lib/kcl/src/std/kcl_stdlib.rs @@ -0,0 +1,82 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{ + ast::types::{BodyItem, FunctionExpression, Program, Value}, + docs::{StdLibFn, StdLibFnData}, + token::lexer, +}; + +pub trait KclStdLibFn: StdLibFn { + fn kcl_clone_box(&self) -> Box; + fn function(&self) -> &FunctionExpression; + fn program(&self) -> &Program; +} + +impl ts_rs::TS for dyn KclStdLibFn { + const EXPORT_TO: Option<&'static str> = Some("bindings/StdLibFnData"); + + fn name() -> String { + "StdLibFnData".to_string() + } + + fn dependencies() -> Vec + where + Self: 'static, + { + StdLibFnData::dependencies() + } + + fn transparent() -> bool { + StdLibFnData::transparent() + } +} + +impl Clone for Box { + fn clone(&self) -> Box { + self.kcl_clone_box() + } +} + +impl JsonSchema for dyn KclStdLibFn { + fn schema_name() -> String { + "KclStdLibFn".to_string() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + gen.subschema_for::() + } +} + +impl<'de> Deserialize<'de> for Box { + fn deserialize>(deserializer: D) -> Result { + let data = StdLibFnData::deserialize(deserializer)?; + let stdlib = crate::std::StdLib::new(); + let stdlib_fn = stdlib + .get_kcl(&data.name) + .ok_or_else(|| serde::de::Error::custom(format!("StdLibFn {} not found", data.name)))?; + Ok(stdlib_fn) + } +} + +impl Serialize for Box { + fn serialize(&self, serializer: S) -> Result { + self.to_json().unwrap().serialize(serializer) + } +} + +/// Parse a KCL program. Expect it to have a single body item, which is a function. +/// Return the program and its single function. +/// Return None if those expectations aren't met. +pub fn extract_function(source: &str) -> Option<(Program, Box)> { + let tokens = lexer(source); + let src = crate::parser::Parser::new(tokens).ast().ok()?; + assert_eq!(src.body.len(), 1); + let BodyItem::ExpressionStatement(expr) = src.body.last()? else { + panic!("expected expression statement"); + }; + let Value::FunctionExpression(function) = expr.expression.clone() else { + panic!("expected function expr"); + }; + Some((src, function)) +} diff --git a/src/wasm-lib/kcl/src/std/mod.rs b/src/wasm-lib/kcl/src/std/mod.rs index 9cbefb2893..40a5547721 100644 --- a/src/wasm-lib/kcl/src/std/mod.rs +++ b/src/wasm-lib/kcl/src/std/mod.rs @@ -1,8 +1,10 @@ //! Functions implemented for language execution. pub mod extrude; +pub mod kcl_stdlib; pub mod math; pub mod segment; +pub mod shapes; pub mod sketch; pub mod utils; @@ -16,6 +18,7 @@ use parse_display::{Display, FromStr}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use self::kcl_stdlib::KclStdLibFn; use crate::{ ast::types::parse_json_number_as_f64, docs::StdLibFn, @@ -92,12 +95,16 @@ pub fn name_in_stdlib(name: &str) -> bool { } pub struct StdLib { - pub fns: HashMap>, + pub fns: HashMap>, + pub kcl_fns: HashMap>, } impl std::fmt::Debug for StdLib { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("StdLib").field("fns.len()", &self.fns.len()).finish() + f.debug_struct("StdLib") + .field("fns.len()", &self.fns.len()) + .field("kcl_fns.len()", &self.kcl_fns.len()) + .finish() } } @@ -109,12 +116,36 @@ impl StdLib { .map(|internal_fn| (internal_fn.name(), internal_fn)) .collect(); - Self { fns } + let kcl_internal_fns: [Box; 1] = [Box::::default()]; + let kcl_fns = kcl_internal_fns + .into_iter() + .map(|internal_fn| (internal_fn.name(), internal_fn)) + .collect(); + + Self { fns, kcl_fns } } pub fn get(&self, name: &str) -> Option> { self.fns.get(name).cloned() } + + pub fn get_kcl(&self, name: &str) -> Option> { + self.kcl_fns.get(name).cloned() + } + + pub fn get_either(&self, name: &str) -> FunctionKind { + if let Some(f) = self.get(name) { + FunctionKind::Core(f) + } else if let Some(f) = self.get_kcl(name) { + FunctionKind::Std(f) + } else { + FunctionKind::UserDefined + } + } + + pub fn contains_key(&self, key: &str) -> bool { + self.fns.contains_key(key) || self.kcl_fns.contains_key(key) + } } impl Default for StdLib { @@ -123,6 +154,12 @@ impl Default for StdLib { } } +pub enum FunctionKind { + Core(Box), + Std(Box), + UserDefined, +} + #[derive(Debug, Clone)] pub struct Args { pub args: Vec, diff --git a/src/wasm-lib/kcl/src/std/shapes.rs b/src/wasm-lib/kcl/src/std/shapes.rs new file mode 100644 index 0000000000..8bafb0cc50 --- /dev/null +++ b/src/wasm-lib/kcl/src/std/shapes.rs @@ -0,0 +1,102 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use super::kcl_stdlib::KclStdLibFn; +use crate::{ + ast::types::{FunctionExpression, Program}, + docs::StdLibFn, +}; + +pub const CIRCLE_FN: &str = r#" +(plane, center, radius) => { + const sg = startSketchOn(plane) + |> startProfileAt([center[0] + radius, center[1]], %) + |> arc({ + angle_end: 360, + angle_start: 0, + radius: radius + }, %) + |> close(%) + return sg +} + "#; + +#[derive(Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] +pub struct Circle { + function: FunctionExpression, + program: Program, +} + +impl Default for Circle { + fn default() -> Self { + // TODO in https://github.com/KittyCAD/modeling-app/issues/1018 + // Don't unwrap here, parse it at compile-time. + let (src, function) = super::kcl_stdlib::extract_function(CIRCLE_FN).unwrap(); + Self { + function: *function, + program: src, + } + } +} + +impl std::fmt::Debug for Circle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + "circle".fmt(f) + } +} + +/// TODO: Parse the KCL in a macro and generate these +impl StdLibFn for Circle { + fn name(&self) -> String { + "unstable_stdlib_circle".to_owned() + } + + fn summary(&self) -> String { + "Sketch a circle on the given plane".to_owned() + } + + fn description(&self) -> String { + String::new() + } + + fn tags(&self) -> Vec { + Vec::new() + } + + fn args(&self) -> Vec { + Vec::new() // TODO + } + + fn return_value(&self) -> Option { + None + } + + fn unpublished(&self) -> bool { + true + } + + fn deprecated(&self) -> bool { + false + } + + fn std_lib_fn(&self) -> crate::std::StdFn { + todo!() + } + + fn clone_box(&self) -> Box { + Box::new(self.to_owned()) + } +} + +impl KclStdLibFn for Circle { + fn function(&self) -> &FunctionExpression { + &self.function + } + fn program(&self) -> &Program { + &self.program + } + + fn kcl_clone_box(&self) -> Box { + Box::new(self.clone()) + } +} diff --git a/src/wasm-lib/tests/executor/inputs/cylinder.kcl b/src/wasm-lib/tests/executor/inputs/cylinder.kcl index abaca2a052..42f3eee28e 100644 --- a/src/wasm-lib/tests/executor/inputs/cylinder.kcl +++ b/src/wasm-lib/tests/executor/inputs/cylinder.kcl @@ -1,14 +1,2 @@ -fn circle = (plane, center, radius) => { - const sg = startSketchOn(plane) - |> startProfileAt([center[0] + radius, center[1]], %) - |> arc({ - angle_end: 360, - angle_start: 0, - radius: radius - }, %) - |> close(%) - return sg -} - -const cylinder = circle('XY', [0,0], 22) |> extrude(14, %) +const cylinder = unstable_stdlib_circle('XY', [0,0], 22) |> extrude(14, %) show(cylinder)