2023-08-24 15:34:51 -07:00
|
|
|
//! The executor for the AST.
|
|
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use anyhow::Result;
|
2023-08-25 13:41:04 -07:00
|
|
|
use schemars::JsonSchema;
|
2023-08-24 15:34:51 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
2023-09-05 16:02:27 -07:00
|
|
|
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
|
2023-08-25 13:41:04 -07:00
|
|
|
|
2023-08-24 15:34:51 -07:00
|
|
|
use crate::{
|
2023-09-05 16:02:27 -07:00
|
|
|
abstract_syntax_tree_types::{BodyItem, Function, FunctionExpression, Value},
|
2023-08-24 15:34:51 -07:00
|
|
|
engine::EngineConnection,
|
|
|
|
errors::{KclError, KclErrorDetails},
|
|
|
|
};
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct ProgramMemory {
|
|
|
|
pub root: HashMap<String, MemoryItem>,
|
|
|
|
#[serde(rename = "return")]
|
|
|
|
pub return_: Option<ProgramReturn>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ProgramMemory {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
root: HashMap::new(),
|
|
|
|
return_: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add to the program memory.
|
2023-08-29 14:12:48 -07:00
|
|
|
pub fn add(&mut self, key: &str, value: MemoryItem, source_range: SourceRange) -> Result<(), KclError> {
|
2023-08-24 15:34:51 -07:00
|
|
|
if self.root.get(key).is_some() {
|
|
|
|
return Err(KclError::ValueAlreadyDefined(KclErrorDetails {
|
|
|
|
message: format!("Cannot redefine {}", key),
|
|
|
|
source_ranges: vec![source_range],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
self.root.insert(key.to_string(), value);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a value from the program memory.
|
|
|
|
pub fn get(&self, key: &str, source_range: SourceRange) -> Result<&MemoryItem, KclError> {
|
|
|
|
self.root.get(key).ok_or_else(|| {
|
|
|
|
KclError::UndefinedValue(KclErrorDetails {
|
|
|
|
message: format!("memory item key `{}` is not defined", key),
|
|
|
|
source_ranges: vec![source_range],
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ProgramMemory {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase", untagged)]
|
|
|
|
pub enum ProgramReturn {
|
|
|
|
Arguments(Vec<Value>),
|
|
|
|
Value(MemoryItem),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ProgramReturn> for Vec<SourceRange> {
|
|
|
|
fn from(item: ProgramReturn) -> Self {
|
|
|
|
match item {
|
|
|
|
ProgramReturn::Arguments(args) => args
|
|
|
|
.iter()
|
|
|
|
.map(|arg| {
|
|
|
|
let r: SourceRange = arg.into();
|
|
|
|
r
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
ProgramReturn::Value(v) => v.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ProgramReturn {
|
|
|
|
pub fn get_value(&self) -> Result<MemoryItem, KclError> {
|
|
|
|
match self {
|
|
|
|
ProgramReturn::Value(v) => Ok(v.clone()),
|
|
|
|
ProgramReturn::Arguments(args) => Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("Cannot get value from arguments: {:?}", args),
|
|
|
|
source_ranges: self.clone().into(),
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
2023-09-12 18:10:27 -07:00
|
|
|
#[serde(tag = "type")]
|
2023-08-24 15:34:51 -07:00
|
|
|
pub enum MemoryItem {
|
2023-09-12 18:10:27 -07:00
|
|
|
UserVal(UserVal),
|
2023-08-24 15:34:51 -07:00
|
|
|
SketchGroup(SketchGroup),
|
|
|
|
ExtrudeGroup(ExtrudeGroup),
|
2023-09-12 18:10:27 -07:00
|
|
|
#[ts(skip)]
|
2023-08-25 13:41:04 -07:00
|
|
|
ExtrudeTransform(ExtrudeTransform),
|
2023-09-12 18:10:27 -07:00
|
|
|
#[ts(skip)]
|
2023-08-24 15:34:51 -07:00
|
|
|
Function {
|
|
|
|
#[serde(skip)]
|
|
|
|
func: Option<MemoryFunction>,
|
|
|
|
expression: Box<FunctionExpression>,
|
|
|
|
#[serde(rename = "__meta")]
|
|
|
|
meta: Vec<Metadata>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
|
|
#[ts(export)]
|
2023-09-12 18:10:27 -07:00
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
|
|
pub struct UserVal {
|
|
|
|
pub value: serde_json::Value,
|
|
|
|
#[serde(rename = "__meta")]
|
|
|
|
pub meta: Vec<Metadata>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
|
|
#[ts(export)]
|
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
2023-08-25 13:41:04 -07:00
|
|
|
pub struct ExtrudeTransform {
|
|
|
|
pub position: Position,
|
|
|
|
pub rotation: Rotation,
|
|
|
|
#[serde(rename = "__meta")]
|
|
|
|
pub meta: Vec<Metadata>,
|
|
|
|
}
|
|
|
|
|
2023-08-24 15:34:51 -07:00
|
|
|
pub type MemoryFunction = fn(
|
|
|
|
s: &[MemoryItem],
|
|
|
|
memory: &ProgramMemory,
|
|
|
|
expression: &FunctionExpression,
|
|
|
|
metadata: &[Metadata],
|
|
|
|
engine: &mut EngineConnection,
|
|
|
|
) -> Result<Option<ProgramReturn>, KclError>;
|
|
|
|
|
|
|
|
impl From<MemoryItem> for Vec<SourceRange> {
|
|
|
|
fn from(item: MemoryItem) -> Self {
|
|
|
|
match item {
|
2023-09-12 18:10:27 -07:00
|
|
|
MemoryItem::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(),
|
2023-08-24 15:34:51 -07:00
|
|
|
MemoryItem::SketchGroup(s) => s.meta.iter().map(|m| m.source_range).collect(),
|
|
|
|
MemoryItem::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
2023-08-25 13:41:04 -07:00
|
|
|
MemoryItem::ExtrudeTransform(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
2023-08-24 15:34:51 -07:00
|
|
|
MemoryItem::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MemoryItem {
|
|
|
|
pub fn get_json_value(&self) -> Result<serde_json::Value, KclError> {
|
2023-09-12 18:10:27 -07:00
|
|
|
if let MemoryItem::UserVal(user_val) = self {
|
|
|
|
Ok(user_val.value.clone())
|
2023-08-24 15:34:51 -07:00
|
|
|
} else {
|
|
|
|
Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("Not a user value: {:?}", self),
|
|
|
|
source_ranges: self.clone().into(),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn call_fn(
|
|
|
|
&self,
|
|
|
|
args: &[MemoryItem],
|
|
|
|
memory: &ProgramMemory,
|
|
|
|
engine: &mut EngineConnection,
|
|
|
|
) -> Result<Option<ProgramReturn>, KclError> {
|
2023-08-29 14:12:48 -07:00
|
|
|
if let MemoryItem::Function { func, expression, meta } = self {
|
2023-08-24 15:34:51 -07:00
|
|
|
if let Some(func) = func {
|
|
|
|
func(args, memory, expression, meta, engine)
|
|
|
|
} else {
|
|
|
|
Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("Not a function: {:?}", self),
|
|
|
|
source_ranges: vec![],
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("not a function: {:?}", self),
|
|
|
|
source_ranges: vec![],
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// A sketch group is a collection of paths.
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
2023-09-12 18:10:27 -07:00
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
2023-08-24 15:34:51 -07:00
|
|
|
pub struct SketchGroup {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The id of the sketch group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub id: uuid::Uuid,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The paths in the sketch group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub value: Vec<Path>,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The starting path.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub start: BasePath,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The position of the sketch group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub position: Position,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The rotation of the sketch group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub rotation: Rotation,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Metadata.
|
2023-08-24 15:34:51 -07:00
|
|
|
#[serde(rename = "__meta")]
|
|
|
|
pub meta: Vec<Metadata>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SketchGroup {
|
|
|
|
pub fn get_path_by_id(&self, id: &uuid::Uuid) -> Option<&Path> {
|
|
|
|
self.value.iter().find(|p| p.get_id() == *id)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_path_by_name(&self, name: &str) -> Option<&Path> {
|
|
|
|
self.value.iter().find(|p| p.get_name() == name)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_base_by_name_or_start(&self, name: &str) -> Option<&BasePath> {
|
|
|
|
if self.start.name == name {
|
|
|
|
Some(&self.start)
|
|
|
|
} else {
|
2023-08-29 14:12:48 -07:00
|
|
|
self.value.iter().find(|p| p.get_name() == name).map(|p| p.get_base())
|
2023-08-24 15:34:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_coords_from_paths(&self) -> Result<Point2d, KclError> {
|
|
|
|
if self.value.is_empty() {
|
|
|
|
return Ok(self.start.to.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
let index = self.value.len() - 1;
|
|
|
|
if let Some(path) = self.value.get(index) {
|
|
|
|
let base = path.get_base();
|
|
|
|
Ok(base.to.into())
|
|
|
|
} else {
|
|
|
|
Ok(self.start.to.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// An extrude group is a collection of extrude surfaces.
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
2023-09-12 18:10:27 -07:00
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
2023-08-24 15:34:51 -07:00
|
|
|
pub struct ExtrudeGroup {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The id of the extrude group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub id: uuid::Uuid,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The extrude surfaces.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub value: Vec<ExtrudeSurface>,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The height of the extrude group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub height: f64,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The position of the extrude group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub position: Position,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The rotation of the extrude group.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub rotation: Rotation,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Metadata.
|
2023-08-24 15:34:51 -07:00
|
|
|
#[serde(rename = "__meta")]
|
|
|
|
pub meta: Vec<Metadata>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExtrudeGroup {
|
|
|
|
pub fn get_path_by_id(&self, id: &uuid::Uuid) -> Option<&ExtrudeSurface> {
|
|
|
|
self.value.iter().find(|p| p.get_id() == *id)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_path_by_name(&self, name: &str) -> Option<&ExtrudeSurface> {
|
|
|
|
self.value.iter().find(|p| p.get_name() == name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub enum BodyType {
|
|
|
|
Root,
|
|
|
|
Sketch,
|
|
|
|
Block,
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
2023-09-12 18:10:27 -07:00
|
|
|
pub struct Position(#[ts(type = "[number, number, number]")] pub [f64; 3]);
|
2023-08-24 15:34:51 -07:00
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
2023-09-12 18:10:27 -07:00
|
|
|
pub struct Rotation(#[ts(type = "[number, number, number, number]")] pub [f64; 4]);
|
2023-08-24 15:34:51 -07:00
|
|
|
|
2023-09-05 16:02:27 -07:00
|
|
|
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema, Hash, Eq)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
2023-09-12 18:10:27 -07:00
|
|
|
pub struct SourceRange(#[ts(type = "[number, number]")] pub [usize; 2]);
|
2023-08-24 15:34:51 -07:00
|
|
|
|
2023-09-05 16:02:27 -07:00
|
|
|
impl SourceRange {
|
|
|
|
/// Create a new source range.
|
|
|
|
pub fn new(start: usize, end: usize) -> Self {
|
|
|
|
Self([start, end])
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the start of the range.
|
|
|
|
pub fn start(&self) -> usize {
|
|
|
|
self.0[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the end of the range.
|
|
|
|
pub fn end(&self) -> usize {
|
|
|
|
self.0[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check if the range contains a position.
|
|
|
|
pub fn contains(&self, pos: usize) -> bool {
|
|
|
|
pos >= self.start() && pos <= self.end()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn start_to_lsp_position(&self, code: &str) -> LspPosition {
|
|
|
|
// Calculate the line and column of the error from the source range.
|
|
|
|
// Lines are zero indexed in vscode so we need to subtract 1.
|
|
|
|
let mut line = code[..self.start()].lines().count();
|
|
|
|
if line > 0 {
|
|
|
|
line = line.saturating_sub(1);
|
|
|
|
}
|
|
|
|
let column = code[..self.start()].lines().last().map(|l| l.len()).unwrap_or_default();
|
|
|
|
|
|
|
|
LspPosition {
|
|
|
|
line: line as u32,
|
|
|
|
character: column as u32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn end_to_lsp_position(&self, code: &str) -> LspPosition {
|
|
|
|
// Calculate the line and column of the error from the source range.
|
|
|
|
// Lines are zero indexed in vscode so we need to subtract 1.
|
|
|
|
let line = code[..self.end()].lines().count() - 1;
|
|
|
|
let column = code[..self.end()].lines().last().map(|l| l.len()).unwrap_or_default();
|
|
|
|
|
|
|
|
LspPosition {
|
|
|
|
line: line as u32,
|
|
|
|
character: column as u32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_lsp_range(&self, code: &str) -> LspRange {
|
|
|
|
let start = self.start_to_lsp_position(code);
|
|
|
|
let end = self.end_to_lsp_position(code);
|
|
|
|
LspRange { start, end }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
pub struct Point2d {
|
|
|
|
pub x: f64,
|
|
|
|
pub y: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<[f64; 2]> for Point2d {
|
|
|
|
fn from(p: [f64; 2]) -> Self {
|
|
|
|
Self { x: p[0], y: p[1] }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 22:19:23 -07:00
|
|
|
impl From<&[f64; 2]> for Point2d {
|
|
|
|
fn from(p: &[f64; 2]) -> Self {
|
|
|
|
Self { x: p[0], y: p[1] }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-24 15:34:51 -07:00
|
|
|
impl From<Point2d> for [f64; 2] {
|
|
|
|
fn from(p: Point2d) -> Self {
|
|
|
|
[p.x, p.y]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 22:19:23 -07:00
|
|
|
impl From<Point2d> for kittycad::types::Point2D {
|
|
|
|
fn from(p: Point2d) -> Self {
|
|
|
|
Self { x: p.x, y: p.y }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
pub struct Point3d {
|
|
|
|
pub x: f64,
|
|
|
|
pub y: f64,
|
|
|
|
pub z: f64,
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Metadata.
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SourceRange> for Metadata {
|
|
|
|
fn from(source_range: SourceRange) -> Self {
|
|
|
|
Self { source_range }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// A base path.
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct BasePath {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The from point.
|
2023-09-12 18:10:27 -07:00
|
|
|
#[ts(type = "[number, number]")]
|
2023-08-24 15:34:51 -07:00
|
|
|
pub from: [f64; 2],
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The to point.
|
2023-09-12 18:10:27 -07:00
|
|
|
#[ts(type = "[number, number]")]
|
2023-08-24 15:34:51 -07:00
|
|
|
pub to: [f64; 2],
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The name of the path.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub name: String,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Metadata.
|
2023-08-24 15:34:51 -07:00
|
|
|
#[serde(rename = "__geoMeta")]
|
|
|
|
pub geo_meta: GeoMeta,
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Geometry metadata.
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct GeoMeta {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The id of the geometry.
|
2023-08-24 15:34:51 -07:00
|
|
|
pub id: uuid::Uuid,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Metadata.
|
2023-08-24 15:34:51 -07:00
|
|
|
#[serde(flatten)]
|
|
|
|
pub metadata: Metadata,
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// A path.
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
|
|
pub enum Path {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// A path that goes to a point.
|
2023-08-24 15:34:51 -07:00
|
|
|
ToPoint {
|
|
|
|
#[serde(flatten)]
|
|
|
|
base: BasePath,
|
|
|
|
},
|
2023-08-25 13:41:04 -07:00
|
|
|
/// A path that is horizontal.
|
2023-08-24 15:34:51 -07:00
|
|
|
Horizontal {
|
|
|
|
#[serde(flatten)]
|
|
|
|
base: BasePath,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The x coordinate.
|
2023-08-24 15:34:51 -07:00
|
|
|
x: f64,
|
|
|
|
},
|
2023-08-25 13:41:04 -07:00
|
|
|
/// An angled line to.
|
2023-08-24 15:34:51 -07:00
|
|
|
AngledLineTo {
|
|
|
|
#[serde(flatten)]
|
|
|
|
base: BasePath,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The x coordinate.
|
2023-08-24 15:34:51 -07:00
|
|
|
x: Option<f64>,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The y coordinate.
|
2023-08-24 15:34:51 -07:00
|
|
|
y: Option<f64>,
|
|
|
|
},
|
2023-08-25 13:41:04 -07:00
|
|
|
/// A base path.
|
2023-08-24 15:34:51 -07:00
|
|
|
Base {
|
|
|
|
#[serde(flatten)]
|
|
|
|
base: BasePath,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Path {
|
|
|
|
pub fn get_id(&self) -> uuid::Uuid {
|
|
|
|
match self {
|
|
|
|
Path::ToPoint { base } => base.geo_meta.id,
|
|
|
|
Path::Horizontal { base, .. } => base.geo_meta.id,
|
|
|
|
Path::AngledLineTo { base, .. } => base.geo_meta.id,
|
|
|
|
Path::Base { base } => base.geo_meta.id,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_name(&self) -> String {
|
|
|
|
match self {
|
|
|
|
Path::ToPoint { base } => base.name.clone(),
|
|
|
|
Path::Horizontal { base, .. } => base.name.clone(),
|
|
|
|
Path::AngledLineTo { base, .. } => base.name.clone(),
|
|
|
|
Path::Base { base } => base.name.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_base(&self) -> &BasePath {
|
|
|
|
match self {
|
|
|
|
Path::ToPoint { base } => base,
|
|
|
|
Path::Horizontal { base, .. } => base,
|
|
|
|
Path::AngledLineTo { base, .. } => base,
|
|
|
|
Path::Base { base } => base,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
/// An extrude surface.
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
|
|
pub enum ExtrudeSurface {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// An extrude plane.
|
2023-08-24 15:34:51 -07:00
|
|
|
ExtrudePlane {
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The position.
|
2023-08-24 15:34:51 -07:00
|
|
|
position: Position,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The rotation.
|
2023-08-24 15:34:51 -07:00
|
|
|
rotation: Rotation,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// The name.
|
2023-08-24 15:34:51 -07:00
|
|
|
name: String,
|
2023-08-25 13:41:04 -07:00
|
|
|
/// Metadata.
|
2023-08-24 15:34:51 -07:00
|
|
|
#[serde(flatten)]
|
|
|
|
geo_meta: GeoMeta,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExtrudeSurface {
|
|
|
|
pub fn get_id(&self) -> uuid::Uuid {
|
|
|
|
match self {
|
|
|
|
ExtrudeSurface::ExtrudePlane { geo_meta, .. } => geo_meta.id,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_name(&self) -> String {
|
|
|
|
match self {
|
|
|
|
ExtrudeSurface::ExtrudePlane { name, .. } => name.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_position(&self) -> Position {
|
|
|
|
match self {
|
|
|
|
ExtrudeSurface::ExtrudePlane { position, .. } => *position,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_rotation(&self) -> Rotation {
|
|
|
|
match self {
|
|
|
|
ExtrudeSurface::ExtrudePlane { rotation, .. } => *rotation,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-25 13:41:04 -07:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
2023-08-24 15:34:51 -07:00
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct PipeInfo {
|
|
|
|
pub previous_results: Vec<MemoryItem>,
|
|
|
|
pub is_in_pipe: bool,
|
|
|
|
pub index: usize,
|
|
|
|
pub body: Vec<Value>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PipeInfo {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
previous_results: Vec::new(),
|
|
|
|
is_in_pipe: false,
|
|
|
|
index: 0,
|
|
|
|
body: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for PipeInfo {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Execute a AST's program.
|
2023-08-28 14:58:24 -07:00
|
|
|
pub fn execute(
|
2023-08-24 15:34:51 -07:00
|
|
|
program: crate::abstract_syntax_tree_types::Program,
|
|
|
|
memory: &mut ProgramMemory,
|
|
|
|
options: BodyType,
|
|
|
|
engine: &mut EngineConnection,
|
|
|
|
) -> Result<ProgramMemory, KclError> {
|
|
|
|
let mut pipe_info = PipeInfo::default();
|
|
|
|
|
|
|
|
// Iterate over the body of the program.
|
|
|
|
for statement in &program.body {
|
|
|
|
match statement {
|
|
|
|
BodyItem::ExpressionStatement(expression_statement) => {
|
|
|
|
if let Value::CallExpression(call_expr) = &expression_statement.expression {
|
|
|
|
let fn_name = call_expr.callee.name.to_string();
|
|
|
|
let mut args: Vec<MemoryItem> = Vec::new();
|
|
|
|
for arg in &call_expr.arguments {
|
|
|
|
match arg {
|
|
|
|
Value::Literal(literal) => args.push(literal.into()),
|
|
|
|
Value::Identifier(identifier) => {
|
2023-08-29 14:12:48 -07:00
|
|
|
let memory_item = memory.get(&identifier.name, identifier.into())?;
|
2023-08-24 15:34:51 -07:00
|
|
|
args.push(memory_item.clone());
|
|
|
|
}
|
|
|
|
// We do nothing for the rest.
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
2023-09-05 16:02:27 -07:00
|
|
|
let _show_fn = Box::new(crate::std::Show);
|
|
|
|
if let Function::StdLib { func: _show_fn } = &call_expr.function {
|
2023-08-24 15:34:51 -07:00
|
|
|
if options != BodyType::Root {
|
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: "Cannot call show outside of a root".to_string(),
|
|
|
|
source_ranges: vec![call_expr.into()],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2023-08-29 14:12:48 -07:00
|
|
|
memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone()));
|
2023-08-24 15:34:51 -07:00
|
|
|
} else if let Some(func) = memory.clone().root.get(&fn_name) {
|
2023-09-11 15:15:37 -07:00
|
|
|
let result = func.call_fn(&args, memory, engine)?;
|
|
|
|
|
|
|
|
memory.return_ = result;
|
2023-08-24 15:34:51 -07:00
|
|
|
} else {
|
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("No such name {} defined", fn_name),
|
|
|
|
source_ranges: vec![call_expr.into()],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BodyItem::VariableDeclaration(variable_declaration) => {
|
|
|
|
for declaration in &variable_declaration.declarations {
|
|
|
|
let var_name = declaration.id.name.to_string();
|
|
|
|
let source_range: SourceRange = declaration.init.clone().into();
|
|
|
|
let metadata = Metadata { source_range };
|
|
|
|
|
|
|
|
match &declaration.init {
|
|
|
|
Value::Literal(literal) => {
|
|
|
|
memory.add(&var_name, literal.into(), source_range)?;
|
|
|
|
}
|
|
|
|
Value::Identifier(identifier) => {
|
|
|
|
let value = memory.get(&identifier.name, identifier.into())?;
|
|
|
|
memory.add(&var_name, value.clone(), source_range)?;
|
|
|
|
}
|
|
|
|
Value::BinaryExpression(binary_expression) => {
|
2023-09-05 16:02:27 -07:00
|
|
|
let result = binary_expression.get_result(memory, &mut pipe_info, engine)?;
|
2023-08-24 15:34:51 -07:00
|
|
|
memory.add(&var_name, result, source_range)?;
|
|
|
|
}
|
|
|
|
Value::FunctionExpression(function_expression) => {
|
|
|
|
memory.add(
|
|
|
|
&var_name,
|
|
|
|
MemoryItem::Function{
|
|
|
|
expression: function_expression.clone(),
|
|
|
|
meta: vec![metadata],
|
|
|
|
func: Some(|args: &[MemoryItem], memory: &ProgramMemory, function_expression: &FunctionExpression, _metadata: &[Metadata], engine: &mut EngineConnection| -> Result<Option<ProgramReturn>, KclError> {
|
|
|
|
let mut fn_memory = memory.clone();
|
|
|
|
|
|
|
|
if args.len() != function_expression.params.len() {
|
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("Expected {} arguments, got {}", function_expression.params.len(), args.len()),
|
|
|
|
source_ranges: vec![function_expression.into()],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the arguments to the memory.
|
|
|
|
for (index, param) in function_expression.params.iter().enumerate() {
|
|
|
|
fn_memory.add(
|
|
|
|
¶m.name,
|
2023-09-06 21:56:10 -07:00
|
|
|
args.get(index).unwrap().clone(),
|
2023-08-24 15:34:51 -07:00
|
|
|
param.into(),
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let result = execute(function_expression.body.clone(), &mut fn_memory, BodyType::Block, engine)?;
|
|
|
|
|
|
|
|
Ok(result.return_)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
source_range,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Value::CallExpression(call_expression) => {
|
2023-09-05 16:02:27 -07:00
|
|
|
let result = call_expression.execute(memory, &mut pipe_info, engine)?;
|
2023-08-24 15:34:51 -07:00
|
|
|
memory.add(&var_name, result, source_range)?;
|
|
|
|
}
|
|
|
|
Value::PipeExpression(pipe_expression) => {
|
2023-09-05 16:02:27 -07:00
|
|
|
let result = pipe_expression.get_result(memory, &mut pipe_info, engine)?;
|
2023-08-24 15:34:51 -07:00
|
|
|
memory.add(&var_name, result, source_range)?;
|
|
|
|
}
|
|
|
|
Value::PipeSubstitution(pipe_substitution) => {
|
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
2023-08-29 14:12:48 -07:00
|
|
|
message: format!(
|
|
|
|
"pipe substitution not implemented for declaration of variable {}",
|
|
|
|
var_name
|
|
|
|
),
|
2023-08-24 15:34:51 -07:00
|
|
|
source_ranges: vec![pipe_substitution.into()],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
Value::ArrayExpression(array_expression) => {
|
2023-09-05 16:02:27 -07:00
|
|
|
let result = array_expression.execute(memory, &mut pipe_info, engine)?;
|
2023-08-24 15:34:51 -07:00
|
|
|
memory.add(&var_name, result, source_range)?;
|
|
|
|
}
|
|
|
|
Value::ObjectExpression(object_expression) => {
|
2023-09-05 16:02:27 -07:00
|
|
|
let result = object_expression.execute(memory, &mut pipe_info, engine)?;
|
2023-08-24 15:34:51 -07:00
|
|
|
memory.add(&var_name, result, source_range)?;
|
|
|
|
}
|
|
|
|
Value::MemberExpression(member_expression) => {
|
|
|
|
let result = member_expression.get_result(memory)?;
|
|
|
|
memory.add(&var_name, result, source_range)?;
|
|
|
|
}
|
|
|
|
Value::UnaryExpression(unary_expression) => {
|
2023-09-05 16:02:27 -07:00
|
|
|
let result = unary_expression.get_result(memory, &mut pipe_info, engine)?;
|
2023-08-24 15:34:51 -07:00
|
|
|
memory.add(&var_name, result, source_range)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
|
|
|
|
Value::BinaryExpression(bin_expr) => {
|
2023-09-05 16:02:27 -07:00
|
|
|
let result = bin_expr.get_result(memory, &mut pipe_info, engine)?;
|
2023-08-24 15:34:51 -07:00
|
|
|
memory.return_ = Some(ProgramReturn::Value(result));
|
|
|
|
}
|
2023-09-11 15:15:37 -07:00
|
|
|
Value::UnaryExpression(unary_expr) => {
|
|
|
|
let result = unary_expr.get_result(memory, &mut pipe_info, engine)?;
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(result));
|
|
|
|
}
|
2023-08-24 15:34:51 -07:00
|
|
|
Value::Identifier(identifier) => {
|
|
|
|
let value = memory.get(&identifier.name, identifier.into())?.clone();
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(value));
|
|
|
|
}
|
2023-09-11 15:15:37 -07:00
|
|
|
Value::Literal(literal) => {
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(literal.into()));
|
|
|
|
}
|
|
|
|
Value::ArrayExpression(array_expr) => {
|
|
|
|
let result = array_expr.execute(memory, &mut pipe_info, engine)?;
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(result));
|
|
|
|
}
|
|
|
|
Value::ObjectExpression(obj_expr) => {
|
|
|
|
let result = obj_expr.execute(memory, &mut pipe_info, engine)?;
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(result));
|
|
|
|
}
|
|
|
|
Value::CallExpression(call_expr) => {
|
|
|
|
let result = call_expr.execute(memory, &mut pipe_info, engine)?;
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(result));
|
|
|
|
}
|
|
|
|
Value::MemberExpression(member_expr) => {
|
|
|
|
let result = member_expr.get_result(memory)?;
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(result));
|
|
|
|
}
|
|
|
|
Value::PipeExpression(pipe_expr) => {
|
|
|
|
let result = pipe_expr.get_result(memory, &mut pipe_info, engine)?;
|
|
|
|
memory.return_ = Some(ProgramReturn::Value(result));
|
|
|
|
}
|
|
|
|
Value::PipeSubstitution(_) => {}
|
|
|
|
Value::FunctionExpression(_) => {}
|
2023-08-24 15:34:51 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(memory.clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
|
2023-08-29 14:12:48 -07:00
|
|
|
use super::*;
|
|
|
|
|
2023-08-24 15:34:51 -07:00
|
|
|
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
|
|
|
let tokens = crate::tokeniser::lexer(code);
|
2023-09-05 16:02:27 -07:00
|
|
|
let parser = crate::parser::Parser::new(tokens);
|
|
|
|
let program = parser.ast()?;
|
2023-08-24 15:34:51 -07:00
|
|
|
let mut mem: ProgramMemory = Default::default();
|
2023-08-28 14:58:24 -07:00
|
|
|
let mut engine = EngineConnection::new().await?;
|
2023-08-24 15:34:51 -07:00
|
|
|
let memory = execute(program, &mut mem, BodyType::Root, &mut engine)?;
|
|
|
|
|
|
|
|
Ok(memory)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_assign_two_variables() {
|
|
|
|
let ast = r#"const myVar = 5
|
|
|
|
const newVar = myVar + 1"#;
|
|
|
|
let memory = parse_execute(ast).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::json!(5),
|
|
|
|
memory.root.get("myVar").unwrap().get_json_value().unwrap()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::json!(6.0),
|
|
|
|
memory.root.get("newVar").unwrap().get_json_value().unwrap()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_angled_line_that_intersects() {
|
|
|
|
let ast_fn = |offset: &str| -> String {
|
|
|
|
format!(
|
|
|
|
r#"const part001 = startSketchAt([0, 0])
|
|
|
|
|> lineTo({{to:[2, 2], tag: "yo"}}, %)
|
|
|
|
|> lineTo([3, 1], %)
|
|
|
|
|> angledLineThatIntersects({{
|
|
|
|
angle: 180,
|
|
|
|
intersectTag: 'yo',
|
|
|
|
offset: {},
|
|
|
|
tag: "yo2"
|
|
|
|
}}, %)
|
|
|
|
const intersect = segEndX('yo2', part001)
|
|
|
|
show(part001)"#,
|
|
|
|
offset
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
let memory = parse_execute(&ast_fn("-1")).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::json!(1.0 + 2.0f64.sqrt()),
|
2023-08-29 14:12:48 -07:00
|
|
|
memory.root.get("intersect").unwrap().get_json_value().unwrap()
|
2023-08-24 15:34:51 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
let memory = parse_execute(&ast_fn("0")).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::json!(1.0000000000000002),
|
2023-08-29 14:12:48 -07:00
|
|
|
memory.root.get("intersect").unwrap().get_json_value().unwrap()
|
2023-08-24 15:34:51 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_fn_definitions() {
|
|
|
|
let ast = r#"const def = (x) => {
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
const ghi = (x) => {
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
const jkl = (x) => {
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
const hmm = (x) => {
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
|
|
|
|
const yo = 5 + 6
|
|
|
|
|
|
|
|
const abc = 3
|
|
|
|
const identifierGuy = 5
|
|
|
|
const part001 = startSketchAt([-1.2, 4.83])
|
|
|
|
|> line([2.8, 0], %)
|
|
|
|
|> angledLine([100 + 100, 3.01], %)
|
|
|
|
|> angledLine([abc, 3.02], %)
|
|
|
|
|> angledLine([def(yo), 3.03], %)
|
|
|
|
|> angledLine([ghi(2), 3.04], %)
|
|
|
|
|> angledLine([jkl(yo) + 2, 3.05], %)
|
|
|
|
|> close(%)
|
|
|
|
const yo2 = hmm([identifierGuy + 5])
|
|
|
|
show(part001)"#;
|
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_pipe_substitutions_unary() {
|
|
|
|
let ast = r#"const myVar = 3
|
|
|
|
const part001 = startSketchAt([0, 0])
|
|
|
|
|> line({ to: [3, 4], tag: 'seg01' }, %)
|
|
|
|
|> line([
|
|
|
|
min(segLen('seg01', %), myVar),
|
|
|
|
-legLen(segLen('seg01', %), myVar)
|
|
|
|
], %)
|
|
|
|
|
|
|
|
show(part001)"#;
|
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_pipe_substitutions() {
|
|
|
|
let ast = r#"const myVar = 3
|
|
|
|
const part001 = startSketchAt([0, 0])
|
|
|
|
|> line({ to: [3, 4], tag: 'seg01' }, %)
|
|
|
|
|> line([
|
|
|
|
min(segLen('seg01', %), myVar),
|
|
|
|
legLen(segLen('seg01', %), myVar)
|
|
|
|
], %)
|
|
|
|
|
2023-09-05 16:02:27 -07:00
|
|
|
show(part001)"#;
|
|
|
|
|
|
|
|
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 }
|
|
|
|
|
|
|
|
const part001 = startSketchAt([0, 0])
|
|
|
|
|> yLineTo(1, %)
|
|
|
|
|> xLine(3.84, %) // selection-range-7ish-before-this
|
|
|
|
|
|
|
|
const variableBelowShouldNotBeIncluded = 3
|
|
|
|
|
2023-08-24 15:34:51 -07:00
|
|
|
show(part001)"#;
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
const firstExtrude = startSketchAt([0,0])
|
|
|
|
|> line([0, l], %)
|
|
|
|
|> line([w, 0], %)
|
|
|
|
|> line([0, thing()], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(h, %)
|
|
|
|
|
|
|
|
show(firstExtrude)"#;
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
const firstExtrude = startSketchAt([0,0])
|
|
|
|
|> line([0, l], %)
|
|
|
|
|> line([w, 0], %)
|
|
|
|
|> line([0, thing(8)], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(h, %)
|
|
|
|
|
|
|
|
show(firstExtrude)"#;
|
|
|
|
|
|
|
|
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]
|
|
|
|
}
|
|
|
|
|
|
|
|
const firstExtrude = startSketchAt([0,0])
|
|
|
|
|> line([0, l], %)
|
|
|
|
|> line([w, 0], %)
|
|
|
|
|> line(thing(8), %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(h, %)
|
|
|
|
|
|
|
|
show(firstExtrude)"#;
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
const firstExtrude = startSketchAt([0,0])
|
|
|
|
|> line([0, l], %)
|
|
|
|
|> line([w, 0], %)
|
|
|
|
|> line([0, thing(8)], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(h, %)
|
|
|
|
|
|
|
|
show(firstExtrude)"#;
|
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn test_execute_with_function_sketch() {
|
|
|
|
let ast = r#"const box = (h, l, w) => {
|
|
|
|
const myBox = startSketchAt([0,0])
|
|
|
|
|> line([0, l], %)
|
|
|
|
|> line([w, 0], %)
|
|
|
|
|> line([0, -l], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(h, %)
|
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
const fnBox = box(3, 6, 10)
|
|
|
|
|
|
|
|
show(fnBox)"#;
|
|
|
|
|
|
|
|
parse_execute(ast).await.unwrap();
|
|
|
|
}
|
2023-08-24 15:34:51 -07:00
|
|
|
}
|