Divorce JSON and KCL (#4436)
Removes JSON from the KCL object model. Closes https://github.com/KittyCAD/modeling-app/issues/1130 -- it was filed on Nov 27 last year. Hopefully I close it before its one year anniversary. Changes: - Removed the UserVal variant from `enum KclValue`. That variant held JSON data. - Replaced it with several new variants like Number, String, Array (of KCL values), Object (where keys are String and values are KCL values) - Added a dedicated Sketch variant to KclValue. We used to have a variant like this, but I removed it as an experimental approach to fix this issue. Eventually I decided to undo it and use the approach of this PR instead. - Removed the `impl_from_arg_via_json` macro, which implemented conversion from KclValue to Rust types by matching the KclValue to its UserVal variant, grabbing the JSON, then deserializing that into the desired Rust type. - Instead, replaced it with manual conversion from KclValue to Rust types, using some convenience macros like `field!`
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4813
docs/kcl/std.json
4813
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
16
docs/kcl/types/KclNone.md
Normal file
16
docs/kcl/types/KclNone.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "KclNone"
|
||||
excerpt: "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application)."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -23,8 +23,110 @@ Any KCL value.
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `UserVal`| | No |
|
||||
| `value` |``| | No |
|
||||
| `type` |enum: `Uuid`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Bool`| | No |
|
||||
| `value` |`boolean`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Number`| | No |
|
||||
| `value` |`number`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Int`| | No |
|
||||
| `value` |`integer`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `String`| | No |
|
||||
| `value` |`string`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Array`| | No |
|
||||
| `value` |`[` [`KclValue`](/docs/kcl/types/KclValue) `]`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Object`| | No |
|
||||
| `value` |`object`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
@ -111,6 +213,38 @@ A face.
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`Sketch`](/docs/kcl/types/Sketch)| | No |
|
||||
| `value` |[`Sketch`](/docs/kcl/types/Sketch)| Any KCL value. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Sketches`| | No |
|
||||
| `value` |`[` [`Sketch`](/docs/kcl/types/Sketch) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
An solid is a collection of extrude surfaces.
|
||||
|
||||
@ -190,6 +324,23 @@ Data for an imported geometry.
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: [`KclNone`](/docs/kcl/types/KclNone)| | No |
|
||||
| `value` |[`KclNone`](/docs/kcl/types/KclNone)| Any KCL value. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -145,7 +145,7 @@ export function useCalc({
|
||||
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||
for (const { key, value } of availableVarInfo.variables) {
|
||||
const error = _programMem.set(key, {
|
||||
type: 'UserVal',
|
||||
type: 'String',
|
||||
value,
|
||||
__meta: [],
|
||||
})
|
||||
|
@ -89,9 +89,9 @@ export const processMemory = (programMemory: ProgramMemory) => {
|
||||
const processedMemory: any = {}
|
||||
for (const [key, val] of programMemory?.visibleEntries()) {
|
||||
if (
|
||||
(val.type === 'UserVal' && val.value.type === 'Sketch') ||
|
||||
val.type === 'Sketch' ||
|
||||
// @ts-ignore
|
||||
(val.type !== 'Function' && val.type !== 'UserVal')
|
||||
val.type !== 'Function'
|
||||
) {
|
||||
const sg = sketchFromKclValue(val, key)
|
||||
if (val.type === 'Solid') {
|
||||
@ -110,8 +110,6 @@ export const processMemory = (programMemory: ProgramMemory) => {
|
||||
processedMemory[key] = `__function(${(val as any)?.expression?.params
|
||||
?.map?.(({ identifier }: any) => identifier?.name || '')
|
||||
.join(', ')})__`
|
||||
} else {
|
||||
processedMemory[key] = val.value
|
||||
}
|
||||
}
|
||||
return processedMemory
|
||||
|
@ -18,8 +18,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
// @ts-ignore
|
||||
const sketch001 = execState.memory.get('mySketch001')
|
||||
expect(sketch001).toEqual({
|
||||
type: 'UserVal',
|
||||
__meta: [{ sourceRange: [46, 71, 0] }],
|
||||
type: 'Sketch',
|
||||
value: {
|
||||
type: 'Sketch',
|
||||
on: expect.any(Object),
|
||||
|
@ -58,7 +58,13 @@ const newVar = myVar + 1`
|
||||
`
|
||||
const mem = await exe(code)
|
||||
// geo is three js buffer geometry and is very bloated to have in tests
|
||||
const minusGeo = mem.get('mySketch')?.value?.paths
|
||||
const sk = mem.get('mySketch')
|
||||
expect(sk?.type).toEqual('Sketch')
|
||||
if (sk?.type !== 'Sketch') {
|
||||
return
|
||||
}
|
||||
|
||||
const minusGeo = sk?.value?.paths
|
||||
expect(minusGeo).toEqual([
|
||||
{
|
||||
type: 'ToPoint',
|
||||
@ -150,7 +156,7 @@ const newVar = myVar + 1`
|
||||
].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('mySk1')).toEqual({
|
||||
type: 'UserVal',
|
||||
type: 'Sketch',
|
||||
value: {
|
||||
type: 'Sketch',
|
||||
on: expect.any(Object),
|
||||
@ -215,7 +221,6 @@ const newVar = myVar + 1`
|
||||
id: expect.any(String),
|
||||
__meta: [{ sourceRange: [39, 63, 0] }],
|
||||
},
|
||||
__meta: [{ sourceRange: [39, 63, 0] }],
|
||||
})
|
||||
})
|
||||
it('execute array expression', async () => {
|
||||
@ -225,7 +230,7 @@ const newVar = myVar + 1`
|
||||
const mem = await exe(code)
|
||||
// TODO path to node is probably wrong here, zero indexes are not correct
|
||||
expect(mem.get('three')).toEqual({
|
||||
type: 'UserVal',
|
||||
type: 'Int',
|
||||
value: 3,
|
||||
__meta: [
|
||||
{
|
||||
@ -234,8 +239,17 @@ const newVar = myVar + 1`
|
||||
],
|
||||
})
|
||||
expect(mem.get('yo')).toEqual({
|
||||
type: 'UserVal',
|
||||
value: [1, '2', 3, 9],
|
||||
type: 'Array',
|
||||
value: [
|
||||
{ type: 'Int', value: 1, __meta: [{ sourceRange: [28, 29, 0] }] },
|
||||
{ type: 'String', value: '2', __meta: [{ sourceRange: [31, 34, 0] }] },
|
||||
{ type: 'Int', value: 3, __meta: [{ sourceRange: [14, 15, 0] }] },
|
||||
{
|
||||
type: 'Number',
|
||||
value: 9,
|
||||
__meta: [{ sourceRange: [43, 44, 0] }, { sourceRange: [47, 48, 0] }],
|
||||
},
|
||||
],
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [27, 49, 0],
|
||||
@ -253,8 +267,25 @@ const newVar = myVar + 1`
|
||||
].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('yo')).toEqual({
|
||||
type: 'UserVal',
|
||||
value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 },
|
||||
type: 'Object',
|
||||
value: {
|
||||
aStr: {
|
||||
type: 'String',
|
||||
value: 'str',
|
||||
__meta: [{ sourceRange: [34, 39, 0] }],
|
||||
},
|
||||
anum: { type: 'Int', value: 2, __meta: [{ sourceRange: [47, 48, 0] }] },
|
||||
identifier: {
|
||||
type: 'Int',
|
||||
value: 3,
|
||||
__meta: [{ sourceRange: [14, 15, 0] }],
|
||||
},
|
||||
binExp: {
|
||||
type: 'Number',
|
||||
value: 9,
|
||||
__meta: [{ sourceRange: [77, 78, 0] }, { sourceRange: [81, 82, 0] }],
|
||||
},
|
||||
},
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [27, 83, 0],
|
||||
@ -268,11 +299,11 @@ const newVar = myVar + 1`
|
||||
)
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')).toEqual({
|
||||
type: 'UserVal',
|
||||
type: 'String',
|
||||
value: '123',
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [41, 50, 0],
|
||||
sourceRange: [19, 24, 0],
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -356,7 +387,26 @@ describe('testing math operators', () => {
|
||||
it('with unaryExpression in ArrayExpression', async () => {
|
||||
const code = 'const myVar = [1,-legLen(5, 4)]'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toEqual([1, -3])
|
||||
expect(mem.get('myVar')?.value).toEqual([
|
||||
{
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [15, 16, 0],
|
||||
},
|
||||
],
|
||||
type: 'Int',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
__meta: [
|
||||
{
|
||||
sourceRange: [17, 30, 0],
|
||||
},
|
||||
],
|
||||
type: 'Number',
|
||||
value: -3,
|
||||
},
|
||||
])
|
||||
})
|
||||
it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => {
|
||||
const code = [
|
||||
|
@ -55,18 +55,13 @@ describe('Test KCL Samples from public Github repository', () => {
|
||||
})
|
||||
// Run through all of the files in the manifest json. This will allow us to be automatically updated
|
||||
// with the latest changes in github. We won't be hard coding the filenames
|
||||
it(
|
||||
'should run through all the files',
|
||||
async () => {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file: KclSampleFile = files[i]
|
||||
files.forEach((file: KclSampleFile) => {
|
||||
it(`should parse ${file.filename} without errors`, async () => {
|
||||
const code = await getKclSampleCodeFromGithub(file.filename)
|
||||
const parsed = parse(code)
|
||||
assert(!(parsed instanceof Error))
|
||||
}
|
||||
},
|
||||
files.length * 1000
|
||||
)
|
||||
}, 1000)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when performing enginelessExecutor', () => {
|
||||
|
@ -336,7 +336,7 @@ export class ProgramMemory {
|
||||
*/
|
||||
hasSketchOrSolid(): boolean {
|
||||
for (const node of this.visibleEntries().values()) {
|
||||
if (node.type === 'Solid' || node.value?.type === 'Sketch') {
|
||||
if (node.type === 'Solid' || node.type === 'Sketch') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ export function useCalculateKclExpression({
|
||||
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||
for (const { key, value } of availableVarInfo.variables) {
|
||||
const error = _programMem.set(key, {
|
||||
type: 'UserVal',
|
||||
type: 'String',
|
||||
value,
|
||||
__meta: [],
|
||||
})
|
||||
|
@ -27,7 +27,7 @@ pub use crate::ast::types::{
|
||||
use crate::{
|
||||
docs::StdLibFn,
|
||||
errors::KclError,
|
||||
executor::{ExecState, ExecutorContext, KclValue, Metadata, SourceRange, TagIdentifier, UserVal},
|
||||
executor::{ExecState, ExecutorContext, KclValue, Metadata, SourceRange, TagIdentifier},
|
||||
parser::PIPE_OPERATOR,
|
||||
std::kcl_stdlib::KclStdLibFn,
|
||||
};
|
||||
@ -59,6 +59,14 @@ pub struct Node<T> {
|
||||
pub module_id: ModuleId,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
source_range: SourceRange([self.start, self.end, self.module_id.0 as usize]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JsonSchema> schemars::JsonSchema for Node<T> {
|
||||
fn schema_name() -> String {
|
||||
T::schema_name()
|
||||
@ -1708,34 +1716,26 @@ impl Literal {
|
||||
|
||||
impl From<Node<Literal>> for KclValue {
|
||||
fn from(literal: Node<Literal>) -> Self {
|
||||
KclValue::UserVal(UserVal {
|
||||
value: JValue::from(literal.value.clone()),
|
||||
meta: vec![Metadata {
|
||||
source_range: literal.into(),
|
||||
}],
|
||||
})
|
||||
let meta = vec![literal.metadata()];
|
||||
match literal.inner.value {
|
||||
LiteralValue::IInteger(value) => KclValue::Int { value, meta },
|
||||
LiteralValue::Fractional(value) => KclValue::Number { value, meta },
|
||||
LiteralValue::String(value) => KclValue::String { value, meta },
|
||||
LiteralValue::Bool(value) => KclValue::Bool { value, meta },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Node<Literal>> for KclValue {
|
||||
fn from(literal: &Node<Literal>) -> Self {
|
||||
KclValue::UserVal(UserVal {
|
||||
value: JValue::from(literal.value.clone()),
|
||||
meta: vec![Metadata {
|
||||
source_range: literal.into(),
|
||||
}],
|
||||
})
|
||||
Self::from(literal.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BoxNode<Literal>> for KclValue {
|
||||
fn from(literal: &BoxNode<Literal>) -> Self {
|
||||
KclValue::UserVal(UserVal {
|
||||
value: JValue::from(literal.value.clone()),
|
||||
meta: vec![Metadata {
|
||||
source_range: literal.into(),
|
||||
}],
|
||||
})
|
||||
let b: &Node<Literal> = literal;
|
||||
Self::from(b)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3010,17 +3010,6 @@ impl ConstraintLevels {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn human_friendly_type(j: &JValue) -> &'static str {
|
||||
match j {
|
||||
JValue::Null => "null",
|
||||
JValue::Bool(_) => "boolean (true/false value)",
|
||||
JValue::Number(_) => "number",
|
||||
JValue::String(_) => "string (text)",
|
||||
JValue::Array(_) => "array (list)",
|
||||
JValue::Object(_) => "object",
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
@ -1,18 +1,21 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{
|
||||
human_friendly_type, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart,
|
||||
CallExpression, Expr, IfExpression, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node,
|
||||
ObjectExpression, TagDeclarator, UnaryExpression, UnaryOperator,
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, CallExpression, Expr,
|
||||
IfExpression, KclNone, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, ObjectExpression,
|
||||
TagDeclarator, UnaryExpression, UnaryOperator,
|
||||
};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{
|
||||
BodyType, ExecState, ExecutorContext, KclValue, Metadata, Sketch, SourceRange, StatementKind, TagEngineInfo,
|
||||
TagIdentifier, UserVal,
|
||||
BodyType, ExecState, ExecutorContext, KclValue, Metadata, SourceRange, StatementKind, TagEngineInfo,
|
||||
TagIdentifier,
|
||||
},
|
||||
std::FunctionKind,
|
||||
};
|
||||
use async_recursion::async_recursion;
|
||||
use serde_json::Value as JValue;
|
||||
|
||||
const FLOAT_TO_INT_MAX_DELTA: f64 = 0.01;
|
||||
|
||||
impl BinaryPart {
|
||||
#[async_recursion]
|
||||
@ -42,28 +45,21 @@ impl Node<MemberExpression> {
|
||||
}
|
||||
};
|
||||
|
||||
let array_json = array.get_json_value()?;
|
||||
let KclValue::Array { value: array, meta: _ } = array else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("MemberExpression array is not an array: {:?}", array),
|
||||
source_ranges: vec![self.clone().into()],
|
||||
}));
|
||||
};
|
||||
|
||||
if let serde_json::Value::Array(array) = array_json {
|
||||
if let Some(value) = array.get(index) {
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: value.clone(),
|
||||
meta: vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
Ok(value.to_owned())
|
||||
} else {
|
||||
Err(KclError::UndefinedValue(KclErrorDetails {
|
||||
message: format!("index {} not found in array", index),
|
||||
source_ranges: vec![self.clone().into()],
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("MemberExpression array is not an array: {:?}", array),
|
||||
source_ranges: vec![self.clone().into()],
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
|
||||
@ -77,18 +73,11 @@ impl Node<MemberExpression> {
|
||||
}
|
||||
};
|
||||
|
||||
let object_json = object.get_json_value()?;
|
||||
|
||||
// Check the property and object match -- e.g. ints for arrays, strs for objects.
|
||||
match (object_json, property) {
|
||||
(JValue::Object(map), Property::String(property)) => {
|
||||
match (object, property) {
|
||||
(KclValue::Object { value: map, meta: _ }, Property::String(property)) => {
|
||||
if let Some(value) = map.get(&property) {
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: value.clone(),
|
||||
meta: vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
Ok(value.to_owned())
|
||||
} else {
|
||||
Err(KclError::UndefinedValue(KclErrorDetails {
|
||||
message: format!("Property '{property}' not found in object"),
|
||||
@ -96,22 +85,20 @@ impl Node<MemberExpression> {
|
||||
}))
|
||||
}
|
||||
}
|
||||
(JValue::Object(_), p) => Err(KclError::Semantic(KclErrorDetails {
|
||||
(KclValue::Object { .. }, p) => {
|
||||
let t = p.type_name();
|
||||
let article = article_for(t);
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Only strings can be used as the property of an object, but you're using a {}",
|
||||
p.type_name()
|
||||
"Only strings can be used as the property of an object, but you're using {article} {t}",
|
||||
),
|
||||
source_ranges: vec![self.clone().into()],
|
||||
})),
|
||||
(JValue::Array(arr), Property::Number(index)) => {
|
||||
let value_of_arr: Option<&JValue> = arr.get(index);
|
||||
if let Some(value) = value_of_arr {
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: value.clone(),
|
||||
meta: vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
}
|
||||
(KclValue::Array { value: arr, meta: _ }, Property::Number(index)) => {
|
||||
let value_of_arr = arr.get(index);
|
||||
if let Some(value) = value_of_arr {
|
||||
Ok(value.to_owned())
|
||||
} else {
|
||||
Err(KclError::UndefinedValue(KclErrorDetails {
|
||||
message: format!("The array doesn't have any item at index {index}"),
|
||||
@ -119,17 +106,36 @@ impl Node<MemberExpression> {
|
||||
}))
|
||||
}
|
||||
}
|
||||
(JValue::Array(_), p) => Err(KclError::Semantic(KclErrorDetails {
|
||||
(KclValue::Array { .. }, p) => {
|
||||
let t = p.type_name();
|
||||
let article = article_for(t);
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Only integers >= 0 can be used as the index of an array, but you're using a {}",
|
||||
p.type_name()
|
||||
"Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",
|
||||
),
|
||||
source_ranges: vec![self.clone().into()],
|
||||
})),
|
||||
}))
|
||||
}
|
||||
(KclValue::Solid(solid), Property::String(prop)) if prop == "sketch" => Ok(KclValue::Sketch {
|
||||
value: Box::new(solid.sketch),
|
||||
}),
|
||||
(KclValue::Sketch { value: sk }, Property::String(prop)) if prop == "tags" => Ok(KclValue::Object {
|
||||
meta: vec![Metadata {
|
||||
source_range: SourceRange::from(self.clone()),
|
||||
}],
|
||||
value: sk
|
||||
.tags
|
||||
.iter()
|
||||
.map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned()))))
|
||||
.collect(),
|
||||
}),
|
||||
(being_indexed, _) => {
|
||||
let t = human_friendly_type(&being_indexed);
|
||||
let t = being_indexed.human_friendly_type();
|
||||
let article = article_for(t);
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Only arrays and objects can be indexed, but you're trying to index a {t}"),
|
||||
message: format!(
|
||||
"Only arrays and objects can be indexed, but you're trying to index {article} {t}"
|
||||
),
|
||||
source_ranges: vec![self.clone().into()],
|
||||
}))
|
||||
}
|
||||
@ -140,81 +146,134 @@ impl Node<MemberExpression> {
|
||||
impl Node<BinaryExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
let left_json_value = self.left.get_result(exec_state, ctx).await?.get_json_value()?;
|
||||
let right_json_value = self.right.get_result(exec_state, ctx).await?.get_json_value()?;
|
||||
let left_value = self.left.get_result(exec_state, ctx).await?;
|
||||
let right_value = self.right.get_result(exec_state, ctx).await?;
|
||||
let mut meta = left_value.metadata();
|
||||
meta.extend(right_value.metadata());
|
||||
|
||||
// First check if we are doing string concatenation.
|
||||
if self.operator == BinaryOperator::Add {
|
||||
if let (Some(left), Some(right)) = (
|
||||
parse_json_value_as_string(&left_json_value),
|
||||
parse_json_value_as_string(&right_json_value),
|
||||
) {
|
||||
let value = serde_json::Value::String(format!("{}{}", left, right));
|
||||
return Ok(KclValue::UserVal(UserVal {
|
||||
value,
|
||||
meta: vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}));
|
||||
if let (KclValue::String { value: left, meta: _ }, KclValue::String { value: right, meta: _ }) =
|
||||
(&left_value, &right_value)
|
||||
{
|
||||
return Ok(KclValue::String {
|
||||
value: format!("{}{}", left, right),
|
||||
meta,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let left = parse_json_number_as_f64(&left_json_value, self.left.clone().into())?;
|
||||
let right = parse_json_number_as_f64(&right_json_value, self.right.clone().into())?;
|
||||
let left = parse_number_as_f64(&left_value, self.left.clone().into())?;
|
||||
let right = parse_number_as_f64(&right_value, self.right.clone().into())?;
|
||||
|
||||
let value: serde_json::Value = match self.operator {
|
||||
BinaryOperator::Add => (left + right).into(),
|
||||
BinaryOperator::Sub => (left - right).into(),
|
||||
BinaryOperator::Mul => (left * right).into(),
|
||||
BinaryOperator::Div => (left / right).into(),
|
||||
BinaryOperator::Mod => (left % right).into(),
|
||||
BinaryOperator::Pow => (left.powf(right)).into(),
|
||||
BinaryOperator::Eq => (left == right).into(),
|
||||
BinaryOperator::Neq => (left != right).into(),
|
||||
BinaryOperator::Gt => (left > right).into(),
|
||||
BinaryOperator::Gte => (left >= right).into(),
|
||||
BinaryOperator::Lt => (left < right).into(),
|
||||
BinaryOperator::Lte => (left <= right).into(),
|
||||
let value = match self.operator {
|
||||
BinaryOperator::Add => KclValue::Number {
|
||||
value: left + right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Sub => KclValue::Number {
|
||||
value: left - right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Mul => KclValue::Number {
|
||||
value: left * right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Div => KclValue::Number {
|
||||
value: left / right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Mod => KclValue::Number {
|
||||
value: left % right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Pow => KclValue::Number {
|
||||
value: left.powf(right),
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Neq => KclValue::Bool {
|
||||
value: left != right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Gt => KclValue::Bool {
|
||||
value: left > right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Gte => KclValue::Bool {
|
||||
value: left >= right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Lt => KclValue::Bool {
|
||||
value: left < right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Lte => KclValue::Bool {
|
||||
value: left <= right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::Eq => KclValue::Bool {
|
||||
value: left == right,
|
||||
meta,
|
||||
},
|
||||
};
|
||||
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value,
|
||||
meta: vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<UnaryExpression> {
|
||||
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
if self.operator == UnaryOperator::Not {
|
||||
let value = self.argument.get_result(exec_state, ctx).await?.get_json_value()?;
|
||||
let Some(bool_value) = json_as_bool(&value) else {
|
||||
let value = self.argument.get_result(exec_state, ctx).await?;
|
||||
let KclValue::Bool {
|
||||
value: bool_value,
|
||||
meta: _,
|
||||
} = value
|
||||
else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Cannot apply unary operator ! to non-boolean value: {}", value),
|
||||
message: format!(
|
||||
"Cannot apply unary operator ! to non-boolean value: {}",
|
||||
value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.into()],
|
||||
}));
|
||||
};
|
||||
let negated = !bool_value;
|
||||
return Ok(KclValue::UserVal(UserVal {
|
||||
value: serde_json::Value::Bool(negated),
|
||||
meta: vec![Metadata {
|
||||
let meta = vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}));
|
||||
}];
|
||||
let negated = KclValue::Bool {
|
||||
value: !bool_value,
|
||||
meta,
|
||||
};
|
||||
|
||||
return Ok(negated);
|
||||
}
|
||||
|
||||
let num = parse_json_number_as_f64(
|
||||
&self.argument.get_result(exec_state, ctx).await?.get_json_value()?,
|
||||
self.into(),
|
||||
)?;
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: (-(num)).into(),
|
||||
meta: vec![Metadata {
|
||||
let value = &self.argument.get_result(exec_state, ctx).await?;
|
||||
match value {
|
||||
KclValue::Number { value, meta: _ } => {
|
||||
let meta = vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
}];
|
||||
Ok(KclValue::Number { value: -value, meta })
|
||||
}
|
||||
KclValue::Int { value, meta: _ } => {
|
||||
let meta = vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}];
|
||||
Ok(KclValue::Number {
|
||||
value: (-value) as f64,
|
||||
meta,
|
||||
})
|
||||
}
|
||||
_ => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"You can only negate numbers, but this is a {}",
|
||||
value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.into()],
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,13 +384,10 @@ impl Node<CallExpression> {
|
||||
// TODO: This could probably be done in a better way, but as of now this was my only idea
|
||||
// and it works.
|
||||
match result {
|
||||
KclValue::UserVal(ref mut uval) => {
|
||||
uval.mutate(|sketch: &mut Sketch| {
|
||||
KclValue::Sketch { value: ref mut sketch } => {
|
||||
for (_, tag) in sketch.tags.iter() {
|
||||
exec_state.memory.update_tag(&tag.value, tag.clone())?;
|
||||
}
|
||||
Ok::<_, KclError>(())
|
||||
})?;
|
||||
}
|
||||
KclValue::Solid(ref mut solid) => {
|
||||
for value in &solid.value {
|
||||
@ -425,10 +481,10 @@ impl Node<CallExpression> {
|
||||
} else {
|
||||
fn_memory.add(
|
||||
¶m.identifier.name,
|
||||
KclValue::UserVal(UserVal {
|
||||
value: serde_json::value::Value::Null,
|
||||
meta: Default::default(),
|
||||
}),
|
||||
KclValue::KclNone {
|
||||
value: KclNone::new(),
|
||||
meta: vec![self.into()],
|
||||
},
|
||||
param.identifier.clone().into(),
|
||||
)?;
|
||||
}
|
||||
@ -531,15 +587,13 @@ impl Node<ArrayExpression> {
|
||||
.execute_expr(element, exec_state, &metadata, StatementKind::Expression)
|
||||
.await?;
|
||||
|
||||
results.push(value.get_json_value()?);
|
||||
results.push(value);
|
||||
}
|
||||
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: results.into(),
|
||||
meta: vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
Ok(KclValue::Array {
|
||||
value: results,
|
||||
meta: vec![self.into()],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -549,15 +603,19 @@ impl Node<ArrayRangeExpression> {
|
||||
let metadata = Metadata::from(&self.start_element);
|
||||
let start = ctx
|
||||
.execute_expr(&self.start_element, exec_state, &metadata, StatementKind::Expression)
|
||||
.await?
|
||||
.get_json_value()?;
|
||||
let start = parse_json_number_as_i64(&start, (&self.start_element).into())?;
|
||||
.await?;
|
||||
let start = start.as_int().ok_or(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![self.into()],
|
||||
message: format!("Expected int but found {}", start.human_friendly_type()),
|
||||
}))?;
|
||||
let metadata = Metadata::from(&self.end_element);
|
||||
let end = ctx
|
||||
.execute_expr(&self.end_element, exec_state, &metadata, StatementKind::Expression)
|
||||
.await?
|
||||
.get_json_value()?;
|
||||
let end = parse_json_number_as_i64(&end, (&self.end_element).into())?;
|
||||
.await?;
|
||||
let end = end.as_int().ok_or(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![self.into()],
|
||||
message: format!("Expected int but found {}", end.human_friendly_type()),
|
||||
}))?;
|
||||
|
||||
if end < start {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -567,91 +625,73 @@ impl Node<ArrayRangeExpression> {
|
||||
}
|
||||
|
||||
let range: Vec<_> = if self.end_inclusive {
|
||||
(start..=end).map(JValue::from).collect()
|
||||
(start..=end).collect()
|
||||
} else {
|
||||
(start..end).map(JValue::from).collect()
|
||||
(start..end).collect()
|
||||
};
|
||||
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: range.into(),
|
||||
meta: vec![Metadata {
|
||||
let meta = vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
}];
|
||||
Ok(KclValue::Array {
|
||||
value: range
|
||||
.into_iter()
|
||||
.map(|num| KclValue::Int {
|
||||
value: num,
|
||||
meta: meta.clone(),
|
||||
})
|
||||
.collect(),
|
||||
meta,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<ObjectExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
let mut object = serde_json::Map::new();
|
||||
let mut object = HashMap::with_capacity(self.properties.len());
|
||||
for property in &self.properties {
|
||||
let metadata = Metadata::from(&property.value);
|
||||
let result = ctx
|
||||
.execute_expr(&property.value, exec_state, &metadata, StatementKind::Expression)
|
||||
.await?;
|
||||
|
||||
object.insert(property.key.name.clone(), result.get_json_value()?);
|
||||
object.insert(property.key.name.clone(), result);
|
||||
}
|
||||
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: object.into(),
|
||||
Ok(KclValue::Object {
|
||||
value: object,
|
||||
meta: vec![Metadata {
|
||||
source_range: self.into(),
|
||||
}],
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_json_number_as_i64(j: &serde_json::Value, source_range: SourceRange) -> Result<i64, KclError> {
|
||||
if let serde_json::Value::Number(n) = &j {
|
||||
n.as_i64().ok_or_else(|| {
|
||||
KclError::Syntax(KclErrorDetails {
|
||||
source_ranges: vec![source_range],
|
||||
message: format!("Invalid integer: {}", j),
|
||||
})
|
||||
})
|
||||
fn article_for(s: &str) -> &'static str {
|
||||
if s.starts_with(['a', 'e', 'i', 'o', 'u']) {
|
||||
"an"
|
||||
} else {
|
||||
Err(KclError::Syntax(KclErrorDetails {
|
||||
source_ranges: vec![source_range],
|
||||
message: format!("Invalid integer: {}", j),
|
||||
}))
|
||||
"a"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_json_number_as_f64(j: &serde_json::Value, source_range: SourceRange) -> Result<f64, KclError> {
|
||||
if let serde_json::Value::Number(n) = &j {
|
||||
n.as_f64().ok_or_else(|| {
|
||||
KclError::Syntax(KclErrorDetails {
|
||||
source_ranges: vec![source_range],
|
||||
message: format!("Invalid number: {}", j),
|
||||
})
|
||||
})
|
||||
pub fn parse_number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<f64, KclError> {
|
||||
if let KclValue::Number { value: n, .. } = &v {
|
||||
Ok(*n)
|
||||
} else if let KclValue::Int { value: n, .. } = &v {
|
||||
Ok(*n as f64)
|
||||
} else {
|
||||
Err(KclError::Syntax(KclErrorDetails {
|
||||
source_ranges: vec![source_range],
|
||||
message: format!("Invalid number: {}", j),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_json_value_as_string(j: &serde_json::Value) -> Option<String> {
|
||||
if let serde_json::Value::String(n) = &j {
|
||||
Some(n.clone())
|
||||
let actual_type = v.human_friendly_type();
|
||||
let article = if actual_type.starts_with(['a', 'e', 'i', 'o', 'u']) {
|
||||
"an"
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// JSON value as bool. If it isn't a bool, returns None.
|
||||
pub fn json_as_bool(j: &serde_json::Value) -> Option<bool> {
|
||||
match j {
|
||||
JValue::Null => None,
|
||||
JValue::Bool(b) => Some(*b),
|
||||
JValue::Number(_) => None,
|
||||
JValue::String(_) => None,
|
||||
JValue::Array(_) => None,
|
||||
JValue::Object(_) => None,
|
||||
"a"
|
||||
};
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![source_range],
|
||||
message: format!("Expected a number, but found {article} {actual_type}",),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -724,15 +764,7 @@ impl Property {
|
||||
} else {
|
||||
// Actually evaluate memory to compute the property.
|
||||
let prop = exec_state.memory.get(name, property_src)?;
|
||||
let KclValue::UserVal(prop) = prop else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: property_sr,
|
||||
message: format!(
|
||||
"{name} is not a valid property/index, you can only use a string or int (>= 0) here",
|
||||
),
|
||||
}));
|
||||
};
|
||||
jvalue_to_prop(&prop.value, property_sr, name)
|
||||
jvalue_to_prop(prop, property_sr, name)
|
||||
}
|
||||
}
|
||||
LiteralIdentifier::Literal(literal) => {
|
||||
@ -759,35 +791,37 @@ impl Property {
|
||||
}
|
||||
}
|
||||
|
||||
fn jvalue_to_prop(value: &JValue, property_sr: Vec<SourceRange>, name: &str) -> Result<Property, KclError> {
|
||||
fn jvalue_to_prop(value: &KclValue, property_sr: Vec<SourceRange>, name: &str) -> Result<Property, KclError> {
|
||||
let make_err = |message: String| {
|
||||
Err::<Property, _>(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: property_sr,
|
||||
message,
|
||||
}))
|
||||
};
|
||||
const MUST_BE_POSINT: &str = "indices must be whole positive numbers";
|
||||
const TRY_INT: &str = "try using the int() function to make this a whole number";
|
||||
match value {
|
||||
JValue::Number(ref num) => {
|
||||
let maybe_uint = num.as_u64().and_then(|x| usize::try_from(x).ok());
|
||||
if let Some(uint) = maybe_uint {
|
||||
KclValue::Int { value:num, meta: _ } => {
|
||||
let maybe_int: Result<usize, _> = (*num).try_into();
|
||||
if let Ok(uint) = maybe_int {
|
||||
Ok(Property::Number(uint))
|
||||
} else if let Some(iint) = num.as_i64() {
|
||||
make_err(format!("'{iint}' is not a valid index, {MUST_BE_POSINT}"))
|
||||
} else if let Some(fnum) = num.as_f64() {
|
||||
if fnum < 0.0 {
|
||||
make_err(format!("'{fnum}' is not a valid index, {MUST_BE_POSINT}"))
|
||||
} else if fnum.fract() == 0.0 {
|
||||
make_err(format!("'{fnum:.1}' is stored as a fractional number but indices must be whole numbers, {TRY_INT}"))
|
||||
}
|
||||
else {
|
||||
make_err(format!("'{num}' is negative, so you can't index an array with it"))
|
||||
}
|
||||
}
|
||||
KclValue::Number{value: num, meta:_} => {
|
||||
let num = *num;
|
||||
if num < 0.0 {
|
||||
return make_err(format!("'{num}' is negative, so you can't index an array with it"))
|
||||
}
|
||||
let nearest_int = num.round();
|
||||
let delta = num-nearest_int;
|
||||
if delta < FLOAT_TO_INT_MAX_DELTA {
|
||||
Ok(Property::Number(nearest_int as usize))
|
||||
} else {
|
||||
make_err(format!("'{fnum}' is not a valid index, {MUST_BE_POSINT}, {TRY_INT}"))
|
||||
}
|
||||
} else {
|
||||
make_err(format!("'{num}' is not a valid index, {MUST_BE_POSINT}"))
|
||||
make_err(format!("'{num}' is not an integer, so you can't index an array with it"))
|
||||
}
|
||||
}
|
||||
JValue::String(ref x) => Ok(Property::String(x.to_owned())),
|
||||
KclValue::String{value: x, meta:_} => Ok(Property::String(x.to_owned())),
|
||||
_ => {
|
||||
make_err(format!("{name} is not a valid property/index, you can only use a string to get the property of an object, or an int (>= 0) to get an item in an array"))
|
||||
}
|
||||
|
@ -4,10 +4,7 @@ use databake::*;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
ast::types::ConstraintLevel,
|
||||
executor::{KclValue, UserVal},
|
||||
};
|
||||
use crate::{ast::types::ConstraintLevel, executor::KclValue};
|
||||
|
||||
use super::Node;
|
||||
|
||||
@ -16,7 +13,7 @@ const KCL_NONE_ID: &str = "KCL_NONE_ID";
|
||||
/// KCL value for an optional parameter which was not given an argument.
|
||||
/// (remember, parameters are in the function declaration,
|
||||
/// arguments are in the function call/application).
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake, Default)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake, Default, Copy)]
|
||||
#[databake(path = kcl_lib::ast::types)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
@ -58,19 +55,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&KclNone> for UserVal {
|
||||
fn from(none: &KclNone) -> Self {
|
||||
UserVal {
|
||||
value: serde_json::to_value(none).expect("can always serialize a None"),
|
||||
meta: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&KclNone> for KclValue {
|
||||
fn from(none: &KclNone) -> Self {
|
||||
let val = UserVal::from(none);
|
||||
KclValue::UserVal(val)
|
||||
KclValue::KclNone {
|
||||
value: *none,
|
||||
meta: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@ use kittycad_modeling_cmds::length_unit::LengthUnit;
|
||||
use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JValue;
|
||||
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
|
||||
|
||||
type Point2D = kcmc::shared::Point2d<f64>;
|
||||
@ -27,8 +26,8 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
||||
|
||||
use crate::{
|
||||
ast::types::{
|
||||
human_friendly_type, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, ModuleId, Node, NodeRef,
|
||||
Program, TagDeclarator, TagNode,
|
||||
BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, ModuleId, Node, NodeRef, Program, TagDeclarator,
|
||||
TagNode,
|
||||
},
|
||||
engine::{EngineManager, ExecutionKind},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
@ -201,33 +200,18 @@ impl Environment {
|
||||
Self {
|
||||
// Prelude
|
||||
bindings: HashMap::from([
|
||||
(
|
||||
"ZERO".to_string(),
|
||||
KclValue::UserVal(UserVal {
|
||||
value: serde_json::Value::Number(serde_json::value::Number::from(0)),
|
||||
meta: Default::default(),
|
||||
}),
|
||||
),
|
||||
("ZERO".to_string(), KclValue::from_number(0.0, Default::default())),
|
||||
(
|
||||
"QUARTER_TURN".to_string(),
|
||||
KclValue::UserVal(UserVal {
|
||||
value: serde_json::Value::Number(serde_json::value::Number::from(90)),
|
||||
meta: Default::default(),
|
||||
}),
|
||||
KclValue::from_number(90.0, Default::default()),
|
||||
),
|
||||
(
|
||||
"HALF_TURN".to_string(),
|
||||
KclValue::UserVal(UserVal {
|
||||
value: serde_json::Value::Number(serde_json::value::Number::from(180)),
|
||||
meta: Default::default(),
|
||||
}),
|
||||
KclValue::from_number(180.0, Default::default()),
|
||||
),
|
||||
(
|
||||
"THREE_QUARTER_TURN".to_string(),
|
||||
KclValue::UserVal(UserVal {
|
||||
value: serde_json::Value::Number(serde_json::value::Number::from(270)),
|
||||
meta: Default::default(),
|
||||
}),
|
||||
KclValue::from_number(270.0, Default::default()),
|
||||
),
|
||||
]),
|
||||
parent: None,
|
||||
@ -264,22 +248,15 @@ impl Environment {
|
||||
}
|
||||
|
||||
for (_, val) in self.bindings.iter_mut() {
|
||||
let KclValue::UserVal(v) = val else { continue };
|
||||
let meta = v.meta.clone();
|
||||
let maybe_sg: Result<Sketch, _> = serde_json::from_value(v.value.clone());
|
||||
let Ok(mut sketch) = maybe_sg else {
|
||||
continue;
|
||||
};
|
||||
let KclValue::Sketch { value } = val else { continue };
|
||||
let mut sketch = value.to_owned();
|
||||
|
||||
if sketch.original_id == sg.original_id {
|
||||
for tag in sg.tags.iter() {
|
||||
sketch.tags.insert(tag.0.clone(), tag.1.clone());
|
||||
}
|
||||
}
|
||||
*val = KclValue::UserVal(UserVal {
|
||||
meta,
|
||||
value: serde_json::to_value(sketch).expect("can always turn Sketch into JSON"),
|
||||
});
|
||||
*val = KclValue::Sketch { value: sketch };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -360,12 +337,52 @@ impl IdGenerator {
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum KclValue {
|
||||
UserVal(UserVal),
|
||||
Uuid {
|
||||
value: ::uuid::Uuid,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
Bool {
|
||||
value: bool,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
Number {
|
||||
value: f64,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
Int {
|
||||
value: i64,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
String {
|
||||
value: String,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
Array {
|
||||
value: Vec<KclValue>,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
Object {
|
||||
value: HashMap<String, KclValue>,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
TagIdentifier(Box<TagIdentifier>),
|
||||
TagDeclarator(crate::ast::types::BoxNode<TagDeclarator>),
|
||||
Plane(Box<Plane>),
|
||||
Face(Box<Face>),
|
||||
|
||||
Sketch {
|
||||
value: Box<Sketch>,
|
||||
},
|
||||
Sketches {
|
||||
value: Vec<Box<Sketch>>,
|
||||
},
|
||||
Solid(Box<Solid>),
|
||||
Solids {
|
||||
value: Vec<Box<Solid>>,
|
||||
@ -380,31 +397,55 @@ pub enum KclValue {
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
KclNone {
|
||||
value: KclNone,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
}
|
||||
|
||||
impl KclValue {
|
||||
pub(crate) fn new_user_val<T: Serialize>(meta: Vec<Metadata>, val: T) -> Self {
|
||||
Self::UserVal(UserVal::new(meta, val))
|
||||
pub(crate) fn metadata(&self) -> Vec<Metadata> {
|
||||
match self {
|
||||
KclValue::Uuid { value: _, meta } => meta.clone(),
|
||||
KclValue::Bool { value: _, meta } => meta.clone(),
|
||||
KclValue::Number { value: _, meta } => meta.clone(),
|
||||
KclValue::Int { value: _, meta } => meta.clone(),
|
||||
KclValue::String { value: _, meta } => meta.clone(),
|
||||
KclValue::Array { value: _, meta } => meta.clone(),
|
||||
KclValue::Object { value: _, meta } => meta.clone(),
|
||||
KclValue::TagIdentifier(x) => x.meta.clone(),
|
||||
KclValue::TagDeclarator(x) => vec![x.metadata()],
|
||||
KclValue::Plane(x) => x.meta.clone(),
|
||||
KclValue::Face(x) => x.meta.clone(),
|
||||
KclValue::Sketch { value } => value.meta.clone(),
|
||||
KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
||||
KclValue::Solid(x) => x.meta.clone(),
|
||||
KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
||||
KclValue::ImportedGeometry(x) => x.meta.clone(),
|
||||
KclValue::Function { meta, .. } => meta.clone(),
|
||||
KclValue::KclNone { meta, .. } => meta.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
|
||||
match self {
|
||||
KclValue::Solid(e) => Ok(SolidSet::Solid(e.clone())),
|
||||
KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
|
||||
KclValue::UserVal(value) => {
|
||||
let value = value.value.clone();
|
||||
match value {
|
||||
JValue::Null | JValue::Bool(_) | JValue::Number(_) | JValue::String(_) => Err(anyhow::anyhow!(
|
||||
"Failed to deserialize solid set from JSON {}",
|
||||
human_friendly_type(&value)
|
||||
)),
|
||||
JValue::Array(_) => serde_json::from_value::<Vec<Box<Solid>>>(value)
|
||||
.map(SolidSet::from)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to deserialize array of solids from JSON: {}", e)),
|
||||
JValue::Object(_) => serde_json::from_value::<Box<Solid>>(value)
|
||||
.map(SolidSet::from)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to deserialize solid from JSON: {}", e)),
|
||||
}
|
||||
KclValue::Array { value, .. } => {
|
||||
let solids: Vec<_> = value
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| {
|
||||
v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"expected this array to only contain solids, but element {i} was actually {}",
|
||||
v.human_friendly_type()
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(SolidSet::Solids(solids))
|
||||
}
|
||||
_ => anyhow::bail!("Not a solid or solids: {:?}", self),
|
||||
}
|
||||
@ -414,43 +455,44 @@ impl KclValue {
|
||||
/// on for program logic.
|
||||
pub(crate) fn human_friendly_type(&self) -> &'static str {
|
||||
match self {
|
||||
KclValue::UserVal(u) => human_friendly_type(&u.value),
|
||||
KclValue::Uuid { .. } => "Unique ID (uuid)",
|
||||
KclValue::TagDeclarator(_) => "TagDeclarator",
|
||||
KclValue::TagIdentifier(_) => "TagIdentifier",
|
||||
KclValue::Solid(_) => "Solid",
|
||||
KclValue::Solids { .. } => "Solids",
|
||||
KclValue::Sketch { .. } => "Sketch",
|
||||
KclValue::Sketches { .. } => "Sketches",
|
||||
KclValue::ImportedGeometry(_) => "ImportedGeometry",
|
||||
KclValue::Function { .. } => "Function",
|
||||
KclValue::Plane(_) => "Plane",
|
||||
KclValue::Face(_) => "Face",
|
||||
KclValue::Bool { .. } => "boolean (true/false value)",
|
||||
KclValue::Number { .. } => "number",
|
||||
KclValue::Int { .. } => "integer",
|
||||
KclValue::String { .. } => "string (text)",
|
||||
KclValue::Array { .. } => "array (list)",
|
||||
KclValue::Object { .. } => "object",
|
||||
KclValue::KclNone { .. } => "None",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_function(&self) -> bool {
|
||||
match self {
|
||||
KclValue::UserVal(..)
|
||||
| KclValue::TagIdentifier(..)
|
||||
| KclValue::TagDeclarator(..)
|
||||
| KclValue::Plane(..)
|
||||
| KclValue::Face(..)
|
||||
| KclValue::Solid(..)
|
||||
| KclValue::Solids { .. }
|
||||
| KclValue::ImportedGeometry(..) => false,
|
||||
KclValue::Function { .. } => true,
|
||||
}
|
||||
matches!(self, KclValue::Function { .. })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SketchSet> for KclValue {
|
||||
fn from(sg: SketchSet) -> Self {
|
||||
KclValue::UserVal(UserVal::new(sg.meta(), sg))
|
||||
match sg {
|
||||
SketchSet::Sketch(value) => KclValue::Sketch { value },
|
||||
SketchSet::Sketches(value) => KclValue::Sketches { value },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Box<Sketch>>> for KclValue {
|
||||
fn from(sg: Vec<Box<Sketch>>) -> Self {
|
||||
let meta = sg.iter().flat_map(|sg| sg.meta.clone()).collect();
|
||||
KclValue::UserVal(UserVal::new(meta, sg))
|
||||
KclValue::Sketches { value: sg }
|
||||
}
|
||||
}
|
||||
|
||||
@ -815,52 +857,6 @@ pub enum PlaneType {
|
||||
Custom,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub struct UserVal {
|
||||
#[ts(type = "any")]
|
||||
pub value: serde_json::Value,
|
||||
#[serde(rename = "__meta")]
|
||||
pub meta: Vec<Metadata>,
|
||||
}
|
||||
|
||||
impl UserVal {
|
||||
pub fn new<T: serde::Serialize>(meta: Vec<Metadata>, val: T) -> Self {
|
||||
Self {
|
||||
meta,
|
||||
value: serde_json::to_value(val).expect("all KCL values should be compatible with JSON"),
|
||||
}
|
||||
}
|
||||
|
||||
/// If the UserVal matches the type `T`, return it.
|
||||
pub fn get<T: serde::de::DeserializeOwned>(&self) -> Option<(T, Vec<Metadata>)> {
|
||||
let meta = self.meta.clone();
|
||||
// TODO: This clone might cause performance problems, it'll happen a lot.
|
||||
let res: Result<T, _> = serde_json::from_value(self.value.clone());
|
||||
if let Ok(t) = res {
|
||||
Some((t, meta))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If the UserVal matches the type `T`, then mutate it via the given closure.
|
||||
/// If the closure returns Err, the mutation won't be applied.
|
||||
pub fn mutate<T, F, E>(&mut self, mutate: F) -> Result<(), E>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + Serialize,
|
||||
F: FnOnce(&mut T) -> Result<(), E>,
|
||||
{
|
||||
let Some((mut val, meta)) = self.get::<T>() else {
|
||||
return Ok(());
|
||||
};
|
||||
mutate(&mut val)?;
|
||||
*self = Self::new(meta, val);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
@ -922,108 +918,177 @@ pub type MemoryFunction =
|
||||
impl From<KclValue> for Vec<SourceRange> {
|
||||
fn from(item: KclValue) -> Self {
|
||||
match item {
|
||||
KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::TagDeclarator(t) => vec![(&t).into()],
|
||||
KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Solid(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Solids { value } => value
|
||||
.iter()
|
||||
.flat_map(|eg| eg.meta.iter().map(|m| m.source_range))
|
||||
.collect(),
|
||||
KclValue::ImportedGeometry(i) => i.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Plane(p) => p.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Face(f) => f.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::TagDeclarator(t) => vec![SourceRange([t.start, t.end, t.module_id.0 as usize])],
|
||||
KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
|
||||
KclValue::Solid(e) => to_vec_sr(&e.meta),
|
||||
KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
||||
KclValue::Sketch { value } => to_vec_sr(&value.meta),
|
||||
KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
||||
KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
|
||||
KclValue::Function { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Plane(p) => to_vec_sr(&p.meta),
|
||||
KclValue::Face(f) => to_vec_sr(&f.meta),
|
||||
KclValue::Bool { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Number { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Int { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::String { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Array { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Object { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_vec_sr(meta: &[Metadata]) -> Vec<SourceRange> {
|
||||
meta.iter().map(|m| m.source_range).collect()
|
||||
}
|
||||
|
||||
impl From<&KclValue> for Vec<SourceRange> {
|
||||
fn from(item: &KclValue) -> Self {
|
||||
match item {
|
||||
KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::TagDeclarator(ref t) => vec![t.into()],
|
||||
KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Solid(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Solids { value } => value
|
||||
.iter()
|
||||
.flat_map(|eg| eg.meta.iter().map(|m| m.source_range))
|
||||
.collect(),
|
||||
KclValue::ImportedGeometry(i) => i.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Plane(p) => p.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::Face(f) => f.meta.iter().map(|m| m.source_range).collect(),
|
||||
KclValue::TagDeclarator(t) => vec![SourceRange([t.start, t.end, t.module_id.0 as usize])],
|
||||
KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
|
||||
KclValue::Solid(e) => to_vec_sr(&e.meta),
|
||||
KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
||||
KclValue::Sketch { value } => to_vec_sr(&value.meta),
|
||||
KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
||||
KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
|
||||
KclValue::Function { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Plane(p) => to_vec_sr(&p.meta),
|
||||
KclValue::Face(f) => to_vec_sr(&f.meta),
|
||||
KclValue::Bool { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Number { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Int { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::String { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Uuid { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Array { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Object { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::KclNone { meta, .. } => to_vec_sr(meta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KclValue {
|
||||
pub fn get_json_value(&self) -> Result<serde_json::Value, KclError> {
|
||||
if let KclValue::UserVal(user_val) = self {
|
||||
Ok(user_val.value.clone())
|
||||
} else {
|
||||
serde_json::to_value(self).map_err(|err| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Cannot convert memory item to json value: {:?}", err),
|
||||
source_ranges: self.clone().into(),
|
||||
})
|
||||
})
|
||||
/// Put the number into a KCL value.
|
||||
pub fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
|
||||
Self::Number { value: f, meta }
|
||||
}
|
||||
|
||||
/// Put the point into a KCL value.
|
||||
pub fn from_point2d(p: [f64; 2], meta: Vec<Metadata>) -> Self {
|
||||
Self::Array {
|
||||
value: vec![
|
||||
Self::Number {
|
||||
value: p[0],
|
||||
meta: meta.clone(),
|
||||
},
|
||||
Self::Number {
|
||||
value: p[1],
|
||||
meta: meta.clone(),
|
||||
},
|
||||
],
|
||||
meta,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a JSON value and deserialize it into some concrete type.
|
||||
pub fn get_json<T: serde::de::DeserializeOwned>(&self) -> Result<T, KclError> {
|
||||
let json = self.get_json_value()?;
|
||||
|
||||
serde_json::from_value(json).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to deserialize struct from JSON: {}", e),
|
||||
source_ranges: self.clone().into(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a JSON value and deserialize it into some concrete type.
|
||||
/// If it's a KCL None, return None. Otherwise return Some.
|
||||
pub fn get_json_opt<T: serde::de::DeserializeOwned>(&self) -> Result<Option<T>, KclError> {
|
||||
let json = self.get_json_value()?;
|
||||
if let JValue::Object(ref o) = json {
|
||||
if let Some(JValue::String(s)) = o.get("type") {
|
||||
if s == "KclNone" {
|
||||
return Ok(None);
|
||||
}
|
||||
pub(crate) fn as_usize(&self) -> Option<usize> {
|
||||
match self {
|
||||
KclValue::Int { value, .. } => Some(*value as usize),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
serde_json::from_value(json)
|
||||
.map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to deserialize struct from JSON: {}", e),
|
||||
source_ranges: self.clone().into(),
|
||||
})
|
||||
})
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
pub fn as_user_val(&self) -> Option<&UserVal> {
|
||||
if let KclValue::UserVal(x) = self {
|
||||
Some(x)
|
||||
pub fn as_int(&self) -> Option<i64> {
|
||||
if let KclValue::Int { value, meta: _ } = &self {
|
||||
Some(*value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If this value is of type u32, return it.
|
||||
pub fn as_object(&self) -> Option<&HashMap<String, KclValue>> {
|
||||
if let KclValue::Object { value, meta: _ } = &self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
if let KclValue::String { value, meta: _ } = &self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_array(&self) -> Option<&[KclValue]> {
|
||||
if let KclValue::Array { value, meta: _ } = &self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_point2d(&self) -> Option<[f64; 2]> {
|
||||
let arr = self.as_array()?;
|
||||
if arr.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
let x = arr[0].as_f64()?;
|
||||
let y = arr[1].as_f64()?;
|
||||
Some([x, y])
|
||||
}
|
||||
|
||||
pub fn as_uuid(&self) -> Option<uuid::Uuid> {
|
||||
if let KclValue::Uuid { value, meta: _ } = &self {
|
||||
Some(*value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_solid(&self) -> Option<&Solid> {
|
||||
if let KclValue::Solid(value) = &self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
if let KclValue::Number { value, meta: _ } = &self {
|
||||
Some(*value)
|
||||
} else if let KclValue::Int { value, meta: _ } = &self {
|
||||
Some(*value as f64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
if let KclValue::Bool { value, meta: _ } = &self {
|
||||
Some(*value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If this value fits in a u32, return it.
|
||||
pub fn get_u32(&self, source_ranges: Vec<SourceRange>) -> Result<u32, KclError> {
|
||||
let err = KclError::Semantic(KclErrorDetails {
|
||||
let u = self.as_int().and_then(|n| u64::try_from(n).ok()).ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected an integer >= 0".to_owned(),
|
||||
source_ranges: source_ranges.clone(),
|
||||
})
|
||||
})?;
|
||||
u32::try_from(u).map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Number was too big".to_owned(),
|
||||
source_ranges,
|
||||
});
|
||||
self.as_user_val()
|
||||
.and_then(|uv| uv.value.as_number())
|
||||
.and_then(|n| n.as_u64())
|
||||
.and_then(|n| u32::try_from(n).ok())
|
||||
.ok_or(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// If this value is of type function, return it.
|
||||
@ -1048,16 +1113,6 @@ impl KclValue {
|
||||
pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
|
||||
match self {
|
||||
KclValue::TagIdentifier(t) => Ok(*t.clone()),
|
||||
KclValue::UserVal(_) => {
|
||||
if let Some(identifier) = self.get_json_opt::<TagIdentifier>()? {
|
||||
Ok(identifier)
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Not a tag identifier: {:?}", self),
|
||||
source_ranges: self.clone().into(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
_ => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Not a tag identifier: {:?}", self),
|
||||
source_ranges: self.clone().into(),
|
||||
@ -1089,19 +1144,13 @@ impl KclValue {
|
||||
|
||||
/// If this KCL value is a bool, retrieve it.
|
||||
pub fn get_bool(&self) -> Result<bool, KclError> {
|
||||
let Self::UserVal(uv) = self else {
|
||||
let Self::Bool { value: b, .. } = self else {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
source_ranges: self.into(),
|
||||
message: format!("Expected bool, found {}", self.human_friendly_type()),
|
||||
}));
|
||||
};
|
||||
let JValue::Bool(b) = uv.value else {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
source_ranges: self.into(),
|
||||
message: format!("Expected bool, found {}", human_friendly_type(&uv.value)),
|
||||
}));
|
||||
};
|
||||
Ok(b)
|
||||
Ok(*b)
|
||||
}
|
||||
|
||||
/// If this memory item is a function, call it with the given arguments, return its val as Ok.
|
||||
@ -1555,7 +1604,7 @@ impl From<Point3d> for kittycad_modeling_cmds::shared::Point3d<LengthUnit> {
|
||||
}
|
||||
|
||||
/// Metadata.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq, Copy)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Metadata {
|
||||
@ -1563,6 +1612,12 @@ pub struct Metadata {
|
||||
pub source_range: SourceRange,
|
||||
}
|
||||
|
||||
impl From<Metadata> for Vec<SourceRange> {
|
||||
fn from(meta: Metadata) -> Self {
|
||||
vec![meta.source_range]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SourceRange> for Metadata {
|
||||
fn from(source_range: SourceRange) -> Self {
|
||||
Self { source_range }
|
||||
@ -2655,74 +2710,8 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Convenience function to get a JSON value from memory and unwrap.
|
||||
fn mem_get_json(memory: &ProgramMemory, name: &str) -> serde_json::Value {
|
||||
memory
|
||||
.get(name, SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[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
|
||||
.get("myVar", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::json!(6.0),
|
||||
memory
|
||||
.get("newVar", SourceRange::default())
|
||||
.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 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([2, 2], %, $yo)
|
||||
|> lineTo([3, 1], %)
|
||||
|> angledLineThatIntersects({{
|
||||
angle: 180,
|
||||
intersectTag: yo,
|
||||
offset: {},
|
||||
}}, %, $yo2)
|
||||
const intersect = segEndX(yo2)"#,
|
||||
offset
|
||||
)
|
||||
};
|
||||
|
||||
let memory = parse_execute(&ast_fn("-1")).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(1.0 + 2.0f64.sqrt()),
|
||||
memory
|
||||
.get("intersect", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let memory = parse_execute(&ast_fn("0")).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(1.0000000000000002),
|
||||
memory
|
||||
.get("intersect", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
fn mem_get_json(memory: &ProgramMemory, name: &str) -> KclValue {
|
||||
memory.get(name, SourceRange::default()).unwrap().to_owned()
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3120,200 +3109,41 @@ let shape = layer() |> patternTransform(10, transform, %)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_function_with_parameter_redefined_outside() {
|
||||
let ast = r#"
|
||||
fn myIdentity = (x) => {
|
||||
return x
|
||||
}
|
||||
|
||||
const x = 33
|
||||
|
||||
const two = myIdentity(2)"#;
|
||||
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(2),
|
||||
memory
|
||||
.get("two", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::json!(33),
|
||||
memory
|
||||
.get("x", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_function_referencing_variable_in_parent_scope() {
|
||||
let ast = r#"
|
||||
const x = 22
|
||||
const y = 3
|
||||
|
||||
fn add = (x) => {
|
||||
return x + y
|
||||
}
|
||||
|
||||
const answer = add(2)"#;
|
||||
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(5.0),
|
||||
memory
|
||||
.get("answer", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::json!(22),
|
||||
memory
|
||||
.get("x", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_function_redefining_variable_in_parent_scope() {
|
||||
let ast = r#"
|
||||
const x = 1
|
||||
|
||||
fn foo = () => {
|
||||
const x = 2
|
||||
return x
|
||||
}
|
||||
|
||||
const answer = foo()"#;
|
||||
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(2),
|
||||
memory
|
||||
.get("answer", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::json!(1),
|
||||
memory
|
||||
.get("x", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_pattern_transform_function_redefining_variable_in_parent_scope() {
|
||||
let ast = r#"
|
||||
const scale = 100
|
||||
fn transform = (replicaId) => {
|
||||
// Redefine same variable as in parent scope.
|
||||
const scale = 2
|
||||
return {
|
||||
translate: [0, 0, replicaId * 10],
|
||||
scale: [scale, 1, 0],
|
||||
}
|
||||
}
|
||||
|
||||
fn layer = () => {
|
||||
return startSketchOn("XY")
|
||||
|> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|
||||
|> extrude(10, %)
|
||||
}
|
||||
|
||||
// The 10 layers are replicas of each other, with a transform applied to each.
|
||||
let shape = layer() |> patternTransform(10, transform, %)"#;
|
||||
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
// TODO: Assert that scale 2 was used.
|
||||
assert_eq!(
|
||||
serde_json::json!(100),
|
||||
memory
|
||||
.get("scale", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
// ADAM: Move some of these into simulation tests.
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute_with_functions() {
|
||||
let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(5.0),
|
||||
memory
|
||||
.get("myVar", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(5.0, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute() {
|
||||
let ast = r#"const myVar = 1 + 2 * (3 - 4) / -5 + 6"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(7.4),
|
||||
memory
|
||||
.get("myVar", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(7.4, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute_start_negative() {
|
||||
let ast = r#"const myVar = -5 + 6"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(1.0),
|
||||
memory
|
||||
.get("myVar", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(1.0, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute_with_pi() {
|
||||
let ast = r#"const myVar = pi() * 2"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(std::f64::consts::TAU),
|
||||
memory
|
||||
.get("myVar", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(std::f64::consts::TAU, mem_get_json(&memory, "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_define_decimal_without_leading_zero() {
|
||||
let ast = r#"let thing = .4 + 7"#;
|
||||
let memory = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::json!(7.4),
|
||||
memory
|
||||
.get("thing", SourceRange::default())
|
||||
.unwrap()
|
||||
.get_json_value()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(7.4, mem_get_json(&memory, "thing").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3353,10 +3183,10 @@ fn check = (x) => {
|
||||
check(false)
|
||||
"#;
|
||||
let mem = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(serde_json::json!(false), mem_get_json(&mem, "notTrue"));
|
||||
assert_eq!(serde_json::json!(true), mem_get_json(&mem, "notFalse"));
|
||||
assert_eq!(serde_json::json!(true), mem_get_json(&mem, "c"));
|
||||
assert_eq!(serde_json::json!(false), mem_get_json(&mem, "d"));
|
||||
assert_eq!(false, mem_get_json(&mem, "notTrue").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&mem, "notFalse").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&mem, "c").as_bool().unwrap());
|
||||
assert_eq!(false, mem_get_json(&mem, "d").as_bool().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3369,7 +3199,7 @@ let notNull = !myNull
|
||||
assert_eq!(
|
||||
parse_execute(code1).await.unwrap_err().downcast::<KclError>().unwrap(),
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot apply unary operator ! to non-boolean value: null".to_owned(),
|
||||
message: "Cannot apply unary operator ! to non-boolean value: number".to_owned(),
|
||||
source_ranges: vec![SourceRange([56, 63, 0])],
|
||||
})
|
||||
);
|
||||
@ -3378,7 +3208,7 @@ let notNull = !myNull
|
||||
assert_eq!(
|
||||
parse_execute(code2).await.unwrap_err().downcast::<KclError>().unwrap(),
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot apply unary operator ! to non-boolean value: 0".to_owned(),
|
||||
message: "Cannot apply unary operator ! to non-boolean value: integer".to_owned(),
|
||||
source_ranges: vec![SourceRange([14, 16, 0])],
|
||||
})
|
||||
);
|
||||
@ -3389,7 +3219,7 @@ let notEmptyString = !""
|
||||
assert_eq!(
|
||||
parse_execute(code3).await.unwrap_err().downcast::<KclError>().unwrap(),
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot apply unary operator ! to non-boolean value: \"\"".to_owned(),
|
||||
message: "Cannot apply unary operator ! to non-boolean value: string (text)".to_owned(),
|
||||
source_ranges: vec![SourceRange([22, 25, 0])],
|
||||
})
|
||||
);
|
||||
@ -3401,7 +3231,7 @@ let notMember = !obj.a
|
||||
assert_eq!(
|
||||
parse_execute(code4).await.unwrap_err().downcast::<KclError>().unwrap(),
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot apply unary operator ! to non-boolean value: 1".to_owned(),
|
||||
message: "Cannot apply unary operator ! to non-boolean value: integer".to_owned(),
|
||||
source_ranges: vec![SourceRange([36, 42, 0])],
|
||||
})
|
||||
);
|
||||
@ -3412,7 +3242,7 @@ let notArray = !a";
|
||||
assert_eq!(
|
||||
parse_execute(code5).await.unwrap_err().downcast::<KclError>().unwrap(),
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot apply unary operator ! to non-boolean value: []".to_owned(),
|
||||
message: "Cannot apply unary operator ! to non-boolean value: array (list)".to_owned(),
|
||||
source_ranges: vec![SourceRange([27, 29, 0])],
|
||||
})
|
||||
);
|
||||
@ -3423,7 +3253,7 @@ let notObject = !x";
|
||||
assert_eq!(
|
||||
parse_execute(code6).await.unwrap_err().downcast::<KclError>().unwrap(),
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot apply unary operator ! to non-boolean value: {}".to_owned(),
|
||||
message: "Cannot apply unary operator ! to non-boolean value: object".to_owned(),
|
||||
source_ranges: vec![SourceRange([28, 30, 0])],
|
||||
})
|
||||
);
|
||||
@ -3451,7 +3281,7 @@ let notTagDeclarator = !myTagDeclarator";
|
||||
assert!(
|
||||
tag_declarator_err
|
||||
.message()
|
||||
.starts_with("Cannot apply unary operator ! to non-boolean value: {\"type\":\"TagDeclarator\","),
|
||||
.starts_with("Cannot apply unary operator ! to non-boolean value: TagDeclarator"),
|
||||
"Actual error: {:?}",
|
||||
tag_declarator_err
|
||||
);
|
||||
@ -3465,7 +3295,7 @@ let notTagIdentifier = !myTag";
|
||||
assert!(
|
||||
tag_identifier_err
|
||||
.message()
|
||||
.starts_with("Cannot apply unary operator ! to non-boolean value: {\"type\":\"TagIdentifier\","),
|
||||
.starts_with("Cannot apply unary operator ! to non-boolean value: TagIdentifier"),
|
||||
"Actual error: {:?}",
|
||||
tag_identifier_err
|
||||
);
|
||||
@ -3603,10 +3433,10 @@ let w = f() + f()
|
||||
fn test_assign_args_to_params() {
|
||||
// Set up a little framework for this test.
|
||||
fn mem(number: usize) -> KclValue {
|
||||
KclValue::UserVal(UserVal {
|
||||
value: number.into(),
|
||||
KclValue::Int {
|
||||
value: number as i64,
|
||||
meta: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
fn ident(s: &'static str) -> Node<Identifier> {
|
||||
Node::no_src(Identifier {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,47 +1,25 @@
|
||||
use derive_docs::stdlib;
|
||||
use serde_json::Value as JValue;
|
||||
|
||||
use super::{args::FromArgs, Args, FnAsArg};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{ExecState, KclValue, SourceRange, UserVal},
|
||||
executor::{ExecState, KclValue, SourceRange},
|
||||
function_param::FunctionParam,
|
||||
};
|
||||
|
||||
/// Apply a function to each element of an array.
|
||||
pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (array, f): (Vec<JValue>, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
||||
let array: Vec<KclValue> = array
|
||||
.into_iter()
|
||||
.map(|jval| {
|
||||
KclValue::UserVal(UserVal {
|
||||
value: jval,
|
||||
meta: vec![args.source_range.into()],
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let (array, f): (Vec<KclValue>, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
||||
let meta = vec![args.source_range.into()];
|
||||
let map_fn = FunctionParam {
|
||||
inner: f.func,
|
||||
fn_expr: f.expr,
|
||||
meta: vec![args.source_range.into()],
|
||||
meta: meta.clone(),
|
||||
ctx: args.ctx.clone(),
|
||||
memory: *f.memory,
|
||||
};
|
||||
let new_array = inner_map(array, map_fn, exec_state, &args).await?;
|
||||
let unwrapped = new_array
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|k| match k {
|
||||
KclValue::UserVal(user_val) => Ok(user_val.value),
|
||||
_ => Err(()),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>();
|
||||
if let Ok(unwrapped) = unwrapped {
|
||||
let uv = UserVal::new(vec![args.source_range.into()], unwrapped);
|
||||
return Ok(KclValue::UserVal(uv));
|
||||
}
|
||||
let uv = UserVal::new(vec![args.source_range.into()], new_array);
|
||||
Ok(KclValue::UserVal(uv))
|
||||
Ok(KclValue::Array { value: new_array, meta })
|
||||
}
|
||||
|
||||
/// Apply a function to every element of a list.
|
||||
@ -110,16 +88,7 @@ async fn call_map_closure<'a>(
|
||||
|
||||
/// For each item in an array, update a value.
|
||||
pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (array, start, f): (Vec<JValue>, KclValue, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
||||
let array: Vec<KclValue> = array
|
||||
.into_iter()
|
||||
.map(|jval| {
|
||||
KclValue::UserVal(UserVal {
|
||||
value: jval,
|
||||
meta: vec![args.source_range.into()],
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let (array, start, f): (Vec<KclValue>, KclValue, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
|
||||
let reduce_fn = FunctionParam {
|
||||
inner: f.func,
|
||||
fn_expr: f.expr,
|
||||
@ -206,50 +175,26 @@ async fn call_reduce_closure<'a>(
|
||||
#[stdlib {
|
||||
name = "push",
|
||||
}]
|
||||
async fn inner_push(array: Vec<KclValue>, elem: KclValue, args: &Args) -> Result<KclValue, KclError> {
|
||||
async fn inner_push(mut array: Vec<KclValue>, elem: KclValue, args: &Args) -> Result<KclValue, KclError> {
|
||||
// Unwrap the KclValues to JValues for manipulation
|
||||
let mut unwrapped_array = array
|
||||
.into_iter()
|
||||
.map(|k| match k {
|
||||
KclValue::UserVal(user_val) => Ok(user_val.value),
|
||||
_ => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a UserVal in array".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
})),
|
||||
array.push(elem);
|
||||
Ok(KclValue::Array {
|
||||
value: array,
|
||||
meta: vec![args.source_range.into()],
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Unwrap the element
|
||||
let unwrapped_elem = match elem {
|
||||
KclValue::UserVal(user_val) => user_val.value,
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a UserVal as element".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Append the element to the array
|
||||
unwrapped_array.push(unwrapped_elem);
|
||||
|
||||
// Wrap the new array into a UserVal with the source range metadata
|
||||
let uv = UserVal::new(vec![args.source_range.into()], unwrapped_array);
|
||||
|
||||
// Return the new array wrapped as a KclValue::UserVal
|
||||
Ok(KclValue::UserVal(uv))
|
||||
}
|
||||
|
||||
pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
// Extract the array and the element from the arguments
|
||||
let (array_jvalues, elem): (Vec<JValue>, KclValue) = FromArgs::from_args(&args, 0)?;
|
||||
let (val, elem): (KclValue, KclValue) = FromArgs::from_args(&args, 0)?;
|
||||
|
||||
// Convert the array of JValue into Vec<KclValue>
|
||||
let array: Vec<KclValue> = array_jvalues
|
||||
.into_iter()
|
||||
.map(|jval| KclValue::UserVal(UserVal::new(vec![args.source_range.into()], jval)))
|
||||
.collect();
|
||||
|
||||
// Call the inner_push function
|
||||
let meta = vec![args.source_range];
|
||||
let KclValue::Array { value: array, meta: _ } = val else {
|
||||
let actual_type = val.human_friendly_type();
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: meta,
|
||||
message: format!("You can't push to a value of type {actual_type}, only an array"),
|
||||
}));
|
||||
};
|
||||
inner_push(array, elem, &args).await
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ async fn _assert(value: bool, message: &str, args: &Args) -> Result<(), KclError
|
||||
pub async fn assert(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, description): (bool, String) = args.get_data()?;
|
||||
inner_assert(data, &description, &args).await?;
|
||||
Ok(args.make_null_user_val())
|
||||
Ok(args.make_user_val_from_f64(0.0)) // TODO: Add a new Void enum for fns that don't return anything.
|
||||
}
|
||||
|
||||
/// Check a value at runtime, and raise an error if the argument provided
|
||||
@ -44,7 +44,7 @@ async fn inner_assert(data: bool, message: &str, args: &Args) -> Result<(), KclE
|
||||
pub async fn assert_lt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_lt(left, right, &description, &args).await?;
|
||||
Ok(args.make_null_user_val())
|
||||
Ok(args.make_user_val_from_f64(0.0)) // TODO: Add a new Void enum for fns that don't return anything.
|
||||
}
|
||||
|
||||
/// Check that a numerical value is less than to another at runtime,
|
||||
@ -63,7 +63,7 @@ async fn inner_assert_lt(left: f64, right: f64, message: &str, args: &Args) -> R
|
||||
pub async fn assert_gt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_gt(left, right, &description, &args).await?;
|
||||
Ok(args.make_null_user_val())
|
||||
Ok(args.make_user_val_from_f64(0.0)) // TODO: Add a new Void enum for fns that don't return anything.
|
||||
}
|
||||
|
||||
/// Check that a numerical value equals another at runtime,
|
||||
@ -96,7 +96,7 @@ async fn inner_assert_equal(left: f64, right: f64, epsilon: f64, message: &str,
|
||||
pub async fn assert_equal(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, epsilon, description): (f64, f64, f64, String) = args.get_data()?;
|
||||
inner_assert_equal(left, right, epsilon, &description, &args).await?;
|
||||
Ok(args.make_null_user_val())
|
||||
Ok(args.make_user_val_from_f64(0.0)) // TODO: Add a new Void enum for fns that don't return anything.
|
||||
}
|
||||
|
||||
/// Check that a numerical value is greater than another at runtime,
|
||||
@ -115,7 +115,7 @@ async fn inner_assert_gt(left: f64, right: f64, message: &str, args: &Args) -> R
|
||||
pub async fn assert_lte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_lte(left, right, &description, &args).await?;
|
||||
Ok(args.make_null_user_val())
|
||||
Ok(args.make_user_val_from_f64(0.0)) // TODO: Add a new Void enum for fns that don't return anything.
|
||||
}
|
||||
|
||||
/// Check that a numerical value is less than or equal to another at runtime,
|
||||
@ -135,7 +135,7 @@ async fn inner_assert_lte(left: f64, right: f64, message: &str, args: &Args) ->
|
||||
pub async fn assert_gte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_gte(left, right, &description, &args).await?;
|
||||
Ok(args.make_null_user_val())
|
||||
Ok(args.make_user_val_from_f64(0.0)) // TODO: Add a new Void enum for fns that don't return anything.
|
||||
}
|
||||
|
||||
/// Check that a numerical value is greater than or equal to another at runtime,
|
||||
|
@ -233,7 +233,7 @@ pub(crate) async fn do_post_extrude(
|
||||
tag: path.get_base().tag.clone(),
|
||||
geo_meta: GeoMeta {
|
||||
id: path.get_base().geo_meta.id,
|
||||
metadata: path.get_base().geo_meta.metadata.clone(),
|
||||
metadata: path.get_base().geo_meta.metadata,
|
||||
},
|
||||
});
|
||||
Some(extrude_surface)
|
||||
@ -244,7 +244,7 @@ pub(crate) async fn do_post_extrude(
|
||||
tag: path.get_base().tag.clone(),
|
||||
geo_meta: GeoMeta {
|
||||
id: path.get_base().geo_meta.id,
|
||||
metadata: path.get_base().geo_meta.metadata.clone(),
|
||||
metadata: path.get_base().geo_meta.metadata,
|
||||
},
|
||||
});
|
||||
Some(extrude_surface)
|
||||
@ -259,7 +259,7 @@ pub(crate) async fn do_post_extrude(
|
||||
tag: path.get_base().tag.clone(),
|
||||
geo_meta: GeoMeta {
|
||||
id: path.get_base().geo_meta.id,
|
||||
metadata: path.get_base().geo_meta.metadata.clone(),
|
||||
metadata: path.get_base().geo_meta.metadata,
|
||||
},
|
||||
});
|
||||
Some(extrude_surface)
|
||||
|
@ -14,7 +14,7 @@ use uuid::Uuid;
|
||||
use crate::{
|
||||
ast::types::TagNode,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier, UserVal},
|
||||
executor::{EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier},
|
||||
settings::types::UnitLength,
|
||||
std::Args,
|
||||
};
|
||||
@ -186,15 +186,10 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
|
||||
let edge = inner_get_opposite_edge(tag, exec_state, args.clone()).await?;
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: serde_json::to_value(edge).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to convert Uuid to json: {}", e),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?,
|
||||
Ok(KclValue::Uuid {
|
||||
value: edge,
|
||||
meta: vec![args.source_range.into()],
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the opposite edge to the edge given.
|
||||
@ -264,15 +259,10 @@ pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> R
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
|
||||
let edge = inner_get_next_adjacent_edge(tag, exec_state, args.clone()).await?;
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: serde_json::to_value(edge).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to convert Uuid to json: {}", e),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?,
|
||||
Ok(KclValue::Uuid {
|
||||
value: edge,
|
||||
meta: vec![args.source_range.into()],
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the next adjacent edge to the edge given.
|
||||
@ -354,15 +344,10 @@ pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args)
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
|
||||
let edge = inner_get_previous_adjacent_edge(tag, exec_state, args.clone()).await?;
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: serde_json::to_value(edge).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to convert Uuid to json: {}", e),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?,
|
||||
Ok(KclValue::Uuid {
|
||||
value: edge,
|
||||
meta: vec![args.source_range.into()],
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the previous adjacent edge to the edge given.
|
||||
|
@ -40,7 +40,7 @@ pub async fn cos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let num = args.get_number()?;
|
||||
let result = inner_cos(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the cosine of a number (in radians).
|
||||
@ -70,7 +70,7 @@ pub async fn sin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let num = args.get_number()?;
|
||||
let result = inner_sin(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the sine of a number (in radians).
|
||||
@ -100,7 +100,7 @@ pub async fn tan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let num = args.get_number()?;
|
||||
let result = inner_tan(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the tangent of a number (in radians).
|
||||
@ -129,7 +129,7 @@ fn inner_tan(num: f64) -> Result<f64, KclError> {
|
||||
pub async fn pi(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_pi()?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Return the value of `pi`. Archimedes’ constant (π).
|
||||
@ -155,7 +155,7 @@ pub async fn sqrt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_sqrt(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the square root of a number.
|
||||
@ -185,7 +185,7 @@ pub async fn abs(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let num = args.get_number()?;
|
||||
let result = inner_abs(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the absolute value of a number.
|
||||
@ -222,7 +222,7 @@ pub async fn floor(_exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let num = args.get_number()?;
|
||||
let result = inner_floor(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the largest integer less than or equal to a number.
|
||||
@ -250,7 +250,7 @@ pub async fn ceil(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_ceil(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the smallest integer greater than or equal to a number.
|
||||
@ -278,7 +278,7 @@ pub async fn min(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let nums = args.get_number_array()?;
|
||||
let result = inner_min(nums);
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the minimum of the given arguments.
|
||||
@ -315,7 +315,7 @@ pub async fn max(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let nums = args.get_number_array()?;
|
||||
let result = inner_max(nums);
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the maximum of the given arguments.
|
||||
@ -366,7 +366,7 @@ pub async fn pow(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
|
||||
let result = inner_pow(nums[0], nums[1])?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the number to a power.
|
||||
@ -396,7 +396,7 @@ pub async fn acos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_acos(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the arccosine of a number (in radians).
|
||||
@ -427,7 +427,7 @@ pub async fn asin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_asin(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the arcsine of a number (in radians).
|
||||
@ -457,7 +457,7 @@ pub async fn atan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_atan(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the arctangent of a number (in radians).
|
||||
@ -504,7 +504,7 @@ pub async fn log(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
}
|
||||
let result = inner_log(nums[0], nums[1])?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the logarithm of the number with respect to an arbitrary base.
|
||||
@ -536,7 +536,7 @@ pub async fn log2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let num = args.get_number()?;
|
||||
let result = inner_log2(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the base 2 logarithm of the number.
|
||||
@ -564,7 +564,7 @@ pub async fn log10(_exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let num = args.get_number()?;
|
||||
let result = inner_log10(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the base 10 logarithm of the number.
|
||||
@ -592,7 +592,7 @@ pub async fn ln(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
|
||||
let num = args.get_number()?;
|
||||
let result = inner_ln(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the natural logarithm of the number.
|
||||
@ -619,7 +619,7 @@ fn inner_ln(num: f64) -> Result<f64, KclError> {
|
||||
pub async fn e(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_e()?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Return the value of Euler’s number `e`.
|
||||
@ -648,7 +648,7 @@ fn inner_e() -> Result<f64, KclError> {
|
||||
pub async fn tau(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_tau()?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Return the value of `tau`. The full circle constant (τ). Equal to 2π.
|
||||
@ -678,7 +678,7 @@ pub async fn to_radians(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
let num = args.get_number()?;
|
||||
let result = inner_to_radians(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Converts a number from degrees to radians.
|
||||
@ -708,7 +708,7 @@ pub async fn to_degrees(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
let num = args.get_number()?;
|
||||
let result = inner_to_degrees(num)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Converts a number from radians to degrees.
|
||||
|
@ -244,7 +244,7 @@ pub enum FunctionKind {
|
||||
pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
||||
let result = inner_leg_length(hypotenuse, leg);
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(KclValue::from_number(result, vec![args.into()]))
|
||||
}
|
||||
|
||||
/// Compute the length of the given leg.
|
||||
@ -264,7 +264,7 @@ fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
|
||||
pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
||||
let result = inner_leg_angle_x(hypotenuse, leg);
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(KclValue::from_number(result, vec![args.into()]))
|
||||
}
|
||||
|
||||
/// Compute the angle of the given leg for x.
|
||||
@ -284,7 +284,7 @@ fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
|
||||
pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
||||
let result = inner_leg_angle_y(hypotenuse, leg);
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(KclValue::from_number(result, vec![args.into()]))
|
||||
}
|
||||
|
||||
/// Compute the angle of the given leg for y.
|
||||
|
@ -14,13 +14,10 @@ use kittycad_modeling_cmds::{
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JValue;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{
|
||||
ExecState, Geometries, Geometry, KclValue, Point3d, Sketch, SketchSet, Solid, SolidSet, SourceRange, UserVal,
|
||||
},
|
||||
executor::{ExecState, Geometries, Geometry, KclValue, Point3d, Sketch, SketchSet, Solid, SolidSet, SourceRange},
|
||||
function_param::FunctionParam,
|
||||
std::{types::Uint, Args},
|
||||
};
|
||||
@ -361,10 +358,10 @@ async fn make_transform<'a>(
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<Transform, KclError> {
|
||||
// Call the transform fn for this repetition.
|
||||
let repetition_num = KclValue::UserVal(UserVal {
|
||||
value: JValue::Number(i.into()),
|
||||
let repetition_num = KclValue::Int {
|
||||
value: i.into(),
|
||||
meta: vec![source_range.into()],
|
||||
});
|
||||
};
|
||||
let transform_fn_args = vec![repetition_num];
|
||||
let transform_fn_return = transform_function.call(exec_state, transform_fn_args).await?;
|
||||
|
||||
@ -376,7 +373,7 @@ async fn make_transform<'a>(
|
||||
source_ranges: source_ranges.clone(),
|
||||
})
|
||||
})?;
|
||||
let KclValue::UserVal(transform) = transform_fn_return else {
|
||||
let KclValue::Object { value: transform, meta } = transform_fn_return else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Transform function must return a transform object".to_string(),
|
||||
source_ranges: source_ranges.clone(),
|
||||
@ -384,9 +381,9 @@ async fn make_transform<'a>(
|
||||
};
|
||||
|
||||
// Apply defaults to the transform.
|
||||
let replicate = match transform.value.get("replicate") {
|
||||
Some(JValue::Bool(true)) => true,
|
||||
Some(JValue::Bool(false)) => false,
|
||||
let replicate = match transform.get("replicate") {
|
||||
Some(KclValue::Bool { value: true, .. }) => true,
|
||||
Some(KclValue::Bool { value: false, .. }) => false,
|
||||
Some(_) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "The 'replicate' key must be a bool".to_string(),
|
||||
@ -395,38 +392,43 @@ async fn make_transform<'a>(
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
let scale = match transform.value.get("scale") {
|
||||
let scale = match transform.get("scale") {
|
||||
Some(x) => array_to_point3d(x, source_ranges.clone())?,
|
||||
None => Point3d { x: 1.0, y: 1.0, z: 1.0 },
|
||||
};
|
||||
let translate = match transform.value.get("translate") {
|
||||
let translate = match transform.get("translate") {
|
||||
Some(x) => array_to_point3d(x, source_ranges.clone())?,
|
||||
None => Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
};
|
||||
let mut rotation = Rotation::default();
|
||||
if let Some(rot) = transform.value.get("rotation") {
|
||||
if let Some(rot) = transform.get("rotation") {
|
||||
let KclValue::Object { value: rot, meta: _ } = rot else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "The 'rotation' key must be an object (with optional fields 'angle', 'axis' and 'origin')"
|
||||
.to_string(),
|
||||
source_ranges: source_ranges.clone(),
|
||||
}));
|
||||
};
|
||||
if let Some(axis) = rot.get("axis") {
|
||||
rotation.axis = array_to_point3d(axis, source_ranges.clone())?.into();
|
||||
}
|
||||
if let Some(angle) = rot.get("angle") {
|
||||
match angle {
|
||||
JValue::Number(number) => {
|
||||
if let Some(number) = number.as_f64() {
|
||||
rotation.angle = Angle::from_degrees(number);
|
||||
}
|
||||
KclValue::Number { value: number, meta: _ } => {
|
||||
rotation.angle = Angle::from_degrees(*number);
|
||||
}
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "The 'rotation.angle' key must be a number (of degrees)".to_string(),
|
||||
source_ranges: source_ranges.clone(),
|
||||
source_ranges: meta.iter().map(|m| m.source_range).collect(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(origin) = rot.get("origin") {
|
||||
rotation.origin = match origin {
|
||||
JValue::String(s) if s == "local" => OriginType::Local,
|
||||
JValue::String(s) if s == "global" => OriginType::Global,
|
||||
KclValue::String { value: s, meta: _ } if s == "local" => OriginType::Local,
|
||||
KclValue::String { value: s, meta: _ } if s == "global" => OriginType::Global,
|
||||
other => {
|
||||
let origin = array_to_point3d(other, source_ranges.clone())?.into();
|
||||
OriginType::Custom { origin }
|
||||
@ -443,8 +445,8 @@ async fn make_transform<'a>(
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
fn array_to_point3d(json: &JValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
|
||||
let JValue::Array(arr) = json else {
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
|
||||
let KclValue::Array { value: arr, meta } = val else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected an array of 3 numbers (i.e. a 3D point)".to_string(),
|
||||
source_ranges,
|
||||
@ -457,17 +459,21 @@ fn array_to_point3d(json: &JValue, source_ranges: Vec<SourceRange>) -> Result<Po
|
||||
source_ranges,
|
||||
}));
|
||||
};
|
||||
// Gets an f64 from a JSON value, returns Option.
|
||||
let f = |j: &JValue| j.as_number().and_then(|num| num.as_f64()).map(|x| x.to_owned());
|
||||
let err = |component| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
// Gets an f64 from a KCL value.
|
||||
let f = |k: &KclValue, component: char| {
|
||||
use super::args::FromKclValue;
|
||||
if let Some(value) = f64::from_mem_item(k) {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("{component} component of this point was not a number"),
|
||||
source_ranges: source_ranges.clone(),
|
||||
})
|
||||
source_ranges: meta.iter().map(|m| m.source_range).collect(),
|
||||
}))
|
||||
}
|
||||
};
|
||||
let x = f(&arr[0]).ok_or_else(|| err("X"))?;
|
||||
let y = f(&arr[1]).ok_or_else(|| err("Y"))?;
|
||||
let z = f(&arr[2]).ok_or_else(|| err("Z"))?;
|
||||
let x = f(&arr[0], 'x')?;
|
||||
let y = f(&arr[1], 'y')?;
|
||||
let z = f(&arr[2], 'z')?;
|
||||
Ok(Point3d { x, y, z })
|
||||
}
|
||||
|
||||
@ -477,8 +483,22 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_array_to_point3d() {
|
||||
let input = serde_json::json! {
|
||||
[1.1, 2.2, 3.3]
|
||||
let input = KclValue::Array {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 1.1,
|
||||
meta: Default::default(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 2.2,
|
||||
meta: Default::default(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 3.3,
|
||||
meta: Default::default(),
|
||||
},
|
||||
],
|
||||
meta: Default::default(),
|
||||
};
|
||||
let expected = Point3d { x: 1.1, y: 2.2, z: 3.3 };
|
||||
let actual = array_to_point3d(&input, Vec::new());
|
||||
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
executor::{ExecState, KclValue, Metadata, Plane, UserVal},
|
||||
executor::{ExecState, KclValue, Plane},
|
||||
std::{sketch::PlaneData, Args},
|
||||
};
|
||||
|
||||
@ -50,15 +50,9 @@ impl From<StandardPlane> for PlaneData {
|
||||
/// Offset a plane by a distance along its normal.
|
||||
pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?;
|
||||
|
||||
let plane = inner_offset_plane(std_plane, offset, exec_state).await?;
|
||||
|
||||
Ok(KclValue::UserVal(UserVal::new(
|
||||
vec![Metadata {
|
||||
source_range: args.source_range,
|
||||
}],
|
||||
plane,
|
||||
)))
|
||||
let plane_data = inner_offset_plane(std_plane, offset, exec_state).await?;
|
||||
let plane = Plane::from_plane_data(plane_data, exec_state);
|
||||
Ok(KclValue::Plane(Box::new(plane)))
|
||||
}
|
||||
|
||||
/// Offset a plane by a distance along its normal.
|
||||
@ -129,6 +123,20 @@ pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
||||
///
|
||||
/// loft([squareSketch, circleSketch])
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// // A circle on the XY plane
|
||||
/// startSketchOn("XY")
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> circle({radius: 10, center: [0, 0]}, %)
|
||||
///
|
||||
/// // Triangle on the plane 4 units above
|
||||
/// startSketchOn(offsetPlane("XY", 4))
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line([10, 0], %)
|
||||
/// |> line([0, 10], %)
|
||||
/// |> close(%)
|
||||
/// ```
|
||||
|
||||
#[stdlib {
|
||||
name = "offsetPlane",
|
||||
}]
|
||||
|
@ -60,7 +60,7 @@ pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
let result = inner_segment_end_x(&tag, exec_state, args.clone())?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'x' axis.
|
||||
@ -96,7 +96,7 @@ pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
let result = inner_segment_end_y(&tag, exec_state, args.clone())?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'y' axis.
|
||||
@ -179,7 +179,7 @@ pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
let result = inner_segment_start_x(&tag, exec_state, args.clone())?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'x' axis.
|
||||
@ -215,7 +215,7 @@ pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
let result = inner_segment_start_y(&tag, exec_state, args.clone())?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'y' axis.
|
||||
@ -251,7 +251,7 @@ pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
let sketch = args.get_sketch()?;
|
||||
let result = inner_last_segment_x(sketch, args.clone())?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Extract the 'x' axis value of the last line segment in the provided 2-d
|
||||
@ -291,7 +291,7 @@ pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
let sketch = args.get_sketch()?;
|
||||
let result = inner_last_segment_y(sketch, args.clone())?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Extract the 'y' axis value of the last line segment in the provided 2-d
|
||||
@ -330,7 +330,7 @@ fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||
pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
let result = inner_segment_length(&tag, exec_state, args.clone())?;
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the length of the provided line segment.
|
||||
@ -376,7 +376,7 @@ pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
let tag: TagIdentifier = args.get_data()?;
|
||||
|
||||
let result = inner_segment_angle(&tag, exec_state, args.clone())?;
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Compute the angle (in degrees) of the provided line segment.
|
||||
@ -415,7 +415,7 @@ fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
||||
pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
|
||||
let result = inner_angle_to_match_length_x(&tag, to, sketch, exec_state, args.clone())?;
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Returns the angle to match the given length for x.
|
||||
@ -478,7 +478,7 @@ fn inner_angle_to_match_length_x(
|
||||
pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
|
||||
let result = inner_angle_to_match_length_y(&tag, to, sketch, exec_state, args.clone())?;
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Returns the angle to match the given length for y.
|
||||
|
@ -48,7 +48,9 @@ pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
args.get_circle_args()?;
|
||||
|
||||
let sketch = inner_circle(data, sketch_surface_or_group, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(sketch.meta.clone(), sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a 2-dimensional circle, of the specified radius, centered at
|
||||
@ -166,7 +168,7 @@ pub struct PolygonData {
|
||||
pub center: [f64; 2],
|
||||
/// The type of the polygon (inscribed or circumscribed)
|
||||
#[serde(skip)]
|
||||
polygon_type: PolygonType,
|
||||
pub polygon_type: PolygonType,
|
||||
/// Whether the polygon is inscribed (true) or circumscribed (false) about a circle with the specified radius
|
||||
#[serde(default = "default_inscribed")]
|
||||
pub inscribed: bool,
|
||||
@ -182,7 +184,9 @@ pub async fn polygon(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
args.get_polygon_args()?;
|
||||
|
||||
let sketch = inner_polygon(data, sketch_surface_or_group, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(sketch.meta.clone(), sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a regular polygon with the specified number of sides that is either inscribed or circumscribed around a circle of the specified radius.
|
||||
|
@ -17,7 +17,7 @@ use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{
|
||||
BasePath, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d, Sketch, SketchSet, SketchSurface,
|
||||
Solid, TagEngineInfo, TagIdentifier, UserVal,
|
||||
Solid, TagEngineInfo, TagIdentifier,
|
||||
},
|
||||
std::{
|
||||
utils::{
|
||||
@ -97,7 +97,9 @@ pub async fn line_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_line_to(to, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a line from the current origin to some absolute (x, y) point.
|
||||
@ -164,7 +166,9 @@ pub async fn x_line_to(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
let (to, sketch, tag): (f64, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_x_line_to(to, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a line parallel to the X axis, that ends at the given X.
|
||||
@ -212,7 +216,9 @@ pub async fn y_line_to(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
let (to, sketch, tag): (f64, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_y_line_to(to, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a line parallel to the Y axis, that ends at the given Y.
|
||||
@ -252,7 +258,9 @@ pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let (delta, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_line(delta, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a line relative to the current origin to a specified (x, y) away
|
||||
@ -333,7 +341,9 @@ pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let (length, sketch, tag): (f64, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_x_line(length, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a line relative to the current origin to a specified distance away
|
||||
@ -376,7 +386,9 @@ pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
let (length, sketch, tag): (f64, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_y_line(length, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a line relative to the current origin to a specified distance away
|
||||
@ -430,7 +442,9 @@ pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_angled_line(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a line segment relative to the current origin using the polar
|
||||
@ -515,7 +529,9 @@ pub async fn angled_line_of_x_length(exec_state: &mut ExecState, args: Args) ->
|
||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_angled_line_of_x_length(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a line segment from the current 2-dimensional sketch origin
|
||||
@ -573,9 +589,9 @@ async fn inner_angled_line_of_x_length(
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AngledLineToData {
|
||||
/// The angle of the line.
|
||||
angle: f64,
|
||||
pub angle: f64,
|
||||
/// The point to draw to.
|
||||
to: f64,
|
||||
pub to: f64,
|
||||
}
|
||||
|
||||
/// Draw an angled line to a given x coordinate.
|
||||
@ -583,7 +599,9 @@ pub async fn angled_line_to_x(exec_state: &mut ExecState, args: Args) -> Result<
|
||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_angled_line_to_x(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a line segment from the current 2-dimensional sketch origin
|
||||
@ -641,7 +659,9 @@ pub async fn angled_line_of_y_length(exec_state: &mut ExecState, args: Args) ->
|
||||
|
||||
let new_sketch = inner_angled_line_of_y_length(data, sketch, tag, exec_state, args).await?;
|
||||
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a line segment from the current 2-dimensional sketch origin
|
||||
@ -700,7 +720,9 @@ pub async fn angled_line_to_y(exec_state: &mut ExecState, args: Args) -> Result<
|
||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_angled_line_to_y(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a line segment from the current 2-dimensional sketch origin
|
||||
@ -771,7 +793,9 @@ pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args)
|
||||
let (data, sketch, tag): (AngledLineThatIntersectsData, Sketch, Option<TagNode>) =
|
||||
args.get_data_and_sketch_and_tag()?;
|
||||
let new_sketch = inner_angled_line_that_intersects(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw an angled line from the current origin, constructing a line segment
|
||||
@ -828,7 +852,9 @@ pub async fn start_sketch_at(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
let data: [f64; 2] = args.get_data()?;
|
||||
|
||||
let sketch = inner_start_sketch_at(data, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(sketch.meta.clone(), sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Start a new 2-dimensional sketch at a given point on the 'XY' plane.
|
||||
@ -1135,7 +1161,9 @@ pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result<
|
||||
args.get_data_and_sketch_surface()?;
|
||||
|
||||
let sketch = inner_start_profile_at(start, sketch_surface, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(sketch.meta.clone(), sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Start a new profile at a given point.
|
||||
@ -1262,7 +1290,7 @@ pub(crate) async fn inner_start_profile_at(
|
||||
pub async fn profile_start_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch()?;
|
||||
let x = inner_profile_start_x(sketch)?;
|
||||
args.make_user_val_from_f64(x)
|
||||
Ok(args.make_user_val_from_f64(x))
|
||||
}
|
||||
|
||||
/// Extract the provided 2-dimensional sketch's profile's origin's 'x'
|
||||
@ -1286,7 +1314,7 @@ pub(crate) fn inner_profile_start_x(sketch: Sketch) -> Result<f64, KclError> {
|
||||
pub async fn profile_start_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch()?;
|
||||
let x = inner_profile_start_y(sketch)?;
|
||||
args.make_user_val_from_f64(x)
|
||||
Ok(args.make_user_val_from_f64(x))
|
||||
}
|
||||
|
||||
/// Extract the provided 2-dimensional sketch's profile's origin's 'y'
|
||||
@ -1309,15 +1337,7 @@ pub(crate) fn inner_profile_start_y(sketch: Sketch) -> Result<f64, KclError> {
|
||||
pub async fn profile_start(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch: Sketch = args.get_sketch()?;
|
||||
let point = inner_profile_start(sketch)?;
|
||||
Ok(KclValue::UserVal(UserVal {
|
||||
value: serde_json::to_value(point).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to convert point to json: {}", e),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?,
|
||||
meta: Default::default(),
|
||||
}))
|
||||
Ok(KclValue::from_point2d(point, args.into()))
|
||||
}
|
||||
|
||||
/// Extract the provided 2-dimensional sketch's profile's origin
|
||||
@ -1345,7 +1365,9 @@ pub async fn close(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
let new_sketch = inner_close(sketch, tag, exec_state, args).await?;
|
||||
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Construct a line segment from the current origin back to the profile's
|
||||
@ -1452,7 +1474,9 @@ pub async fn arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
|
||||
let (data, sketch, tag): (ArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_arc(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a curved line segment along an imaginary circle.
|
||||
@ -1573,7 +1597,9 @@ pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
let (data, sketch, tag): (TangentialArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_tangential_arc(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a curved line segment along part of an imaginary circle.
|
||||
@ -1701,7 +1727,9 @@ pub async fn tangential_arc_to(exec_state: &mut ExecState, args: Args) -> Result
|
||||
let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = super::args::FromArgs::from_args(&args, 0)?;
|
||||
|
||||
let new_sketch = inner_tangential_arc_to(to, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a tangential arc to point some distance away..
|
||||
@ -1709,7 +1737,9 @@ pub async fn tangential_arc_to_relative(exec_state: &mut ExecState, args: Args)
|
||||
let (delta, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = super::args::FromArgs::from_args(&args, 0)?;
|
||||
|
||||
let new_sketch = inner_tangential_arc_to_relative(delta, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Starting at the current sketch's origin, draw a curved line segment along
|
||||
@ -1873,11 +1903,11 @@ async fn inner_tangential_arc_to_relative(
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BezierData {
|
||||
/// The to point.
|
||||
to: [f64; 2],
|
||||
pub to: [f64; 2],
|
||||
/// The first control point.
|
||||
control1: [f64; 2],
|
||||
pub control1: [f64; 2],
|
||||
/// The second control point.
|
||||
control2: [f64; 2],
|
||||
pub control2: [f64; 2],
|
||||
}
|
||||
|
||||
/// Draw a bezier curve.
|
||||
@ -1885,7 +1915,9 @@ pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
||||
let (data, sketch, tag): (BezierData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||
|
||||
let new_sketch = inner_bezier_curve(data, sketch, tag, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Draw a smooth, continuous, curved line segment from the current origin to
|
||||
@ -1965,7 +1997,9 @@ pub async fn hole(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let (hole_sketch, sketch): (SketchSet, Sketch) = args.get_sketches()?;
|
||||
|
||||
let new_sketch = inner_hole(hole_sketch, sketch, exec_state, args).await?;
|
||||
Ok(KclValue::new_user_val(new_sketch.meta.clone(), new_sketch))
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
/// Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch.
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
pub async fn mm(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_mm(&args)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Millimeters conversion factor for current projects units.
|
||||
@ -55,7 +55,7 @@ fn inner_mm(args: &Args) -> Result<f64, KclError> {
|
||||
pub async fn inch(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_inch(&args)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Inches conversion factor for current projects units.
|
||||
@ -96,7 +96,7 @@ fn inner_inch(args: &Args) -> Result<f64, KclError> {
|
||||
pub async fn ft(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_ft(&args)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Feet conversion factor for current projects units.
|
||||
@ -138,7 +138,7 @@ fn inner_ft(args: &Args) -> Result<f64, KclError> {
|
||||
pub async fn m(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_m(&args)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Meters conversion factor for current projects units.
|
||||
@ -180,7 +180,7 @@ fn inner_m(args: &Args) -> Result<f64, KclError> {
|
||||
pub async fn cm(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_cm(&args)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Centimeters conversion factor for current projects units.
|
||||
@ -222,7 +222,7 @@ fn inner_cm(args: &Args) -> Result<f64, KclError> {
|
||||
pub async fn yd(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let result = inner_yd(&args)?;
|
||||
|
||||
args.make_user_val_from_f64(result)
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
}
|
||||
|
||||
/// Yards conversion factor for current projects units.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,36 +8,67 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"arr": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
7,
|
||||
8,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
10,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
13,
|
||||
14,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -50,13 +81,60 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"new_arr1": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
7,
|
||||
8,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
10,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
13,
|
||||
14,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -69,14 +147,73 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"new_arr2": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
7,
|
||||
8,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
10,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
13,
|
||||
14,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 5,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
66,
|
||||
67,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
|
@ -8,32 +8,27 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"five": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 5,
|
||||
"__meta": [
|
||||
{
|
||||
@ -46,8 +41,7 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"four": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
@ -60,14 +54,73 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"r1": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
11,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -80,14 +133,73 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"r2": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
95,
|
||||
107,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
95,
|
||||
107,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
95,
|
||||
107,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
95,
|
||||
107,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
95,
|
||||
107,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -100,15 +212,86 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"r3": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
194,
|
||||
206,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
194,
|
||||
206,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
194,
|
||||
206,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
194,
|
||||
206,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
194,
|
||||
206,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 5,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
194,
|
||||
206,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -121,13 +304,60 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"r4": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
341,
|
||||
373,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
341,
|
||||
373,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
341,
|
||||
373,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
341,
|
||||
373,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -140,8 +370,7 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"zero": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
|
@ -8,44 +8,171 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"xs": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
-5,
|
||||
-4,
|
||||
-3,
|
||||
-2,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
{
|
||||
"type": "Int",
|
||||
"value": -5,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": -4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": -3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": -2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": -1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 5,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
19,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
|
@ -8,27 +8,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
|
@ -8,27 +8,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"cube": {
|
||||
@ -717,27 +713,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
|
@ -8,27 +8,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"increment": {
|
||||
@ -89,27 +85,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
@ -130,12 +122,47 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"xs": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
0,
|
||||
1,
|
||||
2
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
47,
|
||||
53,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
47,
|
||||
53,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
47,
|
||||
53,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -148,12 +175,89 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"ys": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
2.0,
|
||||
3.0,
|
||||
4.0
|
||||
{
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
47,
|
||||
53,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Number",
|
||||
"value": 3.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
47,
|
||||
53,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Number",
|
||||
"value": 4.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
47,
|
||||
53,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
38,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
|
@ -8,27 +8,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"part001": {
|
||||
|
@ -8,32 +8,27 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"a": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 3,
|
||||
"__meta": [
|
||||
{
|
||||
@ -46,8 +41,7 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"b": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 4,
|
||||
"__meta": [
|
||||
{
|
||||
@ -60,8 +54,7 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 5,
|
||||
"__meta": [
|
||||
{
|
||||
|
@ -8,36 +8,67 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"array": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Array",
|
||||
"value": [
|
||||
90,
|
||||
91,
|
||||
92
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 90,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
44,
|
||||
46,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 91,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
48,
|
||||
50,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Int",
|
||||
"value": 92,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
52,
|
||||
54,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"__meta": [
|
||||
{
|
||||
@ -50,8 +81,7 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
@ -64,28 +94,26 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"result0": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 91,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
93,
|
||||
101,
|
||||
48,
|
||||
50,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"result1": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 91,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
277,
|
||||
285,
|
||||
48,
|
||||
50,
|
||||
0
|
||||
]
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 33 KiB |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
@ -8,35 +8,54 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"obj": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Object",
|
||||
"value": {
|
||||
"bar": 0,
|
||||
"foo": 1
|
||||
"bar": {
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
71,
|
||||
72,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"foo": {
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
63,
|
||||
64,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
@ -49,12 +68,47 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"obj2": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Object",
|
||||
"value": {
|
||||
"inner": {
|
||||
"bar": 0,
|
||||
"foo": 1
|
||||
"type": "Object",
|
||||
"value": {
|
||||
"bar": {
|
||||
"type": "Int",
|
||||
"value": 0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
71,
|
||||
72,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"foo": {
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
63,
|
||||
64,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
56,
|
||||
74,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
@ -68,64 +122,59 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"one_a": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
122,
|
||||
132,
|
||||
63,
|
||||
64,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"one_b": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
356,
|
||||
362,
|
||||
63,
|
||||
64,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"one_c": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
553,
|
||||
570,
|
||||
63,
|
||||
64,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"one_d": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Int",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
757,
|
||||
770,
|
||||
63,
|
||||
64,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"p": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "String",
|
||||
"value": "foo",
|
||||
"__meta": [
|
||||
{
|
||||
|
@ -8,27 +8,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"test": {
|
||||
@ -295,27 +291,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
@ -637,27 +629,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"test": {
|
||||
@ -924,27 +912,23 @@ snapshot_kind: text
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 180,
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 90,
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 270,
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": 0,
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
@ -982,45 +966,10 @@ snapshot_kind: text
|
||||
]
|
||||
},
|
||||
"x": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
52,
|
||||
77,
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"on": {
|
||||
"__meta": [],
|
||||
"id": "[uuid]",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"type": "plane",
|
||||
"value": "XY",
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
},
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
@ -1103,63 +1052,15 @@ snapshot_kind: text
|
||||
"type": "ToPoint"
|
||||
}
|
||||
],
|
||||
"start": {
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
52,
|
||||
77,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"type": "Sketch"
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
52,
|
||||
77,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"x2": {
|
||||
"type": "UserVal",
|
||||
"type": "UserVal",
|
||||
"value": {
|
||||
"thing1": {
|
||||
"thing2": {
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
242,
|
||||
267,
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"id": "[uuid]",
|
||||
"on": {
|
||||
"__meta": [],
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"type": "plane",
|
||||
"value": "XY",
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
@ -1174,8 +1075,50 @@ snapshot_kind: text
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"to": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
52,
|
||||
77,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
52,
|
||||
77,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"x2": {
|
||||
"type": "Object",
|
||||
"value": {
|
||||
"thing1": {
|
||||
"type": "Object",
|
||||
"value": {
|
||||
"thing2": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
@ -1258,7 +1201,42 @@ snapshot_kind: text
|
||||
"type": "ToPoint"
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"to": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
@ -1266,19 +1244,29 @@ snapshot_kind: text
|
||||
267,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
},
|
||||
"type": "Sketch"
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
242,
|
||||
267,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
199,
|
||||
365,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"__meta": [
|
||||
|
@ -13,5 +13,5 @@ sq = square([0,0], 10)
|
||||
cb = square([3,3], 4)
|
||||
|> extrude(10, %)
|
||||
|
||||
pt1 = sq.paths[0]
|
||||
pt2 = cb.value[0]
|
||||
// pt1 = sq.paths[0]
|
||||
// pt2 = cb.value[0]
|
||||
|
@ -1354,7 +1354,7 @@ secondSketch = startSketchOn(part001, '')
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([260, 286, 0])], message: "Argument at index 1 was supposed to be type kcl_lib::std::sketch::FaceTag but found string (text)" }"#
|
||||
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([260, 286, 0])], message: "Argument at index 1 was supposed to be type Option<kcl_lib::std::sketch::FaceTag> but found string (text)" }"#
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ gen_test_fail!(
|
||||
);
|
||||
gen_test_fail!(
|
||||
invalid_index_negative,
|
||||
"semantic: '-1' is not a valid index, indices must be whole positive numbers"
|
||||
"semantic: '-1' is negative, so you can't index an array with it"
|
||||
);
|
||||
gen_test_fail!(
|
||||
invalid_index_fractional,
|
||||
@ -81,7 +81,7 @@ gen_test_fail!(
|
||||
);
|
||||
gen_test_fail!(
|
||||
invalid_member_object,
|
||||
"semantic: Only arrays and objects can be indexed, but you're trying to index a number"
|
||||
"semantic: Only arrays and objects can be indexed, but you're trying to index an integer"
|
||||
);
|
||||
gen_test_fail!(
|
||||
invalid_member_object_prop,
|
||||
@ -107,7 +107,10 @@ gen_test_fail!(
|
||||
// if_else_no_expr,
|
||||
// "syntax: blocks inside an if/else expression must end in an expression"
|
||||
// );
|
||||
gen_test_fail!(comparisons_multiple, "syntax: Invalid number: true");
|
||||
gen_test_fail!(
|
||||
comparisons_multiple,
|
||||
"semantic: Expected a number, but found a boolean (true/false value)"
|
||||
);
|
||||
gen_test_fail!(
|
||||
import_cycle1,
|
||||
"import cycle: circular import of modules is not allowed: tests/executor/inputs/no_visuals/import_cycle2.kcl -> tests/executor/inputs/no_visuals/import_cycle3.kcl -> tests/executor/inputs/no_visuals/import_cycle1.kcl -> tests/executor/inputs/no_visuals/import_cycle2.kcl"
|
||||
|
@ -4,7 +4,7 @@ use kcl_lib::{
|
||||
modify::modify_ast_for_sketch,
|
||||
types::{ModuleId, Node, Program},
|
||||
},
|
||||
executor::{ExecutorContext, IdGenerator, KclValue, PlaneType, Sketch, SourceRange},
|
||||
executor::{ExecutorContext, IdGenerator, KclValue, PlaneType, SourceRange},
|
||||
};
|
||||
use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit, shared::Point3d, ModelingCmd};
|
||||
use pretty_assertions::assert_eq;
|
||||
@ -18,12 +18,9 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node<Program>
|
||||
|
||||
// We need to get the sketch ID.
|
||||
// Get the sketch ID from memory.
|
||||
let KclValue::UserVal(user_val) = exec_state.memory.get(name, SourceRange::default()).unwrap() else {
|
||||
let KclValue::Sketch { value: sketch } = exec_state.memory.get(name, SourceRange::default()).unwrap() else {
|
||||
anyhow::bail!("part001 not found in memory: {:?}", exec_state.memory);
|
||||
};
|
||||
let Some((sketch, _meta)) = user_val.get::<Sketch>() else {
|
||||
anyhow::bail!("part001 was not a Sketch");
|
||||
};
|
||||
let sketch_id = sketch.id;
|
||||
|
||||
let plane_id = uuid::Uuid::new_v4();
|
||||
|
Reference in New Issue
Block a user