Compare commits
4 Commits
v0.16.0
...
batch-old-
Author | SHA1 | Date | |
---|---|---|---|
0bebd544a3 | |||
64c9de09aa | |||
46358b41a2 | |||
59274b76bf |
36
.github/workflows/check-exampleKcl.yml
vendored
Normal file
36
.github/workflows/check-exampleKcl.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
name: Check Onboarding KCL
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- 'src/lib/exampleKcl.ts'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Comment on PR
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const message = '`src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
|
||||||
|
const issue_number = context.payload.pull_request.number;
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
|
||||||
|
// Post a comment on the PR
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number,
|
||||||
|
body: message,
|
||||||
|
});
|
Binary file not shown.
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
@ -2,13 +2,16 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use kcl_lib::ast::types::{LiteralIdentifier, LiteralValue};
|
use kcl_lib::ast::types::{LiteralIdentifier, LiteralValue};
|
||||||
|
|
||||||
|
use kittycad_execution_plan::constants;
|
||||||
|
use kittycad_execution_plan_traits::Primitive;
|
||||||
|
|
||||||
use super::{native_functions, Address};
|
use super::{native_functions, Address};
|
||||||
use crate::{CompileError, KclFunction};
|
use crate::{CompileError, KclFunction};
|
||||||
|
|
||||||
/// KCL values which can be written to KCEP memory.
|
/// KCL values which can be written to KCEP memory.
|
||||||
/// This is recursive. For example, the bound value might be an array, which itself contains bound values.
|
/// This is recursive. For example, the bound value might be an array, which itself contains bound values.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
pub enum EpBinding {
|
pub enum EpBinding {
|
||||||
/// A KCL value which gets stored in a particular address in KCEP memory.
|
/// A KCL value which gets stored in a particular address in KCEP memory.
|
||||||
Single(Address),
|
Single(Address),
|
||||||
@ -23,6 +26,8 @@ pub enum EpBinding {
|
|||||||
properties: HashMap<String, EpBinding>,
|
properties: HashMap<String, EpBinding>,
|
||||||
},
|
},
|
||||||
/// Not associated with a KCEP address.
|
/// Not associated with a KCEP address.
|
||||||
|
Constant(Primitive),
|
||||||
|
/// Not associated with a KCEP address.
|
||||||
Function(KclFunction),
|
Function(KclFunction),
|
||||||
/// SketchGroups have their own storage.
|
/// SketchGroups have their own storage.
|
||||||
SketchGroup { index: usize },
|
SketchGroup { index: usize },
|
||||||
@ -52,11 +57,13 @@ impl EpBinding {
|
|||||||
EpBinding::SketchGroup { .. } => Err(CompileError::CannotIndex),
|
EpBinding::SketchGroup { .. } => Err(CompileError::CannotIndex),
|
||||||
EpBinding::Single(_) => Err(CompileError::CannotIndex),
|
EpBinding::Single(_) => Err(CompileError::CannotIndex),
|
||||||
EpBinding::Function(_) => Err(CompileError::CannotIndex),
|
EpBinding::Function(_) => Err(CompileError::CannotIndex),
|
||||||
|
EpBinding::Constant(_) => Err(CompileError::CannotIndex),
|
||||||
},
|
},
|
||||||
// Objects can be indexed by string properties.
|
// Objects can be indexed by string properties.
|
||||||
LiteralValue::String(property) => match self {
|
LiteralValue::String(property) => match self {
|
||||||
EpBinding::Single(_) => Err(CompileError::NoProperties),
|
EpBinding::Single(_) => Err(CompileError::NoProperties),
|
||||||
EpBinding::Function(_) => Err(CompileError::NoProperties),
|
EpBinding::Function(_) => Err(CompileError::NoProperties),
|
||||||
|
EpBinding::Constant(_) => Err(CompileError::CannotIndex),
|
||||||
EpBinding::SketchGroup { .. } => Err(CompileError::NoProperties),
|
EpBinding::SketchGroup { .. } => Err(CompileError::NoProperties),
|
||||||
EpBinding::Sequence { .. } => Err(CompileError::ArrayDoesNotHaveProperties),
|
EpBinding::Sequence { .. } => Err(CompileError::ArrayDoesNotHaveProperties),
|
||||||
EpBinding::Map {
|
EpBinding::Map {
|
||||||
@ -103,8 +110,58 @@ impl BindingScope {
|
|||||||
// TODO: Actually put the stdlib prelude in here,
|
// TODO: Actually put the stdlib prelude in here,
|
||||||
// things like `startSketchAt` and `line`.
|
// things like `startSketchAt` and `line`.
|
||||||
ep_bindings: HashMap::from([
|
ep_bindings: HashMap::from([
|
||||||
|
("E".into(), EpBinding::Constant(constants::E)),
|
||||||
|
("PI".into(), EpBinding::Constant(constants::PI)),
|
||||||
("id".into(), EpBinding::from(KclFunction::Id(native_functions::Id))),
|
("id".into(), EpBinding::from(KclFunction::Id(native_functions::Id))),
|
||||||
|
("abs".into(), EpBinding::from(KclFunction::Abs(native_functions::Abs))),
|
||||||
|
(
|
||||||
|
"acos".into(),
|
||||||
|
EpBinding::from(KclFunction::Acos(native_functions::Acos)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"asin".into(),
|
||||||
|
EpBinding::from(KclFunction::Asin(native_functions::Asin)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"atan".into(),
|
||||||
|
EpBinding::from(KclFunction::Atan(native_functions::Atan)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"ceil".into(),
|
||||||
|
EpBinding::from(KclFunction::Ceil(native_functions::Ceil)),
|
||||||
|
),
|
||||||
|
("cos".into(), EpBinding::from(KclFunction::Cos(native_functions::Cos))),
|
||||||
|
(
|
||||||
|
"floor".into(),
|
||||||
|
EpBinding::from(KclFunction::Floor(native_functions::Floor)),
|
||||||
|
),
|
||||||
|
("ln".into(), EpBinding::from(KclFunction::Ln(native_functions::Ln))),
|
||||||
|
(
|
||||||
|
"log10".into(),
|
||||||
|
EpBinding::from(KclFunction::Log10(native_functions::Log10)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"log2".into(),
|
||||||
|
EpBinding::from(KclFunction::Log2(native_functions::Log2)),
|
||||||
|
),
|
||||||
|
("sin".into(), EpBinding::from(KclFunction::Sin(native_functions::Sin))),
|
||||||
|
(
|
||||||
|
"sqrt".into(),
|
||||||
|
EpBinding::from(KclFunction::Sqrt(native_functions::Sqrt)),
|
||||||
|
),
|
||||||
|
("tan".into(), EpBinding::from(KclFunction::Tan(native_functions::Tan))),
|
||||||
|
(
|
||||||
|
"toDegrees".into(),
|
||||||
|
EpBinding::from(KclFunction::ToDegrees(native_functions::ToDegrees)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"toRadians".into(),
|
||||||
|
EpBinding::from(KclFunction::ToRadians(native_functions::ToRadians)),
|
||||||
|
),
|
||||||
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
|
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
|
||||||
|
("log".into(), EpBinding::from(KclFunction::Log(native_functions::Log))),
|
||||||
|
("max".into(), EpBinding::from(KclFunction::Max(native_functions::Max))),
|
||||||
|
("min".into(), EpBinding::from(KclFunction::Min(native_functions::Min))),
|
||||||
(
|
(
|
||||||
"startSketchAt".into(),
|
"startSketchAt".into(),
|
||||||
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),
|
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),
|
||||||
|
@ -259,6 +259,24 @@ impl Planner {
|
|||||||
binding,
|
binding,
|
||||||
} = match callee {
|
} = match callee {
|
||||||
KclFunction::Id(f) => f.call(&mut ctx, args)?,
|
KclFunction::Id(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Abs(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Acos(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Asin(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Atan(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Ceil(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Cos(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Floor(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Ln(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Log10(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Log2(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Sin(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Sqrt(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Tan(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::ToDegrees(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::ToRadians(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Log(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Max(f) => f.call(&mut ctx, args)?,
|
||||||
|
KclFunction::Min(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
|
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
|
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
|
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
|
||||||
@ -634,6 +652,21 @@ impl Eq for UserDefinedFunction {}
|
|||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
enum KclFunction {
|
enum KclFunction {
|
||||||
Id(native_functions::Id),
|
Id(native_functions::Id),
|
||||||
|
Abs(native_functions::Abs),
|
||||||
|
Acos(native_functions::Acos),
|
||||||
|
Asin(native_functions::Asin),
|
||||||
|
Atan(native_functions::Atan),
|
||||||
|
Ceil(native_functions::Ceil),
|
||||||
|
Cos(native_functions::Cos),
|
||||||
|
Floor(native_functions::Floor),
|
||||||
|
Ln(native_functions::Ln),
|
||||||
|
Log10(native_functions::Log10),
|
||||||
|
Log2(native_functions::Log2),
|
||||||
|
Sin(native_functions::Sin),
|
||||||
|
Sqrt(native_functions::Sqrt),
|
||||||
|
Tan(native_functions::Tan),
|
||||||
|
ToDegrees(native_functions::ToDegrees),
|
||||||
|
ToRadians(native_functions::ToRadians),
|
||||||
StartSketchAt(native_functions::sketch::StartSketchAt),
|
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||||
LineTo(native_functions::sketch::LineTo),
|
LineTo(native_functions::sketch::LineTo),
|
||||||
Line(native_functions::sketch::Line),
|
Line(native_functions::sketch::Line),
|
||||||
@ -642,6 +675,9 @@ enum KclFunction {
|
|||||||
YLineTo(native_functions::sketch::YLineTo),
|
YLineTo(native_functions::sketch::YLineTo),
|
||||||
YLine(native_functions::sketch::YLine),
|
YLine(native_functions::sketch::YLine),
|
||||||
Add(native_functions::Add),
|
Add(native_functions::Add),
|
||||||
|
Log(native_functions::Log),
|
||||||
|
Max(native_functions::Max),
|
||||||
|
Min(native_functions::Min),
|
||||||
UserDefined(UserDefinedFunction),
|
UserDefined(UserDefinedFunction),
|
||||||
Extrude(native_functions::sketch::Extrude),
|
Extrude(native_functions::sketch::Extrude),
|
||||||
Close(native_functions::sketch::Close),
|
Close(native_functions::sketch::Close),
|
||||||
|
@ -2,18 +2,15 @@
|
|||||||
//! This includes some of the stdlib, e.g. `startSketchAt`.
|
//! This includes some of the stdlib, e.g. `startSketchAt`.
|
||||||
//! But some other stdlib functions will be written in KCL.
|
//! But some other stdlib functions will be written in KCL.
|
||||||
|
|
||||||
use kittycad_execution_plan::{BinaryArithmetic, Destination, Instruction};
|
use kittycad_execution_plan::{
|
||||||
|
BinaryArithmetic, BinaryOperation, Destination, Instruction, Operand, UnaryArithmetic, UnaryOperation,
|
||||||
|
};
|
||||||
use kittycad_execution_plan_traits::Address;
|
use kittycad_execution_plan_traits::Address;
|
||||||
|
|
||||||
use crate::{CompileError, EpBinding, EvalPlan};
|
use crate::{CompileError, EpBinding, EvalPlan};
|
||||||
|
|
||||||
pub mod sketch;
|
pub mod sketch;
|
||||||
|
|
||||||
/// The identity function. Always returns its first input.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
|
||||||
pub struct Id;
|
|
||||||
|
|
||||||
pub trait Callable {
|
pub trait Callable {
|
||||||
fn call(&self, ctx: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError>;
|
fn call(&self, ctx: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError>;
|
||||||
}
|
}
|
||||||
@ -32,6 +29,65 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unary operator macro to quickly create new bindings.
|
||||||
|
macro_rules! define_unary {
|
||||||
|
() => {};
|
||||||
|
($h:ident$( $r:ident)*) => {
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
|
pub struct $h;
|
||||||
|
|
||||||
|
impl Callable for $h {
|
||||||
|
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||||
|
if args.len() > 1 {
|
||||||
|
return Err(CompileError::TooManyArgs {
|
||||||
|
fn_name: "$h".into(),
|
||||||
|
maximum: 1,
|
||||||
|
actual: args.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let not_enough_args = CompileError::NotEnoughArgs {
|
||||||
|
fn_name: "$h".into(),
|
||||||
|
required: 1,
|
||||||
|
actual: args.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args.clone())? else {
|
||||||
|
return Err(CompileError::InvalidOperand("A single value binding is expected"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let destination = ctx.next_address.offset_by(1);
|
||||||
|
let instructions = vec![
|
||||||
|
Instruction::UnaryArithmetic {
|
||||||
|
arithmetic: UnaryArithmetic {
|
||||||
|
operation: UnaryOperation::$h,
|
||||||
|
operand: Operand::Reference(arg0)
|
||||||
|
},
|
||||||
|
destination: Destination::Address(destination)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
Ok(EvalPlan {
|
||||||
|
instructions,
|
||||||
|
binding: EpBinding::Single(destination),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_unary!($($r)*);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
define_unary!(Abs Acos Asin Atan Ceil Cos Floor Ln Log10 Log2 Sin Sqrt Tan ToDegrees ToRadians);
|
||||||
|
|
||||||
|
/// The identity function. Always returns its first input.
|
||||||
|
/// Implemented purely on the KCL side so it doesn't need to be in the
|
||||||
|
/// define_unary! macro above.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
|
pub struct Id;
|
||||||
|
|
||||||
impl Callable for Id {
|
impl Callable for Id {
|
||||||
fn call(&self, _: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
fn call(&self, _: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
@ -56,44 +112,53 @@ impl Callable for Id {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A test function that adds two numbers.
|
/// Binary operator macro to quickly create new bindings.
|
||||||
#[derive(Debug, Clone)]
|
macro_rules! define_binary {
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
() => {};
|
||||||
pub struct Add;
|
($h:ident$( $r:ident)*) => {
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
|
pub struct $h;
|
||||||
|
|
||||||
impl Callable for Add {
|
impl Callable for $h {
|
||||||
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||||
let len = args.len();
|
let len = args.len();
|
||||||
if len > 2 {
|
if len > 2 {
|
||||||
return Err(CompileError::TooManyArgs {
|
return Err(CompileError::TooManyArgs {
|
||||||
fn_name: "add".into(),
|
fn_name: "$h".into(),
|
||||||
maximum: 2,
|
maximum: 2,
|
||||||
actual: len,
|
actual: len,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
let not_enough_args = CompileError::NotEnoughArgs {
|
||||||
|
fn_name: "$h".into(),
|
||||||
|
required: 2,
|
||||||
|
actual: len,
|
||||||
|
};
|
||||||
|
const ERR: &str = "cannot use composite values (e.g. array) as arguments to $h";
|
||||||
|
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
|
||||||
|
return Err(CompileError::InvalidOperand(ERR));
|
||||||
|
};
|
||||||
|
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args)? else {
|
||||||
|
return Err(CompileError::InvalidOperand(ERR));
|
||||||
|
};
|
||||||
|
let destination = ctx.next_address.offset_by(1);
|
||||||
|
Ok(EvalPlan {
|
||||||
|
instructions: vec![Instruction::BinaryArithmetic {
|
||||||
|
arithmetic: BinaryArithmetic {
|
||||||
|
operation: BinaryOperation::$h,
|
||||||
|
operand0: Operand::Reference(arg0),
|
||||||
|
operand1: Operand::Reference(arg1),
|
||||||
|
},
|
||||||
|
destination: Destination::Address(destination),
|
||||||
|
}],
|
||||||
|
binding: EpBinding::Single(destination),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
let not_enough_args = CompileError::NotEnoughArgs {
|
|
||||||
fn_name: "add".into(),
|
|
||||||
required: 2,
|
|
||||||
actual: len,
|
|
||||||
};
|
|
||||||
const ERR: &str = "cannot use composite values (e.g. array) as arguments to Add";
|
|
||||||
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
|
|
||||||
return Err(CompileError::InvalidOperand(ERR));
|
|
||||||
};
|
|
||||||
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args)? else {
|
|
||||||
return Err(CompileError::InvalidOperand(ERR));
|
|
||||||
};
|
|
||||||
let destination = ctx.next_address.offset_by(1);
|
|
||||||
Ok(EvalPlan {
|
|
||||||
instructions: vec![Instruction::BinaryArithmetic {
|
|
||||||
arithmetic: BinaryArithmetic {
|
|
||||||
operation: kittycad_execution_plan::BinaryOperation::Add,
|
|
||||||
operand0: kittycad_execution_plan::Operand::Reference(arg0),
|
|
||||||
operand1: kittycad_execution_plan::Operand::Reference(arg1),
|
|
||||||
},
|
|
||||||
destination: Destination::Address(destination),
|
|
||||||
}],
|
|
||||||
binding: EpBinding::Single(destination),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define_binary!($($r)*);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define_binary!(Add Log Max Min);
|
||||||
|
@ -67,6 +67,12 @@ pub fn sg_binding(
|
|||||||
actual: "function".to_owned(),
|
actual: "function".to_owned(),
|
||||||
arg_number,
|
arg_number,
|
||||||
}),
|
}),
|
||||||
|
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "constant".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn single_binding(
|
pub fn single_binding(
|
||||||
@ -101,6 +107,12 @@ pub fn single_binding(
|
|||||||
actual: "function".to_owned(),
|
actual: "function".to_owned(),
|
||||||
arg_number,
|
arg_number,
|
||||||
}),
|
}),
|
||||||
|
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "constant".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +148,12 @@ pub fn sequence_binding(
|
|||||||
actual: "function".to_owned(),
|
actual: "function".to_owned(),
|
||||||
arg_number,
|
arg_number,
|
||||||
}),
|
}),
|
||||||
|
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "constant".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{collections::HashMap, env};
|
use std::{collections::HashMap, env};
|
||||||
|
|
||||||
use ep::{sketch_types, Destination, UnaryArithmetic};
|
use ep::{constants, sketch_types, Destination, UnaryArithmetic};
|
||||||
use ept::{ListHeader, ObjectHeader};
|
use ept::{ListHeader, ObjectHeader, Primitive};
|
||||||
use kittycad_modeling_cmds::shared::Point2d;
|
use kittycad_modeling_cmds::shared::Point2d;
|
||||||
use kittycad_modeling_session::SessionBuilder;
|
use kittycad_modeling_session::SessionBuilder;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@ -1414,3 +1414,31 @@ fn mod_and_pow() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#[tokio::test]
|
||||||
|
async fn cos_sin_pi() {
|
||||||
|
let program = "
|
||||||
|
let x = cos(45.0)*10
|
||||||
|
let y = sin(45.0)*10
|
||||||
|
let z = PI
|
||||||
|
";
|
||||||
|
let (_plan, scope, _) = must_plan(program);
|
||||||
|
let Some(EpBinding::Single(x)) = scope.get("x") else {
|
||||||
|
panic!("Unexpected binding for variable 'x': {:?}", scope.get("x"));
|
||||||
|
};
|
||||||
|
let Some(EpBinding::Single(y)) = scope.get("y") else {
|
||||||
|
panic!("Unexpected binding for variable 'y': {:?}", scope.get("y"));
|
||||||
|
};
|
||||||
|
let Some(EpBinding::Constant(z)) = scope.get("z") else {
|
||||||
|
panic!("Unexpected binding for variable 'z': {:?}", scope.get("z"));
|
||||||
|
};
|
||||||
|
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||||
|
.ast()
|
||||||
|
.unwrap();
|
||||||
|
let mem = crate::execute(ast, &mut None).await.unwrap();
|
||||||
|
use ept::ReadMemory;
|
||||||
|
assert_eq!(*mem.get(x).unwrap(), Primitive::from(5.253219888177298));
|
||||||
|
assert_eq!(*mem.get(y).unwrap(), Primitive::from(8.509035245341185));
|
||||||
|
|
||||||
|
// Constants don't live in memory.
|
||||||
|
assert_eq!(*z, constants::PI);
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::executor::SourceRange;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
@ -71,20 +72,113 @@ struct ToEngineReq {
|
|||||||
request_sent: oneshot::Sender<Result<()>>,
|
request_sent: oneshot::Sender<Result<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_cmd_with_return_values(cmd: &kittycad::types::ModelingCmd) -> bool {
|
||||||
|
let (kittycad::types::ModelingCmd::Export { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Extrude { .. }
|
||||||
|
| kittycad::types::ModelingCmd::SketchModeDisable { .. }
|
||||||
|
| kittycad::types::ModelingCmd::ObjectBringToFront { .. }
|
||||||
|
| kittycad::types::ModelingCmd::SelectWithPoint { .. }
|
||||||
|
| kittycad::types::ModelingCmd::HighlightSetEntity { .. }
|
||||||
|
| kittycad::types::ModelingCmd::EntityGetChildUuid { .. }
|
||||||
|
| kittycad::types::ModelingCmd::EntityGetNumChildren { .. }
|
||||||
|
| kittycad::types::ModelingCmd::EntityGetParentId { .. }
|
||||||
|
| kittycad::types::ModelingCmd::EntityGetAllChildUuids { .. }
|
||||||
|
| kittycad::types::ModelingCmd::CameraDragMove { .. }
|
||||||
|
| kittycad::types::ModelingCmd::CameraDragEnd { .. }
|
||||||
|
| kittycad::types::ModelingCmd::DefaultCameraGetSettings { .. }
|
||||||
|
| kittycad::types::ModelingCmd::DefaultCameraZoom { .. }
|
||||||
|
| kittycad::types::ModelingCmd::SelectGet { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Solid3DGetAllEdgeFaces { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Solid3DGetAllOppositeEdges { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Solid3DGetOppositeEdge { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Solid3DGetNextAdjacentEdge { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Solid3DGetPrevAdjacentEdge { .. }
|
||||||
|
| kittycad::types::ModelingCmd::GetEntityType { .. }
|
||||||
|
| kittycad::types::ModelingCmd::CurveGetControlPoints { .. }
|
||||||
|
| kittycad::types::ModelingCmd::CurveGetType { .. }
|
||||||
|
| kittycad::types::ModelingCmd::MouseClick { .. }
|
||||||
|
| kittycad::types::ModelingCmd::TakeSnapshot { .. }
|
||||||
|
| kittycad::types::ModelingCmd::PathGetInfo { .. }
|
||||||
|
| kittycad::types::ModelingCmd::PathGetCurveUuidsForVertices { .. }
|
||||||
|
| kittycad::types::ModelingCmd::PathGetVertexUuids { .. }
|
||||||
|
| kittycad::types::ModelingCmd::CurveGetEndPoints { .. }
|
||||||
|
| kittycad::types::ModelingCmd::FaceIsPlanar { .. }
|
||||||
|
| kittycad::types::ModelingCmd::FaceGetPosition { .. }
|
||||||
|
| kittycad::types::ModelingCmd::FaceGetGradient { .. }
|
||||||
|
| kittycad::types::ModelingCmd::PlaneIntersectAndProject { .. }
|
||||||
|
| kittycad::types::ModelingCmd::ImportFiles { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Mass { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Volume { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Density { .. }
|
||||||
|
| kittycad::types::ModelingCmd::SurfaceArea { .. }
|
||||||
|
| kittycad::types::ModelingCmd::CenterOfMass { .. }
|
||||||
|
| kittycad::types::ModelingCmd::GetSketchModePlane { .. }
|
||||||
|
| kittycad::types::ModelingCmd::EntityGetDistance { .. }
|
||||||
|
| kittycad::types::ModelingCmd::EntityLinearPattern { .. }
|
||||||
|
| kittycad::types::ModelingCmd::EntityCircularPattern { .. }
|
||||||
|
| kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo { .. }) = cmd
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
impl EngineConnection {
|
impl EngineConnection {
|
||||||
/// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine.
|
/// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine.
|
||||||
async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
|
async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
|
||||||
|
let mut batch: Vec<kittycad::types::ModelingCmdReq> = vec![];
|
||||||
|
|
||||||
while let Some(req) = engine_req_rx.recv().await {
|
while let Some(req) = engine_req_rx.recv().await {
|
||||||
let ToEngineReq { req, request_sent } = req;
|
let ToEngineReq { req, request_sent } = req;
|
||||||
let res = if let kittycad::types::WebSocketRequest::ModelingCmdReq {
|
let kittycad::types::WebSocketRequest::ModelingCmdReq { cmd, cmd_id } = &req else {
|
||||||
cmd: kittycad::types::ModelingCmd::ImportFiles { .. },
|
return;
|
||||||
cmd_id: _,
|
};
|
||||||
} = &req
|
|
||||||
{
|
let res = if let kittycad::types::ModelingCmd::ImportFiles { .. } = cmd {
|
||||||
// Send it as binary.
|
// Send it as binary.
|
||||||
Self::inner_send_to_engine_binary(req, &mut tcp_write).await
|
Self::inner_send_to_engine_binary(req, &mut tcp_write).await
|
||||||
} else {
|
} else {
|
||||||
Self::inner_send_to_engine(req, &mut tcp_write).await
|
// Backported from the new Grackle-core KCL.
|
||||||
|
// We will batch all commands until we hit one which has
|
||||||
|
// return values we want to wait for. Currently, that means
|
||||||
|
// waiting for API requests with return values that are not just
|
||||||
|
// request confirmations (ex. face or edge data).
|
||||||
|
|
||||||
|
batch.push(kittycad::types::ModelingCmdReq {
|
||||||
|
cmd: cmd.clone(),
|
||||||
|
cmd_id: *cmd_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_cmd_with_return_values(cmd) || *cmd_id == uuid::Uuid::nil() {
|
||||||
|
// If the batch has zero commands, and we're about to load
|
||||||
|
// a command that has return values, don't wrap it in a
|
||||||
|
// ModelingCmdBatchReq.
|
||||||
|
let future = if batch.len() == 1 {
|
||||||
|
Self::inner_send_to_engine(
|
||||||
|
kittycad::types::WebSocketRequest::ModelingCmdReq {
|
||||||
|
cmd: cmd.clone(),
|
||||||
|
cmd_id: *cmd_id,
|
||||||
|
},
|
||||||
|
&mut tcp_write,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Serde will properly serialize these.
|
||||||
|
Self::inner_send_to_engine(
|
||||||
|
kittycad::types::WebSocketRequest::ModelingCmdBatchReq {
|
||||||
|
requests: batch.clone(),
|
||||||
|
},
|
||||||
|
&mut tcp_write,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare for a new batch of instructions.
|
||||||
|
batch.clear();
|
||||||
|
|
||||||
|
future.await
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let _ = request_sent.send(res);
|
let _ = request_sent.send(res);
|
||||||
}
|
}
|
||||||
@ -163,7 +257,7 @@ impl EngineManager for EngineConnection {
|
|||||||
async fn send_modeling_cmd(
|
async fn send_modeling_cmd(
|
||||||
&self,
|
&self,
|
||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
source_range: crate::executor::SourceRange,
|
source_range: SourceRange,
|
||||||
cmd: kittycad::types::ModelingCmd,
|
cmd: kittycad::types::ModelingCmd,
|
||||||
) -> Result<OkWebSocketResponseData, KclError> {
|
) -> Result<OkWebSocketResponseData, KclError> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -200,7 +294,18 @@ impl EngineManager for EngineConnection {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Wait for the response.
|
// Only wait for a response if it's a command *with* return values
|
||||||
|
// So most of the time, this condition will be true.
|
||||||
|
if !is_cmd_with_return_values(&cmd) {
|
||||||
|
// Simulate an empty response type
|
||||||
|
return Ok(OkWebSocketResponseData::Modeling {
|
||||||
|
modeling_response: kittycad::types::OkModelingCmdResponse::Empty {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a submitted batch, we want to check the batch return value
|
||||||
|
// in case of any errors.
|
||||||
|
|
||||||
let current_time = std::time::Instant::now();
|
let current_time = std::time::Instant::now();
|
||||||
while current_time.elapsed().as_secs() < 60 {
|
while current_time.elapsed().as_secs() < 60 {
|
||||||
if let Ok(guard) = self.socket_health.lock() {
|
if let Ok(guard) = self.socket_health.lock() {
|
||||||
|
@ -10,6 +10,24 @@ pub mod conn_wasm;
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||||
|
/// Tell the EngineManager there will be no more commands.
|
||||||
|
/// We send a "dummy command" to signal EngineConnection that we're
|
||||||
|
/// at the end of the program and there'll be no more requests.
|
||||||
|
/// This means in tests, where it's impossible to look ahead, we'll need to
|
||||||
|
/// add this to mark the end of commands.
|
||||||
|
/// In compiled KCL tests, it will be auto-inserted.
|
||||||
|
async fn signal_end(&self) -> Result<kittycad::types::OkWebSocketResponseData, crate::errors::KclError> {
|
||||||
|
self.send_modeling_cmd(
|
||||||
|
// THE NIL UUID IS THE SIGNAL OF THE END OF TIMES FOR THIS POOR PROGRAM.
|
||||||
|
uuid::Uuid::nil(),
|
||||||
|
// This will be ignored.
|
||||||
|
crate::executor::SourceRange([0, 0]),
|
||||||
|
// This will be ignored. It was one I found with no fields.
|
||||||
|
kittycad::types::ModelingCmd::EditModeExit {},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a modeling command and wait for the response message.
|
/// Send a modeling command and wait for the response message.
|
||||||
async fn send_modeling_cmd(
|
async fn send_modeling_cmd(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1219,6 +1219,9 @@ pub async fn execute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signal to engine we're done. Flush the batch.
|
||||||
|
ctx.engine.signal_end().await?;
|
||||||
|
|
||||||
Ok(memory.clone())
|
Ok(memory.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +165,7 @@ async fn inner_get_opposite_edge(tag: String, extrude_group: Box<ExtrudeGroup>,
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let kittycad::types::OkWebSocketResponseData::Modeling {
|
let kittycad::types::OkWebSocketResponseData::Modeling {
|
||||||
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetOppositeEdge { data: opposite_edge },
|
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetOppositeEdge { data: opposite_edge },
|
||||||
} = &resp
|
} = &resp
|
||||||
|
@ -915,7 +915,7 @@ async fn start_sketch_on_face(
|
|||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
message: format!("Expected a face with the tag `{}`", tag),
|
message: format!("Expected a face with the tag `{}` for sketch", tag),
|
||||||
source_ranges: vec![args.source_range],
|
source_ranges: vec![args.source_range],
|
||||||
})
|
})
|
||||||
})??,
|
})??,
|
||||||
|
Reference in New Issue
Block a user