2023-08-24 15:34:51 -07:00
|
|
|
//! The executor for the AST.
|
|
|
|
|
2025-02-11 13:52:46 +13:00
|
|
|
use std::{path::PathBuf, sync::Arc};
|
2023-08-24 15:34:51 -07:00
|
|
|
|
|
|
|
use anyhow::Result;
|
2025-02-28 16:06:26 -05:00
|
|
|
pub use artifact::{
|
|
|
|
Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane,
|
|
|
|
};
|
2025-02-05 17:53:49 +13:00
|
|
|
use cache::OldAstState;
|
2025-02-26 19:29:59 -08:00
|
|
|
pub use cache::{bust_cache, clear_mem_cache};
|
|
|
|
pub use cad_op::Operation;
|
|
|
|
pub use geometry::*;
|
2025-03-15 10:08:39 -07:00
|
|
|
pub use id_generator::IdGenerator;
|
2025-04-11 15:11:59 +12:00
|
|
|
pub(crate) use import::PreImportedGeometry;
|
2024-11-07 11:23:41 -05:00
|
|
|
use indexmap::IndexMap;
|
2025-03-21 10:56:55 +13:00
|
|
|
pub use kcl_value::{KclObjectFields, KclValue};
|
2024-09-19 14:06:29 -07:00
|
|
|
use kcmc::{
|
|
|
|
each_cmd as mcmd,
|
|
|
|
ok_response::{output::TakeSnapshot, OkModelingCmdResponse},
|
|
|
|
websocket::{ModelingSessionData, OkWebSocketResponseData},
|
|
|
|
ImageFormat, ModelingCmd,
|
|
|
|
};
|
2025-02-05 17:53:49 +13:00
|
|
|
use kittycad_modeling_cmds as kcmc;
|
2025-02-26 19:29:59 -08:00
|
|
|
pub use memory::EnvironmentRef;
|
2023-08-25 13:41:04 -07:00
|
|
|
use schemars::JsonSchema;
|
2023-08-24 15:34:51 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
2025-03-15 10:08:39 -07:00
|
|
|
pub use state::{ExecState, MetaSettings};
|
2023-08-25 13:41:04 -07:00
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
use crate::{
|
|
|
|
engine::EngineManager,
|
2025-04-16 11:52:14 -07:00
|
|
|
errors::{KclError, KclErrorDetails},
|
2025-02-05 17:53:49 +13:00
|
|
|
execution::{
|
|
|
|
artifact::build_artifact_graph,
|
|
|
|
cache::{CacheInformation, CacheResult},
|
2025-03-21 10:56:55 +13:00
|
|
|
types::{UnitAngle, UnitLen},
|
2025-02-05 17:53:49 +13:00
|
|
|
},
|
2025-02-11 13:52:46 +13:00
|
|
|
fs::FileManager,
|
2025-04-16 11:52:14 -07:00
|
|
|
modules::{ModuleId, ModulePath, ModuleRepr},
|
2025-02-27 22:24:48 -08:00
|
|
|
parsing::ast::types::{Expr, ImportPath, NodeRef},
|
2025-02-11 13:52:46 +13:00
|
|
|
source_range::SourceRange,
|
2025-02-20 19:33:21 +13:00
|
|
|
std::StdLib,
|
2025-04-16 11:52:14 -07:00
|
|
|
CompilationError, ExecError, KclErrorWithOutputs,
|
2025-02-05 17:53:49 +13:00
|
|
|
};
|
2024-09-18 17:04:04 -05:00
|
|
|
|
2025-01-31 13:11:15 -08:00
|
|
|
pub(crate) mod annotations;
|
2025-01-08 20:02:30 -05:00
|
|
|
mod artifact;
|
2024-12-10 18:50:22 -08:00
|
|
|
pub(crate) mod cache;
|
2024-12-16 13:10:31 -05:00
|
|
|
mod cad_op;
|
2024-12-10 18:50:22 -08:00
|
|
|
mod exec_ast;
|
2025-02-05 17:53:49 +13:00
|
|
|
mod geometry;
|
2025-03-15 10:08:39 -07:00
|
|
|
mod id_generator;
|
2025-01-29 08:28:32 +13:00
|
|
|
mod import;
|
2025-01-31 13:11:15 -08:00
|
|
|
pub(crate) mod kcl_value;
|
2025-02-05 17:53:49 +13:00
|
|
|
mod memory;
|
|
|
|
mod state;
|
2025-03-21 10:56:55 +13:00
|
|
|
pub(crate) mod types;
|
2024-09-16 15:10:33 -04:00
|
|
|
|
2025-01-06 16:55:59 -05:00
|
|
|
/// Outcome of executing a program. This is used in TS.
|
2025-03-08 04:04:57 +13:00
|
|
|
#[derive(Debug, Clone, Serialize, ts_rs::TS)]
|
2025-01-06 16:55:59 -05:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct ExecOutcome {
|
2025-02-12 10:22:56 +13:00
|
|
|
/// Variables in the top-level of the root module. Note that functions will have an invalid env ref.
|
|
|
|
pub variables: IndexMap<String, KclValue>,
|
2025-01-06 16:55:59 -05:00
|
|
|
/// Operations that have been performed in execution order, for display in
|
|
|
|
/// the Feature Tree.
|
|
|
|
pub operations: Vec<Operation>,
|
2025-01-08 20:02:30 -05:00
|
|
|
/// Output commands to allow building the artifact graph by the caller.
|
|
|
|
pub artifact_commands: Vec<ArtifactCommand>,
|
2025-01-17 14:34:36 -05:00
|
|
|
/// Output artifact graph.
|
|
|
|
pub artifact_graph: ArtifactGraph,
|
2025-02-21 09:30:44 +13:00
|
|
|
/// Non-fatal errors and warnings.
|
|
|
|
pub errors: Vec<CompilationError>,
|
2025-02-25 11:51:54 -06:00
|
|
|
/// File Names in module Id array index order
|
|
|
|
pub filenames: IndexMap<ModuleId, ModulePath>,
|
2025-03-15 10:08:39 -07:00
|
|
|
/// The default planes.
|
|
|
|
pub default_planes: Option<DefaultPlanes>,
|
2025-01-06 16:55:59 -05:00
|
|
|
}
|
|
|
|
|
2024-04-15 17:18:32 -07:00
|
|
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct DefaultPlanes {
|
|
|
|
pub xy: uuid::Uuid,
|
|
|
|
pub xz: uuid::Uuid,
|
|
|
|
pub yz: uuid::Uuid,
|
|
|
|
pub neg_xy: uuid::Uuid,
|
|
|
|
pub neg_xz: uuid::Uuid,
|
|
|
|
pub neg_yz: uuid::Uuid,
|
|
|
|
}
|
|
|
|
|
2024-07-27 22:56:46 -07:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2024-06-24 14:45:07 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
|
|
pub struct TagIdentifier {
|
|
|
|
pub value: String,
|
2025-03-17 12:28:51 +13:00
|
|
|
// Multi-version representation of info about the tag. Kept ordered. The usize is the epoch at which the info
|
|
|
|
// was written. Note that there might be multiple versions of tag info from the same epoch, the version with
|
|
|
|
// the higher index will be the most recent.
|
|
|
|
#[serde(skip)]
|
|
|
|
pub info: Vec<(usize, TagEngineInfo)>,
|
2025-03-13 11:13:33 -07:00
|
|
|
#[serde(skip)]
|
2024-06-24 14:45:07 -07:00
|
|
|
pub meta: Vec<Metadata>,
|
|
|
|
}
|
|
|
|
|
2025-03-17 12:28:51 +13:00
|
|
|
impl TagIdentifier {
|
|
|
|
/// Get the tag info for this tag at a specified epoch.
|
|
|
|
pub fn get_info(&self, at_epoch: usize) -> Option<&TagEngineInfo> {
|
|
|
|
for (e, info) in self.info.iter().rev() {
|
|
|
|
if *e <= at_epoch {
|
|
|
|
return Some(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the most recent tag info for this tag.
|
|
|
|
pub fn get_cur_info(&self) -> Option<&TagEngineInfo> {
|
|
|
|
self.info.last().map(|i| &i.1)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add info from a different instance of this tag.
|
|
|
|
pub fn merge_info(&mut self, other: &TagIdentifier) {
|
|
|
|
assert_eq!(&self.value, &other.value);
|
|
|
|
'new_info: for (oe, ot) in &other.info {
|
|
|
|
for (e, _) in &self.info {
|
|
|
|
if e > oe {
|
|
|
|
continue 'new_info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.info.push((*oe, ot.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-27 22:56:46 -07:00
|
|
|
impl Eq for TagIdentifier {}
|
|
|
|
|
2024-06-24 14:45:07 -07:00
|
|
|
impl std::fmt::Display for TagIdentifier {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::str::FromStr for TagIdentifier {
|
|
|
|
type Err = KclError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
Ok(Self {
|
|
|
|
value: s.to_string(),
|
2025-03-17 12:28:51 +13:00
|
|
|
info: Vec::new(),
|
2024-06-24 14:45:07 -07:00
|
|
|
meta: Default::default(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ord for TagIdentifier {
|
|
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
|
|
self.value.cmp(&other.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialOrd for TagIdentifier {
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
|
|
Some(self.cmp(other))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::hash::Hash for TagIdentifier {
|
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
|
|
self.value.hash(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
/// Engine information for a tag.
|
2025-04-07 19:02:41 +12:00
|
|
|
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2025-02-05 17:53:49 +13:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
|
|
pub struct TagEngineInfo {
|
|
|
|
/// The id of the tagged object.
|
|
|
|
pub id: uuid::Uuid,
|
|
|
|
/// The sketch the tag is on.
|
|
|
|
pub sketch: uuid::Uuid,
|
|
|
|
/// The path the tag is on.
|
|
|
|
pub path: Option<Path>,
|
|
|
|
/// The surface information for the tag.
|
|
|
|
pub surface: Option<ExtrudeSurface>,
|
2023-08-24 15:34:51 -07:00
|
|
|
}
|
|
|
|
|
2025-02-21 09:30:44 +13:00
|
|
|
#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq)]
|
2023-08-24 15:34:51 -07:00
|
|
|
pub enum BodyType {
|
2025-02-25 16:10:06 +13:00
|
|
|
Root,
|
2023-08-24 15:34:51 -07:00
|
|
|
Block,
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Metadata.
|
2024-11-14 17:27:19 -06:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq, Copy)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct Metadata {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The source range.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub source_range: SourceRange,
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl From<Metadata> for Vec<SourceRange> {
|
|
|
|
fn from(meta: Metadata) -> Self {
|
|
|
|
vec![meta.source_range]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-24 15:34:51 -07:00
|
|
|
impl From<SourceRange> for Metadata {
|
|
|
|
fn from(source_range: SourceRange) -> Self {
|
|
|
|
Self { source_range }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 16:52:17 -04:00
|
|
|
impl<T> From<NodeRef<'_, T>> for Metadata {
|
|
|
|
fn from(node: NodeRef<'_, T>) -> Self {
|
2024-10-17 00:48:33 -04:00
|
|
|
Self {
|
2024-11-07 11:23:41 -05:00
|
|
|
source_range: SourceRange::new(node.start, node.end, node.module_id),
|
2024-08-14 01:57:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-16 15:10:33 -04:00
|
|
|
impl From<&Expr> for Metadata {
|
|
|
|
fn from(expr: &Expr) -> Self {
|
|
|
|
Self {
|
|
|
|
source_range: SourceRange::from(expr),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-01 12:45:01 -07:00
|
|
|
/// The type of ExecutorContext being used
|
|
|
|
#[derive(PartialEq, Debug, Default, Clone)]
|
|
|
|
pub enum ContextType {
|
|
|
|
/// Live engine connection
|
|
|
|
#[default]
|
|
|
|
Live,
|
|
|
|
|
|
|
|
/// Completely mocked connection
|
2025-04-03 22:24:51 -04:00
|
|
|
/// Mock mode is only for the Design Studio when they just want to mock engine calls and not
|
2024-10-01 12:45:01 -07:00
|
|
|
/// actually make them.
|
|
|
|
Mock,
|
|
|
|
|
|
|
|
/// Handled by some other interpreter/conversion system
|
|
|
|
MockCustomForwarded,
|
|
|
|
}
|
|
|
|
|
2023-10-05 14:27:48 -07:00
|
|
|
/// The executor context.
|
2024-06-06 16:01:41 -05:00
|
|
|
/// Cloning will return another handle to the same engine connection/session,
|
|
|
|
/// as this uses `Arc` under the hood.
|
2023-10-05 14:27:48 -07:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct ExecutorContext {
|
2024-03-13 12:56:46 -07:00
|
|
|
pub engine: Arc<Box<dyn EngineManager>>,
|
2024-04-15 17:18:32 -07:00
|
|
|
pub fs: Arc<FileManager>,
|
2023-11-08 20:23:59 -06:00
|
|
|
pub stdlib: Arc<StdLib>,
|
2024-04-25 00:13:09 -07:00
|
|
|
pub settings: ExecutorSettings,
|
2024-10-01 12:45:01 -07:00
|
|
|
pub context_type: ContextType,
|
2023-10-05 14:27:48 -07:00
|
|
|
}
|
|
|
|
|
2024-04-25 00:13:09 -07:00
|
|
|
/// The executor settings.
|
2024-12-05 19:51:06 -08:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
|
|
#[ts(export)]
|
2024-04-25 00:13:09 -07:00
|
|
|
pub struct ExecutorSettings {
|
|
|
|
/// Highlight edges of 3D objects?
|
|
|
|
pub highlight_edges: bool,
|
2024-04-25 02:31:18 -07:00
|
|
|
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
|
|
|
pub enable_ssao: bool,
|
2024-08-23 17:40:30 -05:00
|
|
|
/// Show grid?
|
2024-07-05 12:39:58 -07:00
|
|
|
pub show_grid: bool,
|
2024-08-23 17:40:30 -05:00
|
|
|
/// Should engine store this for replay?
|
|
|
|
/// If so, under what name?
|
|
|
|
pub replay: Option<String>,
|
2024-12-07 07:16:04 +13:00
|
|
|
/// The directory of the current project. This is used for resolving import
|
|
|
|
/// paths. If None is given, the current working directory is used.
|
|
|
|
pub project_directory: Option<PathBuf>,
|
2025-01-28 15:43:39 -08:00
|
|
|
/// This is the path to the current file being executed.
|
|
|
|
/// We use this for preventing cyclic imports.
|
|
|
|
pub current_file: Option<PathBuf>,
|
2024-04-25 00:13:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ExecutorSettings {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
highlight_edges: true,
|
2024-04-25 02:31:18 -07:00
|
|
|
enable_ssao: false,
|
2024-07-05 12:39:58 -07:00
|
|
|
show_grid: false,
|
2024-08-23 17:40:30 -05:00
|
|
|
replay: None,
|
2024-12-07 07:16:04 +13:00
|
|
|
project_directory: None,
|
2025-01-28 15:43:39 -08:00
|
|
|
current_file: None,
|
2024-04-25 02:31:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<crate::settings::types::Configuration> for ExecutorSettings {
|
|
|
|
fn from(config: crate::settings::types::Configuration) -> Self {
|
|
|
|
Self {
|
|
|
|
highlight_edges: config.settings.modeling.highlight_edges.into(),
|
|
|
|
enable_ssao: config.settings.modeling.enable_ssao.into(),
|
2024-07-05 12:39:58 -07:00
|
|
|
show_grid: config.settings.modeling.show_scale_grid,
|
2024-08-23 17:40:30 -05:00
|
|
|
replay: None,
|
2024-12-07 07:16:04 +13:00
|
|
|
project_directory: None,
|
2025-01-28 15:43:39 -08:00
|
|
|
current_file: None,
|
2024-04-25 02:31:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<crate::settings::types::project::ProjectConfiguration> for ExecutorSettings {
|
|
|
|
fn from(config: crate::settings::types::project::ProjectConfiguration) -> Self {
|
|
|
|
Self {
|
|
|
|
highlight_edges: config.settings.modeling.highlight_edges.into(),
|
|
|
|
enable_ssao: config.settings.modeling.enable_ssao.into(),
|
2025-02-28 16:15:57 -08:00
|
|
|
show_grid: Default::default(),
|
2024-08-23 17:40:30 -05:00
|
|
|
replay: None,
|
2024-12-07 07:16:04 +13:00
|
|
|
project_directory: None,
|
2025-01-28 15:43:39 -08:00
|
|
|
current_file: None,
|
2024-04-25 02:31:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<crate::settings::types::ModelingSettings> for ExecutorSettings {
|
|
|
|
fn from(modeling: crate::settings::types::ModelingSettings) -> Self {
|
|
|
|
Self {
|
|
|
|
highlight_edges: modeling.highlight_edges.into(),
|
|
|
|
enable_ssao: modeling.enable_ssao.into(),
|
2024-07-05 12:39:58 -07:00
|
|
|
show_grid: modeling.show_scale_grid,
|
2024-08-23 17:40:30 -05:00
|
|
|
replay: None,
|
2024-12-07 07:16:04 +13:00
|
|
|
project_directory: None,
|
2025-01-28 15:43:39 -08:00
|
|
|
current_file: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-28 19:02:06 -08:00
|
|
|
impl From<crate::settings::types::project::ProjectModelingSettings> for ExecutorSettings {
|
|
|
|
fn from(modeling: crate::settings::types::project::ProjectModelingSettings) -> Self {
|
|
|
|
Self {
|
|
|
|
highlight_edges: modeling.highlight_edges.into(),
|
|
|
|
enable_ssao: modeling.enable_ssao.into(),
|
|
|
|
show_grid: Default::default(),
|
|
|
|
replay: None,
|
|
|
|
project_directory: None,
|
|
|
|
current_file: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-28 15:43:39 -08:00
|
|
|
impl ExecutorSettings {
|
|
|
|
/// Add the current file path to the executor settings.
|
|
|
|
pub fn with_current_file(&mut self, current_file: PathBuf) {
|
|
|
|
// We want the parent directory of the file.
|
|
|
|
if current_file.extension() == Some(std::ffi::OsStr::new("kcl")) {
|
|
|
|
self.current_file = Some(current_file.clone());
|
|
|
|
// Get the parent directory.
|
|
|
|
if let Some(parent) = current_file.parent() {
|
|
|
|
self.project_directory = Some(parent.to_path_buf());
|
|
|
|
} else {
|
|
|
|
self.project_directory = Some(std::path::PathBuf::from(""));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.project_directory = Some(current_file.clone());
|
2024-04-25 00:13:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-12 12:18:37 -08:00
|
|
|
impl ExecutorContext {
|
|
|
|
/// Create a new default executor context.
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2024-08-23 17:40:30 -05:00
|
|
|
pub async fn new(client: &kittycad::Client, settings: ExecutorSettings) -> Result<Self> {
|
|
|
|
let (ws, _headers) = client
|
2024-04-25 02:31:18 -07:00
|
|
|
.modeling()
|
|
|
|
.commands_ws(
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
if settings.enable_ssao {
|
|
|
|
Some(kittycad::types::PostEffectType::Ssao)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
2024-08-23 17:40:30 -05:00
|
|
|
settings.replay.clone(),
|
2024-07-05 12:39:58 -07:00
|
|
|
if settings.show_grid { Some(true) } else { None },
|
2024-04-25 02:31:18 -07:00
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
Some(false),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let engine: Arc<Box<dyn EngineManager>> =
|
|
|
|
Arc::new(Box::new(crate::engine::conn::EngineConnection::new(ws).await?));
|
|
|
|
|
2024-08-23 17:40:30 -05:00
|
|
|
Ok(Self {
|
2024-04-25 02:31:18 -07:00
|
|
|
engine,
|
2024-04-15 17:18:32 -07:00
|
|
|
fs: Arc::new(FileManager::new()),
|
2024-02-12 12:18:37 -08:00
|
|
|
stdlib: Arc::new(StdLib::new()),
|
2024-04-25 00:13:09 -07:00
|
|
|
settings,
|
2024-10-01 12:45:01 -07:00
|
|
|
context_type: ContextType::Live,
|
2024-08-23 17:40:30 -05:00
|
|
|
})
|
2024-02-12 12:18:37 -08:00
|
|
|
}
|
|
|
|
|
2024-11-20 15:19:25 +13:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
2025-03-15 10:08:39 -07:00
|
|
|
pub fn new(engine: Arc<Box<dyn EngineManager>>, fs: Arc<FileManager>, settings: ExecutorSettings) -> Self {
|
|
|
|
ExecutorContext {
|
|
|
|
engine,
|
|
|
|
fs,
|
2024-11-20 15:19:25 +13:00
|
|
|
stdlib: Arc::new(StdLib::new()),
|
2024-12-10 18:50:22 -08:00
|
|
|
settings,
|
2024-11-20 15:19:25 +13:00
|
|
|
context_type: ContextType::Live,
|
2025-03-15 10:08:39 -07:00
|
|
|
}
|
2024-11-20 15:19:25 +13:00
|
|
|
}
|
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
pub async fn new_mock() -> Self {
|
|
|
|
ExecutorContext {
|
|
|
|
engine: Arc::new(Box::new(
|
|
|
|
crate::engine::conn_mock::EngineConnection::new().await.unwrap(),
|
|
|
|
)),
|
|
|
|
fs: Arc::new(FileManager::new()),
|
|
|
|
stdlib: Arc::new(StdLib::new()),
|
|
|
|
settings: Default::default(),
|
|
|
|
context_type: ContextType::Mock,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-20 15:19:25 +13:00
|
|
|
#[cfg(target_arch = "wasm32")]
|
2025-03-17 18:26:11 -07:00
|
|
|
pub fn new_mock(engine: Arc<Box<dyn EngineManager>>, fs: Arc<FileManager>, settings: ExecutorSettings) -> Self {
|
|
|
|
ExecutorContext {
|
|
|
|
engine,
|
|
|
|
fs,
|
2024-11-20 15:19:25 +13:00
|
|
|
stdlib: Arc::new(StdLib::new()),
|
2024-12-10 18:50:22 -08:00
|
|
|
settings,
|
2024-11-20 15:19:25 +13:00
|
|
|
context_type: ContextType::Mock,
|
2025-03-17 18:26:11 -07:00
|
|
|
}
|
2024-11-20 15:19:25 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
pub fn new_forwarded_mock(engine: Arc<Box<dyn EngineManager>>) -> Self {
|
|
|
|
ExecutorContext {
|
|
|
|
engine,
|
|
|
|
fs: Arc::new(FileManager::new()),
|
|
|
|
stdlib: Arc::new(StdLib::new()),
|
|
|
|
settings: Default::default(),
|
|
|
|
context_type: ContextType::MockCustomForwarded,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-25 20:06:32 -07:00
|
|
|
/// Create a new default executor context.
|
|
|
|
/// With a kittycad client.
|
|
|
|
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
|
|
|
|
/// variables.
|
|
|
|
/// But also allows for passing in a token and engine address directly.
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
pub async fn new_with_client(
|
|
|
|
settings: ExecutorSettings,
|
|
|
|
token: Option<String>,
|
|
|
|
engine_addr: Option<String>,
|
|
|
|
) -> Result<Self> {
|
|
|
|
// Create the client.
|
2025-02-05 17:53:49 +13:00
|
|
|
let client = crate::engine::new_zoo_client(token, engine_addr)?;
|
2024-10-25 20:06:32 -07:00
|
|
|
|
|
|
|
let ctx = Self::new(&client, settings).await?;
|
|
|
|
Ok(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new default executor context.
|
|
|
|
/// With the default kittycad client.
|
|
|
|
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
|
|
|
|
/// variables.
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2025-03-31 10:56:03 -04:00
|
|
|
pub async fn new_with_default_client() -> Result<Self> {
|
2024-10-25 20:06:32 -07:00
|
|
|
// Create the client.
|
2025-03-31 10:56:03 -04:00
|
|
|
let ctx = Self::new_with_client(Default::default(), None, None).await?;
|
2024-10-25 20:06:32 -07:00
|
|
|
Ok(ctx)
|
|
|
|
}
|
|
|
|
|
2024-06-18 14:38:25 -05:00
|
|
|
/// For executing unit tests.
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
2025-03-31 10:56:03 -04:00
|
|
|
pub async fn new_for_unit_test(engine_addr: Option<String>) -> Result<Self> {
|
2024-10-25 20:06:32 -07:00
|
|
|
let ctx = ExecutorContext::new_with_client(
|
2024-06-18 14:38:25 -05:00
|
|
|
ExecutorSettings {
|
|
|
|
highlight_edges: true,
|
|
|
|
enable_ssao: false,
|
2024-07-05 12:39:58 -07:00
|
|
|
show_grid: false,
|
2024-08-23 17:40:30 -05:00
|
|
|
replay: None,
|
2024-12-07 07:16:04 +13:00
|
|
|
project_directory: None,
|
2025-01-28 15:43:39 -08:00
|
|
|
current_file: None,
|
2024-06-18 14:38:25 -05:00
|
|
|
},
|
2024-10-25 20:06:32 -07:00
|
|
|
None,
|
|
|
|
engine_addr,
|
2024-06-18 14:38:25 -05:00
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
Ok(ctx)
|
|
|
|
}
|
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
pub fn is_mock(&self) -> bool {
|
|
|
|
self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded
|
|
|
|
}
|
|
|
|
|
2025-02-12 22:37:34 -05:00
|
|
|
/// Returns true if we should not send engine commands for any reason.
|
2025-02-18 13:50:13 -08:00
|
|
|
pub async fn no_engine_commands(&self) -> bool {
|
2025-04-16 11:52:14 -07:00
|
|
|
self.is_mock()
|
2025-02-12 22:37:34 -05:00
|
|
|
}
|
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
pub async fn send_clear_scene(
|
2024-10-09 19:38:40 -04:00
|
|
|
&self,
|
2024-11-20 15:19:25 +13:00
|
|
|
exec_state: &mut ExecState,
|
2024-12-07 07:16:04 +13:00
|
|
|
source_range: crate::execution::SourceRange,
|
2024-12-05 19:51:06 -08:00
|
|
|
) -> Result<(), KclError> {
|
2024-11-20 15:19:25 +13:00
|
|
|
self.engine
|
2025-03-15 10:08:39 -07:00
|
|
|
.clear_scene(&mut exec_state.mod_local.id_generator, source_range)
|
2025-02-05 17:53:49 +13:00
|
|
|
.await
|
2024-06-18 14:38:25 -05:00
|
|
|
}
|
2024-12-10 18:50:22 -08:00
|
|
|
|
2025-03-29 11:43:42 -07:00
|
|
|
pub async fn bust_cache_and_reset_scene(&self) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
|
|
|
cache::bust_cache().await;
|
|
|
|
|
|
|
|
// Execute an empty program to clear and reset the scene.
|
|
|
|
// We specifically want to be returned the objects after the scene is reset.
|
|
|
|
// Like the default planes so it is easier to just execute an empty program
|
|
|
|
// after the cache is busted.
|
|
|
|
let outcome = self.run_with_caching(crate::Program::empty()).await?;
|
|
|
|
|
|
|
|
Ok(outcome)
|
|
|
|
}
|
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
async fn prepare_mem(&self, exec_state: &mut ExecState) -> Result<(), KclErrorWithOutputs> {
|
|
|
|
self.eval_prelude(exec_state, SourceRange::synthetic())
|
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
2025-03-05 12:03:32 +13:00
|
|
|
exec_state.mut_stack().push_new_root_env(true);
|
2025-02-25 16:10:06 +13:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
pub async fn run_mock(
|
|
|
|
&self,
|
|
|
|
program: crate::Program,
|
2025-02-12 10:22:56 +13:00
|
|
|
use_prev_memory: bool,
|
2025-02-05 17:53:49 +13:00
|
|
|
) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
|
|
|
assert!(self.is_mock());
|
2024-12-10 18:50:22 -08:00
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
let mut exec_state = ExecState::new(self);
|
2025-02-25 16:10:06 +13:00
|
|
|
if use_prev_memory {
|
|
|
|
match cache::read_old_memory().await {
|
2025-03-30 11:10:44 +13:00
|
|
|
Some(mem) => {
|
|
|
|
*exec_state.mut_stack() = mem.0;
|
|
|
|
exec_state.global.module_infos = mem.1;
|
|
|
|
}
|
2025-02-25 16:10:06 +13:00
|
|
|
None => self.prepare_mem(&mut exec_state).await?,
|
|
|
|
}
|
2025-02-12 10:22:56 +13:00
|
|
|
} else {
|
2025-02-25 16:10:06 +13:00
|
|
|
self.prepare_mem(&mut exec_state).await?
|
2025-02-12 10:22:56 +13:00
|
|
|
};
|
|
|
|
|
2025-03-08 04:04:57 +13:00
|
|
|
// Push a scope so that old variables can be overwritten (since we might be re-executing some
|
|
|
|
// part of the scene).
|
|
|
|
exec_state.mut_stack().push_new_env_for_scope();
|
2025-02-12 10:22:56 +13:00
|
|
|
|
2025-02-27 22:24:48 -08:00
|
|
|
let result = self.inner_run(&program, &mut exec_state, true).await?;
|
2025-02-12 10:22:56 +13:00
|
|
|
|
|
|
|
// Restore any temporary variables, then save any newly created variables back to
|
|
|
|
// memory in case another run wants to use them. Note this is just saved to the preserved
|
|
|
|
// memory, not to the exec_state which is not cached for mock execution.
|
2025-03-05 12:03:32 +13:00
|
|
|
|
|
|
|
let mut mem = exec_state.stack().clone();
|
2025-03-30 11:10:44 +13:00
|
|
|
let module_infos = exec_state.global.module_infos.clone();
|
2025-03-15 10:08:39 -07:00
|
|
|
let outcome = exec_state.to_mock_wasm_outcome(result.0).await;
|
2025-02-12 10:22:56 +13:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
mem.squash_env(result.0);
|
2025-03-30 11:10:44 +13:00
|
|
|
cache::write_old_memory((mem, module_infos)).await;
|
2025-02-12 10:22:56 +13:00
|
|
|
|
|
|
|
Ok(outcome)
|
2025-02-05 17:53:49 +13:00
|
|
|
}
|
2024-12-10 18:50:22 -08:00
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
pub async fn run_with_caching(&self, program: crate::Program) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
|
|
|
assert!(!self.is_mock());
|
Implement some basic cache program generation (#4840)
Implement some basic cache program generation
A bit ago, @jessfraz added the ability to control reexecution from the
executor. When we did this, it used the digest to determine if there
was a code change rather than a non-code change (whitespace, comment,
etc).
This allows the creation of a new program to be run without clearing the
scene. This, in conjunction with being able to delete Engine objects by
ID will allow us to do some clever stuff when incrementally executing
a program.
I'm still working on something a bit more advanced, but a good first
step to derisk some of the caching behavior here fully is to implement a
basic "changed program" stub.
This process the ast programs (old and new) if it doesn't exactly match.
This would have been a complete refresh before this commit.
1) Check all overlapping top-level statements of the body of the new and
old AST and ensure they all match.
- If this is true, this means that one of the two AST programs has more
elements then the other, and they all otherwise match (addition or
deletion of the end of the program). We continue to #2 in this
case.
- If this is false, we have a meaingful difference in a section of
overlapping code. This will result in a cache miss and rebuild
the scene. We short-cut here and the scene is rebuilt.
2) Check the lengths of the two bodies.
- If they're the same, we shouldn't have even been called. We will
short-cut with a noop cache return (no clear, no program).
- if the old ast is longer, we've removed instructions from the
program. We can't delete things now, so this will result in a cache
miss and rebuild the scene. We short-cut here and the scene is
rebuilt.
- If the new ast is longer, we have an insertion of code at the end.
3) construct a new program using only the new elements from the new
ast, and return a `CacheResult` that *does not clear the scene*.
This means nothing will be rebuilt, and only a new object will polp
onto the scene. This is the first case where we diverge with
existing behavior.
Signed-off-by: Paul R. Tagliamonte <paul@zoo.dev>
2024-12-19 16:18:35 -05:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
let (program, mut exec_state, preserve_mem) = if let Some(OldAstState {
|
2025-02-05 17:53:49 +13:00
|
|
|
ast: old_ast,
|
2025-02-25 16:10:06 +13:00
|
|
|
exec_state: mut old_state,
|
2025-02-05 17:53:49 +13:00
|
|
|
settings: old_settings,
|
2025-02-25 16:10:06 +13:00
|
|
|
result_env,
|
2025-02-12 10:22:56 +13:00
|
|
|
}) = cache::read_old_ast().await
|
2025-02-05 17:53:49 +13:00
|
|
|
{
|
|
|
|
let old = CacheInformation {
|
|
|
|
ast: &old_ast,
|
|
|
|
settings: &old_settings,
|
|
|
|
};
|
|
|
|
let new = CacheInformation {
|
|
|
|
ast: &program.ast,
|
|
|
|
settings: &self.settings,
|
|
|
|
};
|
2024-12-10 18:50:22 -08:00
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
// Get the program that actually changed from the old and new information.
|
|
|
|
let (clear_scene, program) = match cache::get_changed_program(old, new).await {
|
|
|
|
CacheResult::ReExecute {
|
|
|
|
clear_scene,
|
|
|
|
reapply_settings,
|
|
|
|
program: changed_program,
|
|
|
|
} => {
|
|
|
|
if reapply_settings
|
|
|
|
&& self
|
|
|
|
.engine
|
2025-03-29 21:23:11 -07:00
|
|
|
.reapply_settings(&self.settings, Default::default(), old_state.id_generator())
|
2025-02-05 17:53:49 +13:00
|
|
|
.await
|
|
|
|
.is_err()
|
|
|
|
{
|
2025-02-27 14:34:01 -08:00
|
|
|
(true, program)
|
2025-02-05 17:53:49 +13:00
|
|
|
} else {
|
2025-02-27 14:34:01 -08:00
|
|
|
(
|
|
|
|
clear_scene,
|
|
|
|
crate::Program {
|
|
|
|
ast: changed_program,
|
|
|
|
original_file_contents: program.original_file_contents,
|
|
|
|
},
|
|
|
|
)
|
2025-02-05 17:53:49 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
CacheResult::NoAction(true) => {
|
|
|
|
if self
|
|
|
|
.engine
|
2025-03-29 21:23:11 -07:00
|
|
|
.reapply_settings(&self.settings, Default::default(), old_state.id_generator())
|
2025-02-05 17:53:49 +13:00
|
|
|
.await
|
|
|
|
.is_ok()
|
|
|
|
{
|
2025-02-20 22:08:49 -08:00
|
|
|
// We need to update the old ast state with the new settings!!
|
|
|
|
cache::write_old_ast(OldAstState {
|
|
|
|
ast: old_ast,
|
|
|
|
exec_state: old_state.clone(),
|
|
|
|
settings: self.settings.clone(),
|
2025-02-25 16:10:06 +13:00
|
|
|
result_env,
|
2025-02-20 22:08:49 -08:00
|
|
|
})
|
|
|
|
.await;
|
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
let outcome = old_state.to_wasm_outcome(result_env).await;
|
2025-03-05 12:03:32 +13:00
|
|
|
return Ok(outcome);
|
2025-02-05 17:53:49 +13:00
|
|
|
}
|
2025-02-27 14:34:01 -08:00
|
|
|
(true, program)
|
2025-02-05 17:53:49 +13:00
|
|
|
}
|
2025-03-05 12:03:32 +13:00
|
|
|
CacheResult::NoAction(false) => {
|
2025-03-15 10:08:39 -07:00
|
|
|
let outcome = old_state.to_wasm_outcome(result_env).await;
|
2025-03-05 12:03:32 +13:00
|
|
|
return Ok(outcome);
|
|
|
|
}
|
2025-02-05 17:53:49 +13:00
|
|
|
};
|
2024-12-10 18:50:22 -08:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
let (exec_state, preserve_mem) = if clear_scene {
|
2025-02-05 17:53:49 +13:00
|
|
|
// Pop the execution state, since we are starting fresh.
|
|
|
|
let mut exec_state = old_state;
|
2025-03-15 10:08:39 -07:00
|
|
|
exec_state.reset(self);
|
2025-02-05 17:53:49 +13:00
|
|
|
|
|
|
|
// We don't do this in mock mode since there is no engine connection
|
|
|
|
// anyways and from the TS side we override memory and don't want to clear it.
|
|
|
|
self.send_clear_scene(&mut exec_state, Default::default())
|
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
Implement some basic cache program generation (#4840)
Implement some basic cache program generation
A bit ago, @jessfraz added the ability to control reexecution from the
executor. When we did this, it used the digest to determine if there
was a code change rather than a non-code change (whitespace, comment,
etc).
This allows the creation of a new program to be run without clearing the
scene. This, in conjunction with being able to delete Engine objects by
ID will allow us to do some clever stuff when incrementally executing
a program.
I'm still working on something a bit more advanced, but a good first
step to derisk some of the caching behavior here fully is to implement a
basic "changed program" stub.
This process the ast programs (old and new) if it doesn't exactly match.
This would have been a complete refresh before this commit.
1) Check all overlapping top-level statements of the body of the new and
old AST and ensure they all match.
- If this is true, this means that one of the two AST programs has more
elements then the other, and they all otherwise match (addition or
deletion of the end of the program). We continue to #2 in this
case.
- If this is false, we have a meaingful difference in a section of
overlapping code. This will result in a cache miss and rebuild
the scene. We short-cut here and the scene is rebuilt.
2) Check the lengths of the two bodies.
- If they're the same, we shouldn't have even been called. We will
short-cut with a noop cache return (no clear, no program).
- if the old ast is longer, we've removed instructions from the
program. We can't delete things now, so this will result in a cache
miss and rebuild the scene. We short-cut here and the scene is
rebuilt.
- If the new ast is longer, we have an insertion of code at the end.
3) construct a new program using only the new elements from the new
ast, and return a `CacheResult` that *does not clear the scene*.
This means nothing will be rebuilt, and only a new object will polp
onto the scene. This is the first case where we diverge with
existing behavior.
Signed-off-by: Paul R. Tagliamonte <paul@zoo.dev>
2024-12-19 16:18:35 -05:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
(exec_state, false)
|
2025-02-05 17:53:49 +13:00
|
|
|
} else {
|
2025-03-05 12:03:32 +13:00
|
|
|
old_state.mut_stack().restore_env(result_env);
|
2025-02-28 15:00:10 -08:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
(old_state, true)
|
Implement some basic cache program generation (#4840)
Implement some basic cache program generation
A bit ago, @jessfraz added the ability to control reexecution from the
executor. When we did this, it used the digest to determine if there
was a code change rather than a non-code change (whitespace, comment,
etc).
This allows the creation of a new program to be run without clearing the
scene. This, in conjunction with being able to delete Engine objects by
ID will allow us to do some clever stuff when incrementally executing
a program.
I'm still working on something a bit more advanced, but a good first
step to derisk some of the caching behavior here fully is to implement a
basic "changed program" stub.
This process the ast programs (old and new) if it doesn't exactly match.
This would have been a complete refresh before this commit.
1) Check all overlapping top-level statements of the body of the new and
old AST and ensure they all match.
- If this is true, this means that one of the two AST programs has more
elements then the other, and they all otherwise match (addition or
deletion of the end of the program). We continue to #2 in this
case.
- If this is false, we have a meaingful difference in a section of
overlapping code. This will result in a cache miss and rebuild
the scene. We short-cut here and the scene is rebuilt.
2) Check the lengths of the two bodies.
- If they're the same, we shouldn't have even been called. We will
short-cut with a noop cache return (no clear, no program).
- if the old ast is longer, we've removed instructions from the
program. We can't delete things now, so this will result in a cache
miss and rebuild the scene. We short-cut here and the scene is
rebuilt.
- If the new ast is longer, we have an insertion of code at the end.
3) construct a new program using only the new elements from the new
ast, and return a `CacheResult` that *does not clear the scene*.
This means nothing will be rebuilt, and only a new object will polp
onto the scene. This is the first case where we diverge with
existing behavior.
Signed-off-by: Paul R. Tagliamonte <paul@zoo.dev>
2024-12-19 16:18:35 -05:00
|
|
|
};
|
2024-12-10 18:50:22 -08:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
(program, exec_state, preserve_mem)
|
2025-02-05 17:53:49 +13:00
|
|
|
} else {
|
2025-03-15 10:08:39 -07:00
|
|
|
let mut exec_state = ExecState::new(self);
|
2025-02-05 17:53:49 +13:00
|
|
|
self.send_clear_scene(&mut exec_state, Default::default())
|
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
2025-02-27 14:34:01 -08:00
|
|
|
(program, exec_state, false)
|
2025-02-05 17:53:49 +13:00
|
|
|
};
|
|
|
|
|
2025-04-16 11:52:14 -07:00
|
|
|
let result = self.run_concurrent(&program, &mut exec_state, preserve_mem).await;
|
2025-02-05 17:53:49 +13:00
|
|
|
|
|
|
|
if result.is_err() {
|
|
|
|
cache::bust_cache().await;
|
Implement some basic cache program generation (#4840)
Implement some basic cache program generation
A bit ago, @jessfraz added the ability to control reexecution from the
executor. When we did this, it used the digest to determine if there
was a code change rather than a non-code change (whitespace, comment,
etc).
This allows the creation of a new program to be run without clearing the
scene. This, in conjunction with being able to delete Engine objects by
ID will allow us to do some clever stuff when incrementally executing
a program.
I'm still working on something a bit more advanced, but a good first
step to derisk some of the caching behavior here fully is to implement a
basic "changed program" stub.
This process the ast programs (old and new) if it doesn't exactly match.
This would have been a complete refresh before this commit.
1) Check all overlapping top-level statements of the body of the new and
old AST and ensure they all match.
- If this is true, this means that one of the two AST programs has more
elements then the other, and they all otherwise match (addition or
deletion of the end of the program). We continue to #2 in this
case.
- If this is false, we have a meaingful difference in a section of
overlapping code. This will result in a cache miss and rebuild
the scene. We short-cut here and the scene is rebuilt.
2) Check the lengths of the two bodies.
- If they're the same, we shouldn't have even been called. We will
short-cut with a noop cache return (no clear, no program).
- if the old ast is longer, we've removed instructions from the
program. We can't delete things now, so this will result in a cache
miss and rebuild the scene. We short-cut here and the scene is
rebuilt.
- If the new ast is longer, we have an insertion of code at the end.
3) construct a new program using only the new elements from the new
ast, and return a `CacheResult` that *does not clear the scene*.
This means nothing will be rebuilt, and only a new object will polp
onto the scene. This is the first case where we diverge with
existing behavior.
Signed-off-by: Paul R. Tagliamonte <paul@zoo.dev>
2024-12-19 16:18:35 -05:00
|
|
|
}
|
2025-02-05 17:53:49 +13:00
|
|
|
|
|
|
|
// Throw the error.
|
2025-02-25 16:10:06 +13:00
|
|
|
let result = result?;
|
2025-02-05 17:53:49 +13:00
|
|
|
|
|
|
|
// Save this as the last successful execution to the cache.
|
2025-02-12 10:22:56 +13:00
|
|
|
cache::write_old_ast(OldAstState {
|
2025-02-27 14:34:01 -08:00
|
|
|
ast: program.ast,
|
2025-02-05 17:53:49 +13:00
|
|
|
exec_state: exec_state.clone(),
|
|
|
|
settings: self.settings.clone(),
|
2025-02-25 16:10:06 +13:00
|
|
|
result_env: result.0,
|
2025-02-05 17:53:49 +13:00
|
|
|
})
|
|
|
|
.await;
|
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
let outcome = exec_state.to_wasm_outcome(result.0).await;
|
2025-03-05 12:03:32 +13:00
|
|
|
Ok(outcome)
|
2024-12-10 18:50:22 -08:00
|
|
|
}
|
2024-06-18 14:38:25 -05:00
|
|
|
|
2024-04-12 21:32:57 -07:00
|
|
|
/// Perform the execution of a program.
|
2025-01-08 20:02:30 -05:00
|
|
|
///
|
|
|
|
/// You can optionally pass in some initialization memory for partial
|
|
|
|
/// execution.
|
2025-02-21 09:30:44 +13:00
|
|
|
///
|
|
|
|
/// To access non-fatal errors and warnings, extract them from the `ExecState`.
|
2025-02-05 17:53:49 +13:00
|
|
|
pub async fn run(
|
|
|
|
&self,
|
|
|
|
program: &crate::Program,
|
|
|
|
exec_state: &mut ExecState,
|
2025-02-25 16:10:06 +13:00
|
|
|
) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
|
2025-04-16 11:52:14 -07:00
|
|
|
self.run_concurrent(program, exec_state, false).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform the execution of a program.
|
|
|
|
///
|
|
|
|
/// You can optionally pass in some initialization memory for partial
|
|
|
|
/// execution.
|
|
|
|
///
|
|
|
|
/// To access non-fatal errors and warnings, extract them from the `ExecState`.
|
|
|
|
pub async fn run_single_threaded(
|
|
|
|
&self,
|
|
|
|
program: &crate::Program,
|
|
|
|
exec_state: &mut ExecState,
|
|
|
|
) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
|
|
|
|
exec_state.add_root_module_contents(program);
|
|
|
|
self.eval_prelude(exec_state, SourceRange::synthetic())
|
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
|
|
|
|
2025-02-27 22:24:48 -08:00
|
|
|
self.inner_run(program, exec_state, false).await
|
2025-01-08 20:02:30 -05:00
|
|
|
}
|
|
|
|
|
2025-04-16 11:52:14 -07:00
|
|
|
/// Perform the execution of a program using an (experimental!) concurrent
|
|
|
|
/// execution model. This has the same signature as [Self::run].
|
|
|
|
///
|
|
|
|
/// For now -- do not use this unless you're willing to accept some
|
|
|
|
/// breakage.
|
|
|
|
///
|
|
|
|
/// You can optionally pass in some initialization memory for partial
|
|
|
|
/// execution.
|
|
|
|
///
|
|
|
|
/// To access non-fatal errors and warnings, extract them from the `ExecState`.
|
|
|
|
pub async fn run_concurrent(
|
|
|
|
&self,
|
|
|
|
program: &crate::Program,
|
|
|
|
exec_state: &mut ExecState,
|
|
|
|
preserve_mem: bool,
|
|
|
|
) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
|
|
|
|
exec_state.add_root_module_contents(program);
|
|
|
|
self.eval_prelude(exec_state, SourceRange::synthetic())
|
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
|
|
|
|
|
|
|
let mut universe = std::collections::HashMap::new();
|
|
|
|
|
|
|
|
let default_planes = self.engine.get_default_planes().read().await.clone();
|
|
|
|
crate::walk::import_universe(self, &program.ast, &mut universe, exec_state)
|
|
|
|
.await
|
|
|
|
.map_err(|err| {
|
|
|
|
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
|
|
|
|
.global
|
|
|
|
.path_to_source_id
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| ((*v), k.clone()))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
KclErrorWithOutputs::new(
|
|
|
|
err,
|
|
|
|
exec_state.global.operations.clone(),
|
|
|
|
exec_state.global.artifact_commands.clone(),
|
|
|
|
exec_state.global.artifact_graph.clone(),
|
|
|
|
module_id_to_module_path,
|
|
|
|
exec_state.global.id_to_source.clone(),
|
|
|
|
default_planes.clone(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
for modules in crate::walk::import_graph(&universe, self)
|
|
|
|
.map_err(|err| {
|
|
|
|
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
|
|
|
|
.global
|
|
|
|
.path_to_source_id
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| ((*v), k.clone()))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
KclErrorWithOutputs::new(
|
|
|
|
err,
|
|
|
|
exec_state.global.operations.clone(),
|
|
|
|
exec_state.global.artifact_commands.clone(),
|
|
|
|
exec_state.global.artifact_graph.clone(),
|
|
|
|
module_id_to_module_path,
|
|
|
|
exec_state.global.id_to_source.clone(),
|
|
|
|
default_planes.clone(),
|
|
|
|
)
|
|
|
|
})?
|
|
|
|
.into_iter()
|
|
|
|
{
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
let mut set = tokio::task::JoinSet::new();
|
|
|
|
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
let (results_tx, mut results_rx): (
|
|
|
|
tokio::sync::mpsc::Sender<(
|
|
|
|
ModuleId,
|
|
|
|
ModulePath,
|
|
|
|
Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError>,
|
|
|
|
)>,
|
|
|
|
tokio::sync::mpsc::Receiver<_>,
|
|
|
|
) = tokio::sync::mpsc::channel(1);
|
|
|
|
|
|
|
|
for module in modules {
|
|
|
|
let Some((import_stmt, module_id, module_path, program)) = universe.get(&module) else {
|
|
|
|
return Err(KclErrorWithOutputs::no_outputs(KclError::Internal(KclErrorDetails {
|
|
|
|
message: format!("Module {module} not found in universe"),
|
|
|
|
source_ranges: Default::default(),
|
|
|
|
})));
|
|
|
|
};
|
|
|
|
let module_id = *module_id;
|
|
|
|
let module_path = module_path.clone();
|
|
|
|
let program = program.clone();
|
|
|
|
let exec_state = exec_state.clone();
|
|
|
|
let exec_ctxt = self.clone();
|
|
|
|
let results_tx = results_tx.clone();
|
|
|
|
let source_range = SourceRange::from(import_stmt);
|
|
|
|
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
{
|
|
|
|
wasm_bindgen_futures::spawn_local(async move {
|
|
|
|
//set.spawn(async move {
|
|
|
|
let mut exec_state = exec_state;
|
|
|
|
let exec_ctxt = exec_ctxt;
|
|
|
|
|
|
|
|
let result = exec_ctxt
|
|
|
|
.exec_module_from_ast(
|
|
|
|
&program,
|
|
|
|
module_id,
|
|
|
|
&module_path,
|
|
|
|
&mut exec_state,
|
|
|
|
source_range,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
results_tx
|
|
|
|
.send((module_id, module_path, result))
|
|
|
|
.await
|
|
|
|
.unwrap_or_default();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
{
|
|
|
|
set.spawn(async move {
|
|
|
|
let mut exec_state = exec_state;
|
|
|
|
let exec_ctxt = exec_ctxt;
|
|
|
|
|
|
|
|
let result = exec_ctxt
|
|
|
|
.exec_module_from_ast(
|
|
|
|
&program,
|
|
|
|
module_id,
|
|
|
|
&module_path,
|
|
|
|
&mut exec_state,
|
|
|
|
source_range,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
results_tx
|
|
|
|
.send((module_id, module_path, result))
|
|
|
|
.await
|
|
|
|
.unwrap_or_default();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
drop(results_tx);
|
|
|
|
|
|
|
|
while let Some((module_id, _, result)) = results_rx.recv().await {
|
|
|
|
match result {
|
|
|
|
Ok((val, session_data, variables)) => {
|
|
|
|
let mut repr = exec_state.global.module_infos[&module_id].take_repr();
|
|
|
|
|
|
|
|
let ModuleRepr::Kcl(_, cache) = &mut repr else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
*cache = Some((val, session_data, variables));
|
|
|
|
|
|
|
|
exec_state.global.module_infos[&module_id].restore_repr(repr);
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
|
|
|
|
.global
|
|
|
|
.path_to_source_id
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| ((*v), k.clone()))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
return Err(KclErrorWithOutputs::new(
|
|
|
|
e,
|
|
|
|
exec_state.global.operations.clone(),
|
|
|
|
exec_state.global.artifact_commands.clone(),
|
|
|
|
exec_state.global.artifact_graph.clone(),
|
|
|
|
module_id_to_module_path,
|
|
|
|
exec_state.global.id_to_source.clone(),
|
|
|
|
default_planes,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.inner_run(program, exec_state, preserve_mem).await
|
|
|
|
}
|
|
|
|
|
2025-01-08 20:02:30 -05:00
|
|
|
/// Perform the execution of a program. Accept all possible parameters and
|
2025-02-05 17:53:49 +13:00
|
|
|
/// output everything.
|
|
|
|
async fn inner_run(
|
|
|
|
&self,
|
2025-02-27 22:24:48 -08:00
|
|
|
program: &crate::Program,
|
2025-02-05 17:53:49 +13:00
|
|
|
exec_state: &mut ExecState,
|
2025-02-25 16:10:06 +13:00
|
|
|
preserve_mem: bool,
|
|
|
|
) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
|
2025-02-05 17:53:49 +13:00
|
|
|
let _stats = crate::log::LogPerfStats::new("Interpretation");
|
2024-12-05 19:51:06 -08:00
|
|
|
|
2024-12-10 18:50:22 -08:00
|
|
|
// Re-apply the settings, in case the cache was busted.
|
2025-01-08 20:02:30 -05:00
|
|
|
self.engine
|
2025-03-29 21:23:11 -07:00
|
|
|
.reapply_settings(&self.settings, Default::default(), exec_state.id_generator())
|
2025-01-08 20:02:30 -05:00
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
2024-10-09 19:38:40 -04:00
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
let default_planes = self.engine.get_default_planes().read().await.clone();
|
2025-03-20 15:59:16 +13:00
|
|
|
let result = self
|
2025-02-27 22:24:48 -08:00
|
|
|
.execute_and_build_graph(&program.ast, exec_state, preserve_mem)
|
2025-03-20 15:59:16 +13:00
|
|
|
.await;
|
2025-01-17 14:34:36 -05:00
|
|
|
|
2025-03-05 12:03:32 +13:00
|
|
|
crate::log::log(format!(
|
|
|
|
"Post interpretation KCL memory stats: {:#?}",
|
|
|
|
exec_state.stack().memory.stats
|
|
|
|
));
|
2025-03-18 16:19:24 +13:00
|
|
|
crate::log::log(format!("Engine stats: {:?}", self.engine.stats()));
|
2025-03-05 12:03:32 +13:00
|
|
|
|
2025-03-20 15:59:16 +13:00
|
|
|
let env_ref = result.map_err(|e| {
|
|
|
|
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
|
|
|
|
.global
|
|
|
|
.path_to_source_id
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| ((*v), k.clone()))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
KclErrorWithOutputs::new(
|
|
|
|
e,
|
|
|
|
exec_state.global.operations.clone(),
|
|
|
|
exec_state.global.artifact_commands.clone(),
|
|
|
|
exec_state.global.artifact_graph.clone(),
|
|
|
|
module_id_to_module_path,
|
|
|
|
exec_state.global.id_to_source.clone(),
|
2025-04-17 17:22:19 -07:00
|
|
|
default_planes.clone(),
|
2025-03-20 15:59:16 +13:00
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
2025-02-12 10:22:56 +13:00
|
|
|
if !self.is_mock() {
|
2025-03-05 12:03:32 +13:00
|
|
|
let mut mem = exec_state.stack().deep_clone();
|
2025-02-25 16:10:06 +13:00
|
|
|
mem.restore_env(env_ref);
|
2025-03-30 11:10:44 +13:00
|
|
|
cache::write_old_memory((mem, exec_state.global.module_infos.clone())).await;
|
2025-02-12 10:22:56 +13:00
|
|
|
}
|
2025-02-18 13:50:13 -08:00
|
|
|
let session_data = self.engine.get_session_data().await;
|
2025-04-17 17:22:19 -07:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
Ok((env_ref, session_data))
|
2025-01-17 14:34:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Execute an AST's program and build auxiliary outputs like the artifact
|
|
|
|
/// graph.
|
2025-02-05 19:51:54 -05:00
|
|
|
async fn execute_and_build_graph(
|
2025-01-17 14:34:36 -05:00
|
|
|
&self,
|
2025-02-05 19:51:54 -05:00
|
|
|
program: NodeRef<'_, crate::parsing::ast::types::Program>,
|
2025-01-17 14:34:36 -05:00
|
|
|
exec_state: &mut ExecState,
|
2025-02-25 16:10:06 +13:00
|
|
|
preserve_mem: bool,
|
|
|
|
) -> Result<EnvironmentRef, KclError> {
|
2025-01-17 14:34:36 -05:00
|
|
|
// Don't early return! We need to build other outputs regardless of
|
|
|
|
// whether execution failed.
|
2025-02-25 16:10:06 +13:00
|
|
|
|
|
|
|
self.eval_prelude(exec_state, SourceRange::from(program).start_as_range())
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let exec_result = self
|
2025-02-27 15:46:41 +13:00
|
|
|
.exec_module_body(
|
|
|
|
program,
|
|
|
|
exec_state,
|
|
|
|
preserve_mem,
|
2025-03-15 10:08:39 -07:00
|
|
|
ModuleId::default(),
|
2025-02-27 15:46:41 +13:00
|
|
|
&ModulePath::Main,
|
|
|
|
)
|
2025-02-25 16:10:06 +13:00
|
|
|
.await;
|
|
|
|
|
2025-04-17 17:22:19 -07:00
|
|
|
// Ensure all the async commands completed.
|
|
|
|
self.engine.ensure_async_commands_completed().await?;
|
|
|
|
|
2025-03-20 15:59:16 +13:00
|
|
|
// If we errored out and early-returned, there might be commands which haven't been executed
|
|
|
|
// and should be dropped.
|
|
|
|
self.engine.clear_queues().await;
|
|
|
|
|
2025-01-17 14:34:36 -05:00
|
|
|
// Move the artifact commands and responses to simplify cache management
|
|
|
|
// and error creation.
|
2025-01-10 22:33:05 -05:00
|
|
|
exec_state
|
|
|
|
.global
|
|
|
|
.artifact_commands
|
2025-02-18 13:50:13 -08:00
|
|
|
.extend(self.engine.take_artifact_commands().await);
|
|
|
|
exec_state
|
|
|
|
.global
|
|
|
|
.artifact_responses
|
|
|
|
.extend(self.engine.take_responses().await);
|
2025-01-17 14:34:36 -05:00
|
|
|
// Build the artifact graph.
|
|
|
|
match build_artifact_graph(
|
|
|
|
&exec_state.global.artifact_commands,
|
|
|
|
&exec_state.global.artifact_responses,
|
|
|
|
program,
|
|
|
|
&exec_state.global.artifacts,
|
|
|
|
) {
|
|
|
|
Ok(artifact_graph) => {
|
|
|
|
exec_state.global.artifact_graph = artifact_graph;
|
2025-02-27 15:46:41 +13:00
|
|
|
exec_result.map(|(_, env_ref, _)| env_ref)
|
2025-01-17 14:34:36 -05:00
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
// Prefer the exec error.
|
|
|
|
exec_result.and(Err(err))
|
|
|
|
}
|
|
|
|
}
|
2024-04-12 21:32:57 -07:00
|
|
|
}
|
2024-02-20 17:55:06 -08:00
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
/// 'Import' std::prelude as the outermost scope.
|
2025-03-05 12:03:32 +13:00
|
|
|
///
|
|
|
|
/// SAFETY: the current thread must have sole access to the memory referenced in exec_state.
|
2025-02-25 16:10:06 +13:00
|
|
|
async fn eval_prelude(&self, exec_state: &mut ExecState, source_range: SourceRange) -> Result<(), KclError> {
|
2025-03-05 12:03:32 +13:00
|
|
|
if exec_state.stack().memory.requires_std() {
|
2025-02-25 16:10:06 +13:00
|
|
|
let id = self
|
|
|
|
.open_module(
|
|
|
|
&ImportPath::Std {
|
|
|
|
path: vec!["std".to_owned(), "prelude".to_owned()],
|
|
|
|
},
|
|
|
|
&[],
|
|
|
|
exec_state,
|
|
|
|
source_range,
|
|
|
|
)
|
|
|
|
.await?;
|
2025-04-16 11:52:14 -07:00
|
|
|
let (module_memory, _) = self.exec_module_for_items(id, exec_state, source_range).await?;
|
2025-02-25 16:10:06 +13:00
|
|
|
|
2025-03-05 12:03:32 +13:00
|
|
|
exec_state.mut_stack().memory.set_std(module_memory);
|
2025-02-25 16:10:06 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-12-10 18:50:22 -08:00
|
|
|
/// Get a snapshot of the current scene.
|
|
|
|
pub async fn prepare_snapshot(&self) -> std::result::Result<TakeSnapshot, ExecError> {
|
2024-06-06 16:01:41 -05:00
|
|
|
// Zoom to fit.
|
|
|
|
self.engine
|
|
|
|
.send_modeling_cmd(
|
|
|
|
uuid::Uuid::new_v4(),
|
2024-12-07 07:16:04 +13:00
|
|
|
crate::execution::SourceRange::default(),
|
2025-01-08 20:02:30 -05:00
|
|
|
&ModelingCmd::from(mcmd::ZoomToFit {
|
2024-06-06 16:01:41 -05:00
|
|
|
object_ids: Default::default(),
|
2024-09-18 17:04:04 -05:00
|
|
|
animated: false,
|
2024-06-06 16:01:41 -05:00
|
|
|
padding: 0.1,
|
2024-09-18 17:04:04 -05:00
|
|
|
}),
|
2024-06-06 16:01:41 -05:00
|
|
|
)
|
2025-01-08 20:02:30 -05:00
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
2024-06-06 16:01:41 -05:00
|
|
|
|
|
|
|
// Send a snapshot request to the engine.
|
|
|
|
let resp = self
|
|
|
|
.engine
|
|
|
|
.send_modeling_cmd(
|
|
|
|
uuid::Uuid::new_v4(),
|
2024-12-07 07:16:04 +13:00
|
|
|
crate::execution::SourceRange::default(),
|
2025-01-08 20:02:30 -05:00
|
|
|
&ModelingCmd::from(mcmd::TakeSnapshot {
|
2024-09-18 17:04:04 -05:00
|
|
|
format: ImageFormat::Png,
|
|
|
|
}),
|
2024-06-06 16:01:41 -05:00
|
|
|
)
|
2025-01-08 20:02:30 -05:00
|
|
|
.await
|
|
|
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
2024-06-06 16:01:41 -05:00
|
|
|
|
2024-09-18 17:04:04 -05:00
|
|
|
let OkWebSocketResponseData::Modeling {
|
|
|
|
modeling_response: OkModelingCmdResponse::TakeSnapshot(contents),
|
2024-06-06 16:01:41 -05:00
|
|
|
} = resp
|
|
|
|
else {
|
2024-11-25 14:06:23 -06:00
|
|
|
return Err(ExecError::BadPng(format!(
|
|
|
|
"Instead of a TakeSnapshot response, the engine returned {resp:?}"
|
|
|
|
)));
|
2024-06-06 16:01:41 -05:00
|
|
|
};
|
2024-11-20 15:19:25 +13:00
|
|
|
Ok(contents)
|
2024-06-06 16:01:41 -05:00
|
|
|
}
|
2024-12-10 18:50:22 -08:00
|
|
|
|
2025-03-07 18:45:33 -08:00
|
|
|
/// Export the current scene as a CAD file.
|
|
|
|
pub async fn export(
|
|
|
|
&self,
|
|
|
|
format: kittycad_modeling_cmds::format::OutputFormat3d,
|
|
|
|
) -> Result<Vec<kittycad_modeling_cmds::websocket::RawFile>, KclError> {
|
|
|
|
let resp = self
|
|
|
|
.engine
|
|
|
|
.send_modeling_cmd(
|
|
|
|
uuid::Uuid::new_v4(),
|
|
|
|
crate::SourceRange::default(),
|
|
|
|
&kittycad_modeling_cmds::ModelingCmd::Export(kittycad_modeling_cmds::Export {
|
|
|
|
entity_ids: vec![],
|
|
|
|
format,
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let kittycad_modeling_cmds::websocket::OkWebSocketResponseData::Export { files } = resp else {
|
|
|
|
return Err(KclError::Internal(crate::errors::KclErrorDetails {
|
|
|
|
message: format!("Expected Export response, got {resp:?}",),
|
|
|
|
source_ranges: vec![SourceRange::default()],
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(files)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Export the current scene as a STEP file.
|
|
|
|
pub async fn export_step(
|
|
|
|
&self,
|
|
|
|
deterministic_time: bool,
|
|
|
|
) -> Result<Vec<kittycad_modeling_cmds::websocket::RawFile>, KclError> {
|
2025-03-13 18:01:47 -07:00
|
|
|
let files = self
|
2025-03-07 18:45:33 -08:00
|
|
|
.export(kittycad_modeling_cmds::format::OutputFormat3d::Step(
|
|
|
|
kittycad_modeling_cmds::format::step::export::Options {
|
|
|
|
coords: *kittycad_modeling_cmds::coord::KITTYCAD,
|
2025-03-13 18:01:47 -07:00
|
|
|
created: if deterministic_time {
|
|
|
|
Some("2021-01-01T00:00:00Z".parse().map_err(|e| {
|
|
|
|
KclError::Internal(crate::errors::KclErrorDetails {
|
|
|
|
message: format!("Failed to parse date: {}", e),
|
|
|
|
source_ranges: vec![SourceRange::default()],
|
|
|
|
})
|
|
|
|
})?)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
2025-03-07 18:45:33 -08:00
|
|
|
},
|
|
|
|
))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(files)
|
|
|
|
}
|
|
|
|
|
2025-01-10 20:05:27 -05:00
|
|
|
pub async fn close(&self) {
|
|
|
|
self.engine.close().await;
|
|
|
|
}
|
2023-08-24 15:34:51 -07:00
|
|
|
}
|
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
#[cfg(test)]
|
2025-03-08 08:00:55 +13:00
|
|
|
pub(crate) async fn parse_execute(code: &str) -> Result<ExecTestResults, KclError> {
|
2025-04-16 11:52:14 -07:00
|
|
|
parse_execute_with_project_dir(code, None).await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub(crate) async fn parse_execute_with_project_dir(
|
|
|
|
code: &str,
|
|
|
|
project_directory: Option<std::path::PathBuf>,
|
|
|
|
) -> Result<ExecTestResults, KclError> {
|
2025-02-05 17:53:49 +13:00
|
|
|
let program = crate::Program::parse_no_errs(code)?;
|
|
|
|
|
2025-03-08 08:00:55 +13:00
|
|
|
let exec_ctxt = ExecutorContext {
|
2025-02-28 17:40:01 -08:00
|
|
|
engine: Arc::new(Box::new(
|
|
|
|
crate::engine::conn_mock::EngineConnection::new().await.map_err(|err| {
|
|
|
|
KclError::Internal(crate::errors::KclErrorDetails {
|
|
|
|
message: format!("Failed to create mock engine connection: {}", err),
|
|
|
|
source_ranges: vec![SourceRange::default()],
|
|
|
|
})
|
|
|
|
})?,
|
|
|
|
)),
|
2025-02-05 17:53:49 +13:00
|
|
|
fs: Arc::new(crate::fs::FileManager::new()),
|
|
|
|
stdlib: Arc::new(crate::std::StdLib::new()),
|
2025-04-16 11:52:14 -07:00
|
|
|
settings: ExecutorSettings {
|
|
|
|
project_directory,
|
|
|
|
..Default::default()
|
|
|
|
},
|
2025-02-05 17:53:49 +13:00
|
|
|
context_type: ContextType::Mock,
|
2024-09-16 15:10:33 -04:00
|
|
|
};
|
2025-03-15 10:08:39 -07:00
|
|
|
let mut exec_state = ExecState::new(&exec_ctxt);
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = exec_ctxt.run(&program, &mut exec_state).await?;
|
|
|
|
|
|
|
|
Ok(ExecTestResults {
|
|
|
|
program,
|
|
|
|
mem_env: result.0,
|
|
|
|
exec_ctxt,
|
|
|
|
exec_state,
|
|
|
|
})
|
|
|
|
}
|
2024-09-16 15:10:33 -04:00
|
|
|
|
2025-03-08 08:00:55 +13:00
|
|
|
#[cfg(test)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct ExecTestResults {
|
|
|
|
program: crate::Program,
|
|
|
|
mem_env: EnvironmentRef,
|
|
|
|
exec_ctxt: ExecutorContext,
|
|
|
|
exec_state: ExecState,
|
2024-05-23 14:50:22 -05:00
|
|
|
}
|
|
|
|
|
2023-08-24 15:34:51 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
|
2023-08-29 14:12:48 -07:00
|
|
|
use super::*;
|
2025-03-31 10:56:03 -04:00
|
|
|
use crate::{errors::KclErrorDetails, execution::memory::Stack, ModuleId};
|
2023-08-24 15:34:51 -07:00
|
|
|
|
2024-08-14 02:38:37 -04:00
|
|
|
/// Convenience function to get a JSON value from memory and unwrap.
|
2024-12-17 09:38:32 +13:00
|
|
|
#[track_caller]
|
2025-03-05 12:03:32 +13:00
|
|
|
fn mem_get_json(memory: &Stack, env: EnvironmentRef, name: &str) -> KclValue {
|
2025-03-17 12:28:51 +13:00
|
|
|
memory.memory.get_from_unchecked(name, env).unwrap().to_owned()
|
2023-08-24 15:34:51 -07:00
|
|
|
}
|
2025-02-21 09:30:44 +13:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_warn() {
|
|
|
|
let text = "@blah";
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(text).await.unwrap();
|
|
|
|
let errs = result.exec_state.errors();
|
2025-02-21 09:30:44 +13:00
|
|
|
assert_eq!(errs.len(), 1);
|
|
|
|
assert_eq!(errs[0].severity, crate::errors::Severity::Warning);
|
2025-02-22 20:16:29 +13:00
|
|
|
assert!(
|
|
|
|
errs[0].message.contains("Unknown annotation"),
|
|
|
|
"unexpected warning message: {}",
|
|
|
|
errs[0].message
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_warn_on_deprecated() {
|
|
|
|
let text = "p = pi()";
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(text).await.unwrap();
|
|
|
|
let errs = result.exec_state.errors();
|
2025-02-22 20:16:29 +13:00
|
|
|
assert_eq!(errs.len(), 1);
|
|
|
|
assert_eq!(errs[0].severity, crate::errors::Severity::Warning);
|
|
|
|
assert!(
|
|
|
|
errs[0].message.contains("`pi` is deprecated"),
|
|
|
|
"unexpected warning message: {}",
|
|
|
|
errs[0].message
|
|
|
|
);
|
2025-02-21 09:30:44 +13:00
|
|
|
}
|
2023-08-24 15:34:51 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_fn_definitions() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let ast = r#"fn def = (x) => {
|
2023-08-24 15:34:51 -07:00
|
|
|
return x
|
|
|
|
}
|
2023-09-13 11:42:09 -07:00
|
|
|
fn ghi = (x) => {
|
2023-08-24 15:34:51 -07:00
|
|
|
return x
|
|
|
|
}
|
2023-09-13 11:42:09 -07:00
|
|
|
fn jkl = (x) => {
|
2023-08-24 15:34:51 -07:00
|
|
|
return x
|
|
|
|
}
|
2023-09-13 11:42:09 -07:00
|
|
|
fn hmm = (x) => {
|
2023-08-24 15:34:51 -07:00
|
|
|
return x
|
|
|
|
}
|
|
|
|
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
yo = 5 + 6
|
2023-08-24 15:34:51 -07:00
|
|
|
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
abc = 3
|
|
|
|
identifierGuy = 5
|
2025-02-27 15:58:58 +13:00
|
|
|
part001 = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([-1.2, 4.83], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [2.8, 0])
|
KCL: Angled line should use keyword args (#5803)
We continue migrating KCL stdlib functions to use keyword arguments. Next up is the `angledLine` family of functions (except `angledLineThatIntersects, which will be a quick follow-up).
Before vs. after:
`angledLine({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, length = 3, tag = $edge)`
`angledLineOfXLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthX = 3, tag = $edge)`
`angledLineOfYLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthY = 3, tag = $edge)`
`angledLineToX({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteX = 3, tag = $edge)`
`angledLineToY({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteY = 3, tag = $edge)`
2025-04-09 14:55:15 -05:00
|
|
|
|> angledLine(angle = 100 + 100, length = 3.01)
|
|
|
|
|> angledLine(angle = abc, length = 3.02)
|
|
|
|
|> angledLine(angle = def(yo), length = 3.03)
|
|
|
|
|> angledLine(angle = ghi(2), length = 3.04)
|
|
|
|
|> angledLine(angle = jkl(yo) + 2, length = 3.05)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> close()
|
|
|
|
yo2 = hmm([identifierGuy + 5])"#;
|
2023-08-24 15:34:51 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_pipe_substitutions_unary() {
|
|
|
|
let ast = r#"const myVar = 3
|
2025-02-27 15:58:58 +13:00
|
|
|
const part001 = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0, 0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [3, 4], tag = $seg01)
|
|
|
|
|> line(end = [
|
2024-07-27 22:56:46 -07:00
|
|
|
min(segLen(seg01), myVar),
|
|
|
|
-legLen(segLen(seg01), myVar)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
])
|
2024-03-01 17:16:18 -08:00
|
|
|
"#;
|
2023-08-24 15:34:51 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_pipe_substitutions() {
|
|
|
|
let ast = r#"const myVar = 3
|
2025-02-27 15:58:58 +13:00
|
|
|
const part001 = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0, 0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [3, 4], tag = $seg01)
|
|
|
|
|> line(end = [
|
2024-07-27 22:56:46 -07:00
|
|
|
min(segLen(seg01), myVar),
|
|
|
|
legLen(segLen(seg01), myVar)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
])
|
2024-03-01 17:16:18 -08:00
|
|
|
"#;
|
2023-09-05 16:02:27 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_inline_comment() {
|
|
|
|
let ast = r#"const baseThick = 1
|
|
|
|
const armAngle = 60
|
|
|
|
|
|
|
|
const baseThickHalf = baseThick / 2
|
|
|
|
const halfArmAngle = armAngle / 2
|
|
|
|
|
|
|
|
const arrExpShouldNotBeIncluded = [1, 2, 3]
|
|
|
|
const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 }
|
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
const part001 = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0, 0], %)
|
2025-03-07 22:07:16 -06:00
|
|
|
|> yLine(endAbsolute = 1)
|
|
|
|
|> xLine(length = 3.84) // selection-range-7ish-before-this
|
2023-09-05 16:02:27 -07:00
|
|
|
|
|
|
|
const variableBelowShouldNotBeIncluded = 3
|
2024-03-01 17:16:18 -08:00
|
|
|
"#;
|
2023-08-24 15:34:51 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
2023-09-11 15:15:37 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_function_literal_in_pipe() {
|
|
|
|
let ast = r#"const w = 20
|
|
|
|
const l = 8
|
|
|
|
const h = 10
|
|
|
|
|
|
|
|
fn thing = () => {
|
|
|
|
return -8
|
|
|
|
}
|
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
const firstExtrude = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, l])
|
|
|
|
|> line(end = [w, 0])
|
|
|
|
|> line(end = [0, thing()])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = h)"#;
|
2023-09-11 15:15:37 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_function_unary_in_pipe() {
|
|
|
|
let ast = r#"const w = 20
|
|
|
|
const l = 8
|
|
|
|
const h = 10
|
|
|
|
|
|
|
|
fn thing = (x) => {
|
|
|
|
return -x
|
|
|
|
}
|
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
const firstExtrude = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, l])
|
|
|
|
|> line(end = [w, 0])
|
|
|
|
|> line(end = [0, thing(8)])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = h)"#;
|
2023-09-11 15:15:37 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_function_array_in_pipe() {
|
|
|
|
let ast = r#"const w = 20
|
|
|
|
const l = 8
|
|
|
|
const h = 10
|
|
|
|
|
|
|
|
fn thing = (x) => {
|
|
|
|
return [0, -x]
|
|
|
|
}
|
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
const firstExtrude = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, l])
|
|
|
|
|> line(end = [w, 0])
|
|
|
|
|> line(end = thing(8))
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = h)"#;
|
2023-09-11 15:15:37 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_function_call_in_pipe() {
|
|
|
|
let ast = r#"const w = 20
|
|
|
|
const l = 8
|
|
|
|
const h = 10
|
|
|
|
|
|
|
|
fn other_thing = (y) => {
|
|
|
|
return -y
|
|
|
|
}
|
|
|
|
|
|
|
|
fn thing = (x) => {
|
|
|
|
return other_thing(x)
|
|
|
|
}
|
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
const firstExtrude = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, l])
|
|
|
|
|> line(end = [w, 0])
|
|
|
|
|> line(end = [0, thing(8)])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = h)"#;
|
2023-09-11 15:15:37 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_function_sketch() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let ast = r#"fn box = (h, l, w) => {
|
2025-02-27 15:58:58 +13:00
|
|
|
const myBox = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, l])
|
|
|
|
|> line(end = [w, 0])
|
|
|
|
|> line(end = [0, -l])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = h)
|
2023-09-11 15:15:37 -07:00
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
2024-03-01 17:16:18 -08:00
|
|
|
const fnBox = box(3, 6, 10)"#;
|
2023-09-11 15:15:37 -07:00
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_get_member_of_object_with_function_period() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let ast = r#"fn box = (obj) => {
|
2025-02-27 15:58:58 +13:00
|
|
|
let myBox = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt(obj.start, %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, obj.l])
|
|
|
|
|> line(end = [obj.w, 0])
|
|
|
|
|> line(end = [0, -obj.l])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = obj.h)
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_get_member_of_object_with_function_brace() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let ast = r#"fn box = (obj) => {
|
2025-02-27 15:58:58 +13:00
|
|
|
let myBox = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt(obj["start"], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, obj["l"]])
|
|
|
|
|> line(end = [obj["w"], 0])
|
|
|
|
|> line(end = [0, -obj["l"]])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = obj["h"])
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_get_member_of_object_with_function_mix_period_brace() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let ast = r#"fn box = (obj) => {
|
2025-02-27 15:58:58 +13:00
|
|
|
let myBox = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt(obj["start"], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, obj["l"]])
|
|
|
|
|> line(end = [obj["w"], 0])
|
|
|
|
|> line(end = [10 - obj["w"], -obj.l])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = obj["h"])
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
2024-08-13 16:25:09 -04:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
#[ignore] // https://github.com/KittyCAD/modeling-app/issues/3338
|
|
|
|
async fn test_object_member_starting_pipeline() {
|
|
|
|
let ast = r#"
|
|
|
|
fn test2 = () => {
|
|
|
|
return {
|
2025-02-27 15:58:58 +13:00
|
|
|
thing: startSketchOn(XY)
|
2024-08-13 16:25:09 -04:00
|
|
|
|> startProfileAt([0, 0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, 1])
|
|
|
|
|> line(end = [1, 0])
|
|
|
|
|> line(end = [0, -1])
|
|
|
|
|> close()
|
2024-08-13 16:25:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const x2 = test2()
|
|
|
|
|
|
|
|
x2.thing
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> extrude(length = 10)
|
2024-08-13 16:25:09 -04:00
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
2023-09-13 07:23:14 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
#[ignore] // ignore til we get loops
|
|
|
|
async fn test_execute_with_function_sketch_loop_objects() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let ast = r#"fn box = (obj) => {
|
2025-02-27 15:58:58 +13:00
|
|
|
let myBox = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt(obj.start, %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, obj.l])
|
|
|
|
|> line(end = [obj.w, 0])
|
|
|
|
|> line(end = [0, -obj.l])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = obj.h)
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
for var in [{start: [0,0], l: 6, w: 10, h: 3}, {start: [-10,-10], l: 3, w: 5, h: 1.5}] {
|
|
|
|
const thisBox = box(var)
|
|
|
|
}"#;
|
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
#[ignore] // ignore til we get loops
|
|
|
|
async fn test_execute_with_function_sketch_loop_array() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let ast = r#"fn box = (h, l, w, start) => {
|
2025-02-27 15:58:58 +13:00
|
|
|
const myBox = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, l])
|
|
|
|
|> line(end = [w, 0])
|
|
|
|
|> line(end = [0, -l])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = h)
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for var in [[3, 6, 10, [0,0]], [1.5, 3, 5, [-10,-10]]] {
|
|
|
|
const thisBox = box(var[0], var[1], var[2], var[3])
|
|
|
|
}"#;
|
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_get_member_of_array_with_function() {
|
2024-11-18 19:54:25 -05:00
|
|
|
let ast = r#"fn box = (arr) => {
|
2025-02-27 15:58:58 +13:00
|
|
|
let myBox =startSketchOn(XY)
|
2024-11-18 19:54:25 -05:00
|
|
|
|> startProfileAt(arr[0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, arr[1]])
|
|
|
|
|> line(end = [arr[2], 0])
|
|
|
|
|> line(end = [0, -arr[1]])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = arr[3])
|
2023-09-13 07:23:14 -07:00
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
const thisBox = box([[0,0], 6, 10, 3])
|
|
|
|
|
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
2024-07-22 19:43:40 -04:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_function_cannot_access_future_definitions() {
|
|
|
|
let ast = r#"
|
|
|
|
fn returnX = () => {
|
|
|
|
// x shouldn't be defined yet.
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
|
|
|
|
const x = 5
|
|
|
|
|
|
|
|
const answer = returnX()"#;
|
|
|
|
|
|
|
|
let result = parse_execute(ast).await;
|
2025-02-28 17:40:01 -08:00
|
|
|
let err = result.unwrap_err();
|
2024-07-22 19:43:40 -04:00
|
|
|
assert_eq!(
|
|
|
|
err,
|
|
|
|
KclError::UndefinedValue(KclErrorDetails {
|
|
|
|
message: "memory item key `x` is not defined".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![
|
|
|
|
SourceRange::new(64, 65, ModuleId::default()),
|
|
|
|
SourceRange::new(97, 106, ModuleId::default())
|
|
|
|
],
|
2024-07-22 19:43:40 -04:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-02-25 16:10:06 +13:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_override_prelude() {
|
|
|
|
let text = "PI = 3.0";
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(text).await.unwrap();
|
|
|
|
let errs = result.exec_state.errors();
|
2025-02-25 16:10:06 +13:00
|
|
|
assert!(errs.is_empty());
|
|
|
|
}
|
|
|
|
|
2025-03-21 10:56:55 +13:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn type_aliases() {
|
|
|
|
let text = r#"type MyTy = [number; 2]
|
|
|
|
fn foo(x: MyTy) {
|
|
|
|
return x[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
foo([0, 1])
|
|
|
|
|
|
|
|
type Other = MyTy | Helix
|
|
|
|
"#;
|
|
|
|
let result = parse_execute(text).await.unwrap();
|
|
|
|
let errs = result.exec_state.errors();
|
|
|
|
assert!(errs.is_empty());
|
|
|
|
}
|
|
|
|
|
2024-12-13 12:10:33 -08:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_cannot_shebang_in_fn() {
|
|
|
|
let ast = r#"
|
|
|
|
fn foo () {
|
|
|
|
#!hello
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
foo
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = parse_execute(ast).await;
|
2025-02-28 17:40:01 -08:00
|
|
|
let err = result.unwrap_err();
|
2024-12-13 12:10:33 -08:00
|
|
|
assert_eq!(
|
|
|
|
err,
|
|
|
|
KclError::Syntax(KclErrorDetails {
|
|
|
|
message: "Unexpected token: #".to_owned(),
|
|
|
|
source_ranges: vec![SourceRange::new(15, 16, ModuleId::default())],
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-07-22 19:43:40 -04:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_pattern_transform_function_cannot_access_future_definitions() {
|
|
|
|
let ast = r#"
|
|
|
|
fn transform = (replicaId) => {
|
|
|
|
// x shouldn't be defined yet.
|
|
|
|
let scale = x
|
|
|
|
return {
|
|
|
|
translate: [0, 0, replicaId * 10],
|
|
|
|
scale: [scale, 1, 0],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn layer = () => {
|
2025-02-27 15:58:58 +13:00
|
|
|
return startSketchOn(XY)
|
2025-02-28 17:40:01 -08:00
|
|
|
|> circle( center= [0, 0], radius= 1 , tag =$tag1)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> extrude(length = 10)
|
2024-07-22 19:43:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const x = 5
|
|
|
|
|
|
|
|
// The 10 layers are replicas of each other, with a transform applied to each.
|
2025-02-12 15:47:02 -06:00
|
|
|
let shape = layer() |> patternTransform(instances = 10, transform = transform)
|
2024-07-22 19:43:40 -04:00
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = parse_execute(ast).await;
|
2025-02-28 17:40:01 -08:00
|
|
|
let err = result.unwrap_err();
|
2024-07-22 19:43:40 -04:00
|
|
|
assert_eq!(
|
|
|
|
err,
|
|
|
|
KclError::UndefinedValue(KclErrorDetails {
|
|
|
|
message: "memory item key `x` is not defined".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(80, 81, ModuleId::default())],
|
2024-07-22 19:43:40 -04:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
// ADAM: Move some of these into simulation tests.
|
2024-07-22 19:43:40 -04:00
|
|
|
|
2023-09-13 07:23:14 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_execute_with_functions() {
|
|
|
|
let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
5.0,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "myVar")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
|
|
|
);
|
2023-09-13 07:23:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_execute() {
|
|
|
|
let ast = r#"const myVar = 1 + 2 * (3 - 4) / -5 + 6"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
7.4,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "myVar")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
|
|
|
);
|
2023-09-13 07:23:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_execute_start_negative() {
|
|
|
|
let ast = r#"const myVar = -5 + 6"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
1.0,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "myVar")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
|
|
|
);
|
2023-09-13 07:23:14 -07:00
|
|
|
}
|
2023-09-13 13:10:55 -07:00
|
|
|
|
2023-09-19 16:05:53 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_execute_with_pi() {
|
2025-02-20 19:33:21 +13:00
|
|
|
let ast = r#"const myVar = PI * 2"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
2024-12-10 18:50:22 -08:00
|
|
|
assert_eq!(
|
|
|
|
std::f64::consts::TAU,
|
2025-03-08 08:00:55 +13:00
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "myVar")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
2024-12-10 18:50:22 -08:00
|
|
|
);
|
2023-09-19 16:05:53 -07:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:10:55 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_define_decimal_without_leading_zero() {
|
|
|
|
let ast = r#"let thing = .4 + 7"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
7.4,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "thing")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
|
|
|
);
|
2023-09-13 13:10:55 -07:00
|
|
|
}
|
2023-09-15 13:19:53 -07:00
|
|
|
|
2025-01-17 07:55:01 +13:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_unit_default() {
|
2025-04-08 14:21:23 +12:00
|
|
|
let ast = r#"const inMm = fromMm(25.4)
|
|
|
|
const inInches = fromInches(1)"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
2025-02-25 16:10:06 +13:00
|
|
|
assert_eq!(
|
|
|
|
25.4,
|
2025-03-08 08:00:55 +13:00
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "inMm")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
25.4,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "inInches")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
2025-02-25 16:10:06 +13:00
|
|
|
);
|
2025-01-17 07:55:01 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_unit_overriden() {
|
|
|
|
let ast = r#"@settings(defaultLengthUnit = inch)
|
2025-04-08 14:21:23 +12:00
|
|
|
const inMm = fromMm(25.4)
|
|
|
|
const inInches = fromInches(1)"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
1.0,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "inMm")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
|
|
|
.round()
|
|
|
|
);
|
2025-02-25 16:10:06 +13:00
|
|
|
assert_eq!(
|
|
|
|
1.0,
|
2025-03-08 08:00:55 +13:00
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "inInches")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
2025-02-25 16:10:06 +13:00
|
|
|
);
|
2025-01-17 07:55:01 +13:00
|
|
|
}
|
|
|
|
|
2025-01-28 17:09:27 -08:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_unit_overriden_in() {
|
|
|
|
let ast = r#"@settings(defaultLengthUnit = in)
|
2025-04-08 14:21:23 +12:00
|
|
|
const inMm = fromMm(25.4)
|
|
|
|
const inInches = fromInches(2)"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
2025-02-25 16:10:06 +13:00
|
|
|
assert_eq!(
|
|
|
|
1.0,
|
2025-03-08 08:00:55 +13:00
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "inMm")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
|
|
|
.round()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
2.0,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "inInches")
|
|
|
|
.as_f64()
|
|
|
|
.unwrap()
|
2025-02-25 16:10:06 +13:00
|
|
|
);
|
2025-01-28 17:09:27 -08:00
|
|
|
}
|
|
|
|
|
2023-09-20 10:51:49 -05:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_zero_param_fn() {
|
|
|
|
let ast = r#"const sigmaAllow = 35000 // psi
|
|
|
|
const leg1 = 5 // inches
|
|
|
|
const leg2 = 8 // inches
|
|
|
|
fn thickness = () => { return 0.56 }
|
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
const bracket = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, leg1])
|
|
|
|
|> line(end = [leg2, 0])
|
|
|
|
|> line(end = [0, -thickness()])
|
|
|
|
|> line(end = [-leg2 + thickness(), 0])
|
2023-09-20 10:51:49 -05:00
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
2025-03-20 15:59:16 +13:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_bad_arg_count_std() {
|
|
|
|
let ast = "startSketchOn(XY)
|
|
|
|
|> startProfileAt([0, 0], %)
|
|
|
|
|> profileStartX()";
|
|
|
|
assert!(parse_execute(ast)
|
|
|
|
.await
|
|
|
|
.unwrap_err()
|
|
|
|
.message()
|
|
|
|
.contains("Expected a sketch argument"));
|
|
|
|
}
|
|
|
|
|
2024-08-14 02:38:37 -04:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_unary_operator_not_succeeds() {
|
|
|
|
let ast = r#"
|
|
|
|
fn returnTrue = () => { return !false }
|
|
|
|
const t = true
|
|
|
|
const f = false
|
|
|
|
let notTrue = !t
|
|
|
|
let notFalse = !f
|
|
|
|
let c = !!true
|
|
|
|
let d = !returnTrue()
|
|
|
|
|
|
|
|
assert(!false, "expected to pass")
|
|
|
|
|
|
|
|
fn check = (x) => {
|
|
|
|
assert(!x, "expected argument to be false")
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
check(false)
|
|
|
|
"#;
|
2025-03-08 08:00:55 +13:00
|
|
|
let result = parse_execute(ast).await.unwrap();
|
2025-02-25 16:10:06 +13:00
|
|
|
assert_eq!(
|
|
|
|
false,
|
2025-03-08 08:00:55 +13:00
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "notTrue")
|
|
|
|
.as_bool()
|
|
|
|
.unwrap()
|
2025-02-25 16:10:06 +13:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
true,
|
2025-03-08 08:00:55 +13:00
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "notFalse")
|
|
|
|
.as_bool()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
true,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "c")
|
|
|
|
.as_bool()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
false,
|
|
|
|
mem_get_json(result.exec_state.stack(), result.mem_env, "d")
|
|
|
|
.as_bool()
|
|
|
|
.unwrap()
|
2025-02-25 16:10:06 +13:00
|
|
|
);
|
2024-08-14 02:38:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_unary_operator_not_on_non_bool_fails() {
|
|
|
|
let code1 = r#"
|
|
|
|
// Yup, this is null.
|
|
|
|
let myNull = 0 / 0
|
|
|
|
let notNull = !myNull
|
|
|
|
"#;
|
|
|
|
assert_eq!(
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code1).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
2024-11-14 17:27:19 -06:00
|
|
|
message: "Cannot apply unary operator ! to non-boolean value: number".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(56, 63, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
let code2 = "let notZero = !0";
|
|
|
|
assert_eq!(
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code2).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
2024-11-25 10:50:43 +13:00
|
|
|
message: "Cannot apply unary operator ! to non-boolean value: number".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(14, 16, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
let code3 = r#"
|
|
|
|
let notEmptyString = !""
|
|
|
|
"#;
|
|
|
|
assert_eq!(
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code3).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
2024-11-14 17:27:19 -06:00
|
|
|
message: "Cannot apply unary operator ! to non-boolean value: string (text)".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(22, 25, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
let code4 = r#"
|
|
|
|
let obj = { a: 1 }
|
|
|
|
let notMember = !obj.a
|
|
|
|
"#;
|
|
|
|
assert_eq!(
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code4).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
2024-11-25 10:50:43 +13:00
|
|
|
message: "Cannot apply unary operator ! to non-boolean value: number".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(36, 42, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
let code5 = "
|
|
|
|
let a = []
|
|
|
|
let notArray = !a";
|
|
|
|
assert_eq!(
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code5).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
2024-11-14 17:27:19 -06:00
|
|
|
message: "Cannot apply unary operator ! to non-boolean value: array (list)".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(27, 29, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
let code6 = "
|
|
|
|
let x = {}
|
|
|
|
let notObject = !x";
|
|
|
|
assert_eq!(
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code6).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
2024-11-14 17:27:19 -06:00
|
|
|
message: "Cannot apply unary operator ! to non-boolean value: object".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(28, 30, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
let code7 = "
|
|
|
|
fn x = () => { return 1 }
|
|
|
|
let notFunction = !x";
|
2025-02-28 17:40:01 -08:00
|
|
|
let fn_err = parse_execute(code7).await.unwrap_err();
|
2024-08-14 02:38:37 -04:00
|
|
|
// These are currently printed out as JSON objects, so we don't want to
|
|
|
|
// check the full error.
|
|
|
|
assert!(
|
|
|
|
fn_err
|
|
|
|
.message()
|
|
|
|
.starts_with("Cannot apply unary operator ! to non-boolean value: "),
|
|
|
|
"Actual error: {:?}",
|
|
|
|
fn_err
|
|
|
|
);
|
|
|
|
|
|
|
|
let code8 = "
|
|
|
|
let myTagDeclarator = $myTag
|
|
|
|
let notTagDeclarator = !myTagDeclarator";
|
2025-02-28 17:40:01 -08:00
|
|
|
let tag_declarator_err = parse_execute(code8).await.unwrap_err();
|
2024-08-14 02:38:37 -04:00
|
|
|
// These are currently printed out as JSON objects, so we don't want to
|
|
|
|
// check the full error.
|
|
|
|
assert!(
|
|
|
|
tag_declarator_err
|
|
|
|
.message()
|
2024-11-14 17:27:19 -06:00
|
|
|
.starts_with("Cannot apply unary operator ! to non-boolean value: TagDeclarator"),
|
2024-08-14 02:38:37 -04:00
|
|
|
"Actual error: {:?}",
|
|
|
|
tag_declarator_err
|
|
|
|
);
|
|
|
|
|
|
|
|
let code9 = "
|
2024-12-13 16:39:40 -05:00
|
|
|
let myTagDeclarator = $myTag
|
|
|
|
let notTagIdentifier = !myTag";
|
2025-02-28 17:40:01 -08:00
|
|
|
let tag_identifier_err = parse_execute(code9).await.unwrap_err();
|
2024-08-14 02:38:37 -04:00
|
|
|
// These are currently printed out as JSON objects, so we don't want to
|
|
|
|
// check the full error.
|
|
|
|
assert!(
|
|
|
|
tag_identifier_err
|
|
|
|
.message()
|
2024-11-14 17:27:19 -06:00
|
|
|
.starts_with("Cannot apply unary operator ! to non-boolean value: TagIdentifier"),
|
2024-08-14 02:38:37 -04:00
|
|
|
"Actual error: {:?}",
|
|
|
|
tag_identifier_err
|
|
|
|
);
|
|
|
|
|
|
|
|
let code10 = "let notPipe = !(1 |> 2)";
|
|
|
|
assert_eq!(
|
|
|
|
// TODO: We don't currently parse this, but we should. It should be
|
|
|
|
// a runtime error instead.
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code10).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Syntax(KclErrorDetails {
|
2024-08-20 23:49:19 -04:00
|
|
|
message: "Unexpected token: !".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(14, 15, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
let code11 = "
|
|
|
|
fn identity = (x) => { return x }
|
|
|
|
let notPipeSub = 1 |> identity(!%))";
|
|
|
|
assert_eq!(
|
|
|
|
// TODO: We don't currently parse this, but we should. It should be
|
|
|
|
// a runtime error instead.
|
2025-02-28 17:40:01 -08:00
|
|
|
parse_execute(code11).await.unwrap_err(),
|
2024-08-14 02:38:37 -04:00
|
|
|
KclError::Syntax(KclErrorDetails {
|
2024-08-20 23:49:19 -04:00
|
|
|
message: "Unexpected token: |>".to_owned(),
|
2024-12-03 16:39:51 +13:00
|
|
|
source_ranges: vec![SourceRange::new(54, 56, ModuleId::default())],
|
2024-08-14 02:38:37 -04:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
// TODO: Add these tests when we support these types.
|
|
|
|
// let notNan = !NaN
|
|
|
|
// let notInfinity = !Infinity
|
|
|
|
}
|
|
|
|
|
2023-09-15 13:19:53 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_negative_variable_in_binary_expression() {
|
|
|
|
let ast = r#"const sigmaAllow = 35000 // psi
|
|
|
|
const width = 1 // inch
|
|
|
|
|
|
|
|
const p = 150 // lbs
|
|
|
|
const distance = 6 // inches
|
|
|
|
const FOS = 2
|
|
|
|
|
|
|
|
const leg1 = 5 // inches
|
|
|
|
const leg2 = 8 // inches
|
|
|
|
|
|
|
|
const thickness_squared = distance * p * FOS * 6 / sigmaAllow
|
|
|
|
const thickness = 0.56 // inches. App does not support square root function yet
|
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
const bracket = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, leg1])
|
|
|
|
|> line(end = [leg2, 0])
|
|
|
|
|> line(end = [0, -thickness])
|
|
|
|
|> line(end = [-leg2 + thickness, 0])
|
2023-09-15 17:40:57 -07:00
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
2024-08-01 19:40:22 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_function_no_return() {
|
|
|
|
let ast = r#"fn test = (origin) => {
|
|
|
|
origin
|
|
|
|
}
|
|
|
|
|
|
|
|
test([0, 0])
|
|
|
|
"#;
|
|
|
|
let result = parse_execute(ast).await;
|
|
|
|
assert!(result.is_err());
|
2025-02-22 20:16:29 +13:00
|
|
|
assert!(result.unwrap_err().to_string().contains("undefined"),);
|
2024-08-01 19:40:22 -07:00
|
|
|
}
|
|
|
|
|
2023-09-15 17:40:57 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_doubly_nested_parens() {
|
|
|
|
let ast = r#"const sigmaAllow = 35000 // psi
|
|
|
|
const width = 4 // inch
|
|
|
|
const p = 150 // Force on shelf - lbs
|
|
|
|
const distance = 6 // inches
|
|
|
|
const FOS = 2
|
|
|
|
const leg1 = 5 // inches
|
|
|
|
const leg2 = 8 // inches
|
|
|
|
const thickness_squared = (distance * p * FOS * 6 / (sigmaAllow - width))
|
|
|
|
const thickness = 0.32 // inches. App does not support square root function yet
|
2025-02-27 15:58:58 +13:00
|
|
|
const bracket = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, leg1])
|
|
|
|
|> line(end = [leg2, 0])
|
|
|
|
|> line(end = [0, -thickness])
|
|
|
|
|> line(end = [-1 * leg2 + thickness, 0])
|
|
|
|
|> line(end = [0, -1 * leg1 + thickness])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = width)
|
2023-09-15 17:40:57 -07:00
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_math_nested_parens_one_less() {
|
|
|
|
let ast = r#"const sigmaAllow = 35000 // psi
|
|
|
|
const width = 4 // inch
|
|
|
|
const p = 150 // Force on shelf - lbs
|
|
|
|
const distance = 6 // inches
|
|
|
|
const FOS = 2
|
|
|
|
const leg1 = 5 // inches
|
|
|
|
const leg2 = 8 // inches
|
|
|
|
const thickness_squared = distance * p * FOS * 6 / (sigmaAllow - width)
|
|
|
|
const thickness = 0.32 // inches. App does not support square root function yet
|
2025-02-27 15:58:58 +13:00
|
|
|
const bracket = startSketchOn(XY)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(end = [0, leg1])
|
|
|
|
|> line(end = [leg2, 0])
|
|
|
|
|> line(end = [0, -thickness])
|
|
|
|
|> line(end = [-1 * leg2 + thickness, 0])
|
|
|
|
|> line(end = [0, -1 * leg1 + thickness])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = width)
|
2023-09-15 13:19:53 -07:00
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
|
|
|
|
2024-07-29 00:33:31 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_fn_as_operand() {
|
|
|
|
let ast = r#"fn f = () => { return 1 }
|
|
|
|
let x = f()
|
|
|
|
let y = x + 1
|
|
|
|
let z = f() + 1
|
|
|
|
let w = f() + f()
|
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
2024-12-17 09:38:32 +13:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn kcl_test_ids_stable_between_executions() {
|
2025-02-27 15:58:58 +13:00
|
|
|
let code = r#"sketch001 = startSketchOn(XZ)
|
2024-12-17 09:38:32 +13:00
|
|
|
|> startProfileAt([61.74, 206.13], %)
|
2025-03-07 22:07:16 -06:00
|
|
|
|> xLine(length = 305.11, tag = $seg01)
|
|
|
|
|> yLine(length = -291.85)
|
|
|
|
|> xLine(length = -segLen(seg01))
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = 40.14)
|
2025-02-06 20:03:12 -06:00
|
|
|
|> shell(
|
|
|
|
thickness = 3.14,
|
|
|
|
faces = [seg01]
|
|
|
|
)
|
2024-12-17 09:38:32 +13:00
|
|
|
"#;
|
|
|
|
|
2025-03-31 10:56:03 -04:00
|
|
|
let ctx = crate::test_server::new_context(true, None).await.unwrap();
|
2024-12-17 09:38:32 +13:00
|
|
|
let old_program = crate::Program::parse_no_errs(code).unwrap();
|
2025-02-05 17:53:49 +13:00
|
|
|
|
2024-12-17 09:38:32 +13:00
|
|
|
// Execute the program.
|
2025-03-15 10:08:39 -07:00
|
|
|
if let Err(err) = ctx.run_with_caching(old_program).await {
|
|
|
|
let report = err.into_miette_report_with_outputs(code).unwrap();
|
|
|
|
let report = miette::Report::new(report);
|
|
|
|
panic!("Error executing program: {:?}", report);
|
|
|
|
}
|
2024-12-17 09:38:32 +13:00
|
|
|
|
|
|
|
// Get the id_generator from the first execution.
|
2025-03-15 10:08:39 -07:00
|
|
|
let id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
|
2024-12-17 09:38:32 +13:00
|
|
|
|
2025-02-27 15:58:58 +13:00
|
|
|
let code = r#"sketch001 = startSketchOn(XZ)
|
2024-12-17 09:38:32 +13:00
|
|
|
|> startProfileAt([62.74, 206.13], %)
|
2025-03-07 22:07:16 -06:00
|
|
|
|> xLine(length = 305.11, tag = $seg01)
|
|
|
|
|> yLine(length = -291.85)
|
|
|
|
|> xLine(length = -segLen(seg01))
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = 40.14)
|
2025-02-06 20:03:12 -06:00
|
|
|
|> shell(
|
|
|
|
faces = [seg01],
|
|
|
|
thickness = 3.14,
|
|
|
|
)
|
2024-12-17 09:38:32 +13:00
|
|
|
"#;
|
|
|
|
|
|
|
|
// Execute a slightly different program again.
|
2025-02-05 17:53:49 +13:00
|
|
|
let program = crate::Program::parse_no_errs(code).unwrap();
|
2024-12-17 09:38:32 +13:00
|
|
|
// Execute the program.
|
2025-02-05 17:53:49 +13:00
|
|
|
ctx.run_with_caching(program).await.unwrap();
|
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
|
2024-12-17 09:38:32 +13:00
|
|
|
|
2025-02-05 17:53:49 +13:00
|
|
|
assert_eq!(id_generator, new_id_generator);
|
2024-12-17 09:38:32 +13:00
|
|
|
}
|
2025-02-20 22:08:49 -08:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn kcl_test_changing_a_setting_updates_the_cached_state() {
|
|
|
|
let code = r#"sketch001 = startSketchOn('XZ')
|
|
|
|
|> startProfileAt([61.74, 206.13], %)
|
2025-03-07 22:07:16 -06:00
|
|
|
|> xLine(length = 305.11, tag = $seg01)
|
|
|
|
|> yLine(length = -291.85)
|
|
|
|
|> xLine(length = -segLen(seg01))
|
2025-02-20 22:08:49 -08:00
|
|
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
|
|
|> close()
|
|
|
|
|> extrude(length = 40.14)
|
|
|
|
|> shell(
|
|
|
|
thickness = 3.14,
|
|
|
|
faces = [seg01]
|
|
|
|
)
|
|
|
|
"#;
|
|
|
|
|
2025-03-31 10:56:03 -04:00
|
|
|
let mut ctx = crate::test_server::new_context(true, None).await.unwrap();
|
2025-02-20 22:08:49 -08:00
|
|
|
let old_program = crate::Program::parse_no_errs(code).unwrap();
|
|
|
|
|
|
|
|
// Execute the program.
|
|
|
|
ctx.run_with_caching(old_program.clone()).await.unwrap();
|
|
|
|
|
|
|
|
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
|
|
|
|
|
|
|
// Ensure the settings are as expected.
|
|
|
|
assert_eq!(settings_state, ctx.settings);
|
|
|
|
|
|
|
|
// Change a setting.
|
|
|
|
ctx.settings.highlight_edges = !ctx.settings.highlight_edges;
|
|
|
|
|
|
|
|
// Execute the program.
|
|
|
|
ctx.run_with_caching(old_program.clone()).await.unwrap();
|
|
|
|
|
|
|
|
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
|
|
|
|
|
|
|
// Ensure the settings are as expected.
|
|
|
|
assert_eq!(settings_state, ctx.settings);
|
|
|
|
|
|
|
|
// Change a setting.
|
|
|
|
ctx.settings.highlight_edges = !ctx.settings.highlight_edges;
|
|
|
|
|
|
|
|
// Execute the program.
|
|
|
|
ctx.run_with_caching(old_program).await.unwrap();
|
|
|
|
|
|
|
|
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
|
|
|
|
|
|
|
// Ensure the settings are as expected.
|
|
|
|
assert_eq!(settings_state, ctx.settings);
|
2025-03-28 14:14:29 -07:00
|
|
|
|
|
|
|
ctx.close().await;
|
2025-02-20 22:08:49 -08:00
|
|
|
}
|
2025-02-25 16:10:06 +13:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn mock_after_not_mock() {
|
2025-03-31 10:56:03 -04:00
|
|
|
let ctx = ExecutorContext::new_with_default_client().await.unwrap();
|
2025-02-25 16:10:06 +13:00
|
|
|
let program = crate::Program::parse_no_errs("x = 2").unwrap();
|
|
|
|
let result = ctx.run_with_caching(program).await.unwrap();
|
|
|
|
assert_eq!(result.variables.get("x").unwrap().as_f64().unwrap(), 2.0);
|
|
|
|
|
|
|
|
let ctx2 = ExecutorContext::new_mock().await;
|
|
|
|
let program2 = crate::Program::parse_no_errs("z = x + 1").unwrap();
|
2025-03-08 04:04:57 +13:00
|
|
|
let result = ctx2.run_mock(program2, true).await.unwrap();
|
2025-02-25 16:10:06 +13:00
|
|
|
assert_eq!(result.variables.get("z").unwrap().as_f64().unwrap(), 3.0);
|
2025-03-28 14:14:29 -07:00
|
|
|
|
|
|
|
ctx.close().await;
|
|
|
|
ctx2.close().await;
|
2025-02-25 16:10:06 +13:00
|
|
|
}
|
2025-03-17 12:28:51 +13:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn read_tag_version() {
|
|
|
|
let ast = r#"fn bar(t) {
|
|
|
|
return startSketchOn(XY)
|
|
|
|
|> startProfileAt([0,0], %)
|
KCL: Angled line should use keyword args (#5803)
We continue migrating KCL stdlib functions to use keyword arguments. Next up is the `angledLine` family of functions (except `angledLineThatIntersects, which will be a quick follow-up).
Before vs. after:
`angledLine({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, length = 3, tag = $edge)`
`angledLineOfXLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthX = 3, tag = $edge)`
`angledLineOfYLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthY = 3, tag = $edge)`
`angledLineToX({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteX = 3, tag = $edge)`
`angledLineToY({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteY = 3, tag = $edge)`
2025-04-09 14:55:15 -05:00
|
|
|
|> angledLine(
|
2025-03-17 12:28:51 +13:00
|
|
|
angle = -60,
|
|
|
|
length = segLen(t),
|
KCL: Angled line should use keyword args (#5803)
We continue migrating KCL stdlib functions to use keyword arguments. Next up is the `angledLine` family of functions (except `angledLineThatIntersects, which will be a quick follow-up).
Before vs. after:
`angledLine({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, length = 3, tag = $edge)`
`angledLineOfXLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthX = 3, tag = $edge)`
`angledLineOfYLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthY = 3, tag = $edge)`
`angledLineToX({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteX = 3, tag = $edge)`
`angledLineToY({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteY = 3, tag = $edge)`
2025-04-09 14:55:15 -05:00
|
|
|
)
|
2025-03-17 12:28:51 +13:00
|
|
|
|> line(end = [0, 0])
|
|
|
|
|> close()
|
|
|
|
}
|
|
|
|
|
|
|
|
sketch = startSketchOn(XY)
|
|
|
|
|> startProfileAt([0,0], %)
|
|
|
|
|> line(end = [0, 10])
|
|
|
|
|> line(end = [10, 0], tag = $tag0)
|
|
|
|
|> line(end = [0, 0])
|
|
|
|
|
|
|
|
fn foo() {
|
|
|
|
// tag0 tags an edge
|
|
|
|
return bar(tag0)
|
|
|
|
}
|
|
|
|
|
|
|
|
solid = sketch |> extrude(length = 10)
|
|
|
|
// tag0 tags a face
|
2025-04-14 05:58:19 -04:00
|
|
|
sketch2 = startSketchOn(solid, face = tag0)
|
2025-03-17 12:28:51 +13:00
|
|
|
|> startProfileAt([0,0], %)
|
|
|
|
|> line(end = [0, 1])
|
|
|
|
|> line(end = [1, 0])
|
|
|
|
|> line(end = [0, 0])
|
|
|
|
|
|
|
|
foo() |> extrude(length = 1)
|
|
|
|
"#;
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
2023-08-24 15:34:51 -07:00
|
|
|
}
|