Implement as aliases for sub-expressions (#4723)

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2024-12-11 21:26:42 +13:00
committed by GitHub
parent 0400e6228e
commit 87f50cd5e9
12 changed files with 510 additions and 175 deletions

View File

@ -326,29 +326,12 @@ async fn inner_execute_pipe_body(
ctx: &ExecutorContext,
) -> Result<KclValue, KclError> {
for expression in body {
match expression {
Expr::TagDeclarator(_) => {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("This cannot be in a PipeExpression: {:?}", expression),
source_ranges: vec![expression.into()],
}));
}
Expr::Literal(_)
| Expr::Identifier(_)
| Expr::BinaryExpression(_)
| Expr::FunctionExpression(_)
| Expr::CallExpression(_)
| Expr::CallExpressionKw(_)
| Expr::PipeExpression(_)
| Expr::PipeSubstitution(_)
| Expr::ArrayExpression(_)
| Expr::ArrayRangeExpression(_)
| Expr::ObjectExpression(_)
| Expr::MemberExpression(_)
| Expr::UnaryExpression(_)
| Expr::IfExpression(_)
| Expr::None(_) => {}
};
if let Expr::TagDeclarator(_) = expression {
return Err(KclError::Semantic(KclErrorDetails {
message: format!("This cannot be in a PipeExpression: {:?}", expression),
source_ranges: vec![expression.into()],
}));
}
let metadata = Metadata {
source_range: SourceRange::from(expression),
};

View File

@ -2117,7 +2117,8 @@ impl ExecutorContext {
Ok((module_memory, module_exports))
}
pub async fn execute_expr<'a>(
#[async_recursion]
pub async fn execute_expr<'a: 'async_recursion>(
&self,
init: &Expr,
exec_state: &mut ExecState,
@ -2174,6 +2175,14 @@ impl ExecutorContext {
Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state)?,
Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
Expr::LabelledExpression(expr) => {
let result = self
.execute_expr(&expr.expr, exec_state, metadata, statement_kind)
.await?;
exec_state.memory.add(&expr.label.name, result.clone(), init.into())?;
// TODO this lets us use the label as a variable name, but not as a tag in most cases
result
}
};
Ok(item)
}

View File

@ -1,6 +1,6 @@
use sha2::{Digest as DigestTrait, Sha256};
use super::types::{DefaultParamVal, ItemVisibility, VariableKind};
use super::types::{DefaultParamVal, ItemVisibility, LabelledExpression, VariableKind};
use crate::parsing::ast::types::{
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw,
CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression,
@ -115,6 +115,7 @@ impl Expr {
Expr::MemberExpression(me) => me.compute_digest(),
Expr::UnaryExpression(ue) => ue.compute_digest(),
Expr::IfExpression(e) => e.compute_digest(),
Expr::LabelledExpression(e) => e.compute_digest(),
Expr::None(_) => {
let mut hasher = Sha256::new();
hasher.update(b"Value::None");
@ -396,6 +397,13 @@ impl UnaryExpression {
});
}
impl LabelledExpression {
compute_digest!(|slf, hasher| {
hasher.update(slf.expr.compute_digest());
hasher.update(slf.label.compute_digest());
});
}
impl PipeExpression {
compute_digest!(|slf, hasher| {
hasher.update(slf.body.len().to_ne_bytes());

View File

@ -36,6 +36,7 @@ impl Expr {
Expr::MemberExpression(member_expression) => member_expression.module_id,
Expr::UnaryExpression(unary_expression) => unary_expression.module_id,
Expr::IfExpression(expr) => expr.module_id,
Expr::LabelledExpression(expr) => expr.expr.module_id(),
Expr::None(none) => none.module_id,
}
}

View File

@ -598,6 +598,7 @@ pub enum Expr {
MemberExpression(BoxNode<MemberExpression>),
UnaryExpression(BoxNode<UnaryExpression>),
IfExpression(BoxNode<IfExpression>),
LabelledExpression(BoxNode<LabelledExpression>),
None(Node<KclNone>),
}
@ -640,6 +641,7 @@ impl Expr {
Expr::UnaryExpression(_unary_exp) => None,
Expr::PipeSubstitution(_pipe_substitution) => None,
Expr::IfExpression(_) => None,
Expr::LabelledExpression(expr) => expr.expr.get_non_code_meta(),
Expr::None(_none) => None,
}
}
@ -666,6 +668,7 @@ impl Expr {
Expr::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
Expr::IfExpression(_) => {}
Expr::PipeSubstitution(_) => {}
Expr::LabelledExpression(expr) => expr.expr.replace_value(source_range, new_value),
Expr::None(_) => {}
}
}
@ -687,6 +690,7 @@ impl Expr {
Expr::MemberExpression(member_expression) => member_expression.start,
Expr::UnaryExpression(unary_expression) => unary_expression.start,
Expr::IfExpression(expr) => expr.start,
Expr::LabelledExpression(expr) => expr.start,
Expr::None(none) => none.start,
}
}
@ -708,6 +712,7 @@ impl Expr {
Expr::MemberExpression(member_expression) => member_expression.end,
Expr::UnaryExpression(unary_expression) => unary_expression.end,
Expr::IfExpression(expr) => expr.end,
Expr::LabelledExpression(expr) => expr.end,
Expr::None(none) => none.end,
}
}
@ -734,6 +739,8 @@ impl Expr {
Expr::Literal(_) => None,
Expr::Identifier(_) => None,
Expr::TagDeclarator(_) => None,
// TODO LSP hover info for tag
Expr::LabelledExpression(expr) => expr.expr.get_hover_value_for_position(pos, code),
// TODO: LSP hover information for symbols. https://github.com/KittyCAD/modeling-app/issues/1127
Expr::PipeSubstitution(_) => None,
}
@ -763,6 +770,7 @@ impl Expr {
}
Expr::UnaryExpression(ref mut unary_expression) => unary_expression.rename_identifiers(old_name, new_name),
Expr::IfExpression(ref mut expr) => expr.rename_identifiers(old_name, new_name),
Expr::LabelledExpression(expr) => expr.expr.rename_identifiers(old_name, new_name),
Expr::None(_) => {}
}
}
@ -788,9 +796,19 @@ impl Expr {
Expr::MemberExpression(member_expression) => member_expression.get_constraint_level(),
Expr::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
Expr::IfExpression(expr) => expr.get_constraint_level(),
Expr::LabelledExpression(expr) => expr.expr.get_constraint_level(),
Expr::None(none) => none.get_constraint_level(),
}
}
pub fn has_substitution_arg(&self) -> bool {
match self {
Expr::CallExpression(call_expression) => call_expression.has_substitution_arg(),
Expr::CallExpressionKw(call_expression) => call_expression.has_substitution_arg(),
Expr::LabelledExpression(expr) => expr.expr.has_substitution_arg(),
_ => false,
}
}
}
impl From<Expr> for SourceRange {
@ -805,6 +823,36 @@ impl From<&Expr> for SourceRange {
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]
pub struct LabelledExpression {
pub expr: Expr,
pub label: Node<Identifier>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub digest: Option<Digest>,
}
impl LabelledExpression {
pub(crate) fn new(expr: Expr, label: Node<Identifier>) -> Node<LabelledExpression> {
let start = expr.start();
let end = label.end;
let module_id = expr.module_id();
Node::new(
LabelledExpression {
expr,
label,
digest: None,
},
start,
end,
module_id,
)
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type")]

View File

@ -33,6 +33,8 @@ use crate::{
SourceRange,
};
use super::ast::types::LabelledExpression;
thread_local! {
/// The current `ParseContext`. `None` if parsing is not currently happening on this thread.
static CTXT: RefCell<Option<ParseContext>> = const { RefCell::new(None) };
@ -337,7 +339,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
let mut values = vec![head];
let value_surrounded_by_comments = (
repeat(0.., preceded(opt(whitespace), non_code_node)), // Before the expression.
preceded(opt(whitespace), fn_call), // The expression.
preceded(opt(whitespace), labelled_fn_call), // The expression.
repeat(0.., noncode_just_after_code), // After the expression.
);
let tail: Vec<(Vec<_>, _, Vec<_>)> = repeat(
@ -353,7 +355,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
// First, ensure they all have a % in their args.
let calls_without_substitution = tail.iter().find_map(|(_nc, call_expr, _nc2)| {
if !call_expr.has_substitution_arg() {
Some(call_expr.as_source_range())
Some(call_expr.into())
} else {
None
}
@ -373,7 +375,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
max_noncode_end = nc.end.max(max_noncode_end);
non_code_meta.insert(code_count, nc);
}
values.push(Expr::CallExpression(Box::new(code)));
values.push(code);
code_count += 1;
for nc in noncode_after {
max_noncode_end = nc.end.max(max_noncode_end);
@ -527,7 +529,8 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
| Expr::PipeSubstitution(_)
| Expr::ArrayExpression(_)
| Expr::ArrayRangeExpression(_)
| Expr::ObjectExpression(_) => return Err(CompilationError::fatal(source_range, TODO_783)),
| Expr::ObjectExpression(_)
| Expr::LabelledExpression(..) => return Err(CompilationError::fatal(source_range, TODO_783)),
Expr::None(_) => {
return Err(CompilationError::fatal(
source_range,
@ -1628,13 +1631,34 @@ fn expression(i: &mut TokenSlice) -> PResult<Expr> {
}
fn expression_but_not_pipe(i: &mut TokenSlice) -> PResult<Expr> {
alt((
let expr = alt((
binary_expression.map(Box::new).map(Expr::BinaryExpression),
unary_expression.map(Box::new).map(Expr::UnaryExpression),
expr_allowed_in_pipe_expr,
))
.context(expected("a KCL value"))
.parse_next(i)
.parse_next(i)?;
let label = opt(label).parse_next(i)?;
match label {
Some(label) => Ok(Expr::LabelledExpression(Box::new(LabelledExpression::new(expr, label)))),
None => Ok(expr),
}
}
fn label(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
let result = preceded(
(whitespace, import_as_keyword, whitespace),
identifier.context(expected("an identifier")),
)
.parse_next(i)?;
ParseContext::warn(CompilationError::err(
SourceRange::new(result.start, result.end, result.module_id),
"Using `as` for tagging expressions is experimental, likely to be buggy, and likely to change",
));
Ok(result)
}
fn unnecessarily_bracketed(i: &mut TokenSlice) -> PResult<Expr> {
@ -2450,6 +2474,17 @@ fn typecheck(spec_arg: &crate::docs::StdLibFnArg, arg: &&Expr) -> PResult<()> {
Ok(())
}
fn labelled_fn_call(i: &mut TokenSlice) -> PResult<Expr> {
let call = fn_call.parse_next(i)?;
let expr = Expr::CallExpression(Box::new(call));
let label = opt(label).parse_next(i)?;
match label {
Some(label) => Ok(Expr::LabelledExpression(Box::new(LabelledExpression::new(expr, label)))),
None => Ok(expr),
}
}
fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
let fn_name = identifier(i)?;
opt(whitespace).parse_next(i)?;

View File

@ -194,6 +194,12 @@ impl Expr {
Expr::UnaryExpression(unary_exp) => unary_exp.recast(options),
Expr::IfExpression(e) => e.recast(options, indentation_level, ctxt),
Expr::PipeSubstitution(_) => crate::parsing::PIPE_SUBSTITUTION_OPERATOR.to_string(),
Expr::LabelledExpression(e) => {
let mut result = e.expr.recast(options, indentation_level, ctxt);
result += " as ";
result += &e.label.name;
result
}
Expr::None(_) => {
unimplemented!("there is no literal None, see https://github.com/KittyCAD/modeling-app/issues/1115")
}
@ -407,7 +413,8 @@ fn expr_is_trivial(expr: &Expr) -> bool {
| Expr::ObjectExpression(_)
| Expr::MemberExpression(_)
| Expr::UnaryExpression(_)
| Expr::IfExpression(_) => false,
| Expr::IfExpression(_)
| Expr::LabelledExpression(_) => false,
}
}
@ -1519,6 +1526,28 @@ tabs_l = startSketchOn({
);
}
#[test]
fn test_as() {
let some_program_string = r#"fn cube(pos, scale) {
x = dfsfs + dfsfsd as y
sg = startSketchOn('XY')
|> startProfileAt(pos, %) as foo
|> line([0, scale], %)
|> line([scale, 0], %) as bar
|> line([0 as baz, -scale] as qux, %)
|> close(%)
|> extrude(scale, %)
}
cube(0, 0) as cub
"#;
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(recasted, some_program_string,);
}
#[test]
fn test_recast_with_bad_indentation() {
let some_program_string = r#"part001 = startSketchOn('XY')

View File

@ -32,6 +32,7 @@ pub enum Node<'a> {
UnaryExpression(NodeRef<'a, types::UnaryExpression>),
IfExpression(NodeRef<'a, types::IfExpression>),
ElseIf(&'a types::ElseIf),
LabelledExpression(NodeRef<'a, types::LabelledExpression>),
Parameter(&'a types::Parameter),
@ -79,6 +80,7 @@ impl TryFrom<&Node<'_>> for SourceRange {
Node::ObjectProperty(n) => SourceRange::from(*n),
Node::MemberObject(m) => SourceRange::new(m.start(), m.end(), m.module_id()),
Node::IfExpression(n) => SourceRange::from(*n),
Node::LabelledExpression(n) => SourceRange::from(*n),
Node::LiteralIdentifier(l) => SourceRange::new(l.start(), l.end(), l.module_id()),
// This is broken too
@ -120,6 +122,7 @@ impl<'tree> From<&'tree types::Expr> for Node<'tree> {
types::Expr::MemberExpression(me) => me.as_ref().into(),
types::Expr::UnaryExpression(ue) => ue.as_ref().into(),
types::Expr::IfExpression(e) => e.as_ref().into(),
types::Expr::LabelledExpression(e) => e.as_ref().into(),
types::Expr::None(n) => n.into(),
}
}
@ -185,5 +188,6 @@ impl_from_ref!(Node, Parameter);
impl_from_ref!(Node, MemberObject);
impl_from!(Node, IfExpression);
impl_from!(Node, ElseIf);
impl_from!(Node, LabelledExpression);
impl_from_ref!(Node, LiteralIdentifier);
impl_from!(Node, KclNone);

View File

@ -127,6 +127,9 @@ impl<'tree> Visitable<'tree> for Node<'tree> {
Node::ElseIf(n) => {
vec![(&n.cond).into(), n.then_val.as_ref().into()]
}
Node::LabelledExpression(e) => {
vec![(&e.expr).into(), (&e.label).into()]
}
Node::PipeSubstitution(_)
| Node::TagDeclarator(_)
| Node::Identifier(_)

View File

@ -296,7 +296,7 @@ description: Result of parsing sketch_on_face_start.kcl
},
{
"declaration": {
"end": 236,
"end": 243,
"id": {
"end": 183,
"name": "part001",
@ -372,35 +372,47 @@ description: Result of parsing sketch_on_face_start.kcl
"type": "CallExpression"
},
{
"arguments": [
{
"end": 232,
"raw": "20",
"start": 230,
"type": "Literal",
"type": "Literal",
"value": 20.0
"end": 243,
"expr": {
"arguments": [
{
"end": 232,
"raw": "20",
"start": 230,
"type": "Literal",
"type": "Literal",
"value": 20.0
},
{
"end": 235,
"start": 234,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 229,
"name": "extrude",
"start": 222,
"type": "Identifier"
},
{
"end": 235,
"start": 234,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 229,
"name": "extrude",
"end": 236,
"start": 222,
"type": "CallExpression",
"type": "CallExpression"
},
"label": {
"end": 243,
"name": "foo",
"start": 240,
"type": "Identifier"
},
"end": 236,
"start": 222,
"type": "CallExpression",
"type": "CallExpression"
"type": "LabelledExpression",
"type": "LabelledExpression"
}
],
"end": 236,
"end": 243,
"start": 186,
"type": "PipeExpression",
"type": "PipeExpression"
@ -408,7 +420,7 @@ description: Result of parsing sketch_on_face_start.kcl
"start": 176,
"type": "VariableDeclarator"
},
"end": 236,
"end": 243,
"kind": "const",
"start": 176,
"type": "VariableDeclaration",
@ -416,11 +428,11 @@ description: Result of parsing sketch_on_face_start.kcl
},
{
"declaration": {
"end": 410,
"end": 413,
"id": {
"end": 245,
"end": 252,
"name": "part002",
"start": 238,
"start": 245,
"type": "Identifier"
},
"init": {
@ -428,29 +440,29 @@ description: Result of parsing sketch_on_face_start.kcl
{
"arguments": [
{
"end": 269,
"name": "part001",
"start": 262,
"end": 272,
"name": "foo",
"start": 269,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 278,
"end": 281,
"raw": "\"start\"",
"start": 271,
"start": 274,
"type": "Literal",
"type": "Literal",
"value": "start"
}
],
"callee": {
"end": 261,
"end": 268,
"name": "startSketchOn",
"start": 248,
"start": 255,
"type": "Identifier"
},
"end": 279,
"start": 248,
"end": 282,
"start": 255,
"type": "CallExpression",
"type": "CallExpression"
},
@ -458,14 +470,6 @@ description: Result of parsing sketch_on_face_start.kcl
"arguments": [
{
"elements": [
{
"end": 302,
"raw": "0",
"start": 301,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 305,
"raw": "0",
@ -473,28 +477,36 @@ description: Result of parsing sketch_on_face_start.kcl
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 308,
"raw": "0",
"start": 307,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 306,
"start": 300,
"end": 309,
"start": 303,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 309,
"start": 308,
"end": 312,
"start": 311,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 299,
"end": 302,
"name": "startProfileAt",
"start": 285,
"start": 288,
"type": "Identifier"
},
"end": 310,
"start": 285,
"end": 313,
"start": 288,
"type": "CallExpression",
"type": "CallExpression"
},
@ -503,42 +515,42 @@ description: Result of parsing sketch_on_face_start.kcl
{
"elements": [
{
"end": 323,
"end": 326,
"raw": "0",
"start": 322,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 327,
"raw": "10",
"start": 325,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"end": 330,
"raw": "10",
"start": 328,
"type": "Literal",
"type": "Literal",
"value": 10.0
}
],
"end": 328,
"start": 321,
"end": 331,
"start": 324,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 331,
"start": 330,
"end": 334,
"start": 333,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 320,
"end": 323,
"name": "line",
"start": 316,
"start": 319,
"type": "Identifier"
},
"end": 332,
"start": 316,
"end": 335,
"start": 319,
"type": "CallExpression",
"type": "CallExpression"
},
@ -547,42 +559,42 @@ description: Result of parsing sketch_on_face_start.kcl
{
"elements": [
{
"end": 346,
"end": 349,
"raw": "10",
"start": 344,
"start": 347,
"type": "Literal",
"type": "Literal",
"value": 10.0
},
{
"end": 349,
"end": 352,
"raw": "0",
"start": 348,
"start": 351,
"type": "Literal",
"type": "Literal",
"value": 0.0
}
],
"end": 350,
"start": 343,
"end": 353,
"start": 346,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 353,
"start": 352,
"end": 356,
"start": 355,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 342,
"end": 345,
"name": "line",
"start": 338,
"start": 341,
"type": "Identifier"
},
"end": 354,
"start": 338,
"end": 357,
"start": 341,
"type": "CallExpression",
"type": "CallExpression"
},
@ -591,123 +603,123 @@ description: Result of parsing sketch_on_face_start.kcl
{
"elements": [
{
"end": 367,
"end": 370,
"raw": "0",
"start": 366,
"start": 369,
"type": "Literal",
"type": "Literal",
"value": 0.0
},
{
"argument": {
"end": 372,
"end": 375,
"raw": "10",
"start": 370,
"start": 373,
"type": "Literal",
"type": "Literal",
"value": 10.0
},
"end": 372,
"end": 375,
"operator": "-",
"start": 369,
"start": 372,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
],
"end": 373,
"start": 365,
"end": 376,
"start": 368,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{
"end": 376,
"start": 375,
"end": 379,
"start": 378,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 364,
"end": 367,
"name": "line",
"start": 360,
"start": 363,
"type": "Identifier"
},
"end": 377,
"start": 360,
"end": 380,
"start": 363,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 390,
"start": 389,
"end": 393,
"start": 392,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 388,
"end": 391,
"name": "close",
"start": 383,
"start": 386,
"type": "Identifier"
},
"end": 391,
"start": 383,
"end": 394,
"start": 386,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 406,
"end": 409,
"raw": "5",
"start": 405,
"start": 408,
"type": "Literal",
"type": "Literal",
"value": 5.0
},
{
"end": 409,
"start": 408,
"end": 412,
"start": 411,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 404,
"end": 407,
"name": "extrude",
"start": 397,
"start": 400,
"type": "Identifier"
},
"end": 410,
"start": 397,
"end": 413,
"start": 400,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 410,
"start": 248,
"end": 413,
"start": 255,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 238,
"start": 245,
"type": "VariableDeclarator"
},
"end": 410,
"end": 413,
"kind": "const",
"start": 238,
"start": 245,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"end": 411,
"end": 414,
"nonCodeMeta": {
"nonCodeNodes": {
"1": [
{
"end": 238,
"start": 236,
"end": 245,
"start": 243,
"type": "NonCodeNode",
"value": {
"type": "newLine"

View File

@ -9,9 +9,9 @@ fn cube(pos, scale) {
}
part001 = cube([0, 0], 20)
|> close(%)
|> extrude(20, %)
|> extrude(20, %) as foo
part002 = startSketchOn(part001, "start")
part002 = startSketchOn(foo, "start")
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)

View File

@ -339,6 +339,209 @@ description: Program memory after executing sketch_on_face_start.kcl
}
]
},
"foo": {
"type": "Solid",
"type": "Solid",
"id": "[uuid]",
"value": [
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
86,
105,
0
],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
113,
132,
0
],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
140,
160,
0
],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
208,
216,
0
],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
86,
105,
0
]
},
"from": [
0.0,
0.0
],
"tag": null,
"to": [
0.0,
20.0
],
"type": "ToPoint"
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
113,
132,
0
]
},
"from": [
0.0,
20.0
],
"tag": null,
"to": [
20.0,
20.0
],
"type": "ToPoint"
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
140,
160,
0
]
},
"from": [
20.0,
20.0
],
"tag": null,
"to": [
20.0,
0.0
],
"type": "ToPoint"
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
208,
216,
0
]
},
"from": [
20.0,
0.0
],
"tag": null,
"to": [
0.0,
0.0
],
"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": [
56,
78,
0
]
}
},
"__meta": [
{
"sourceRange": [
56,
78,
0
]
}
]
},
"height": 20.0,
"startCapId": "[uuid]",
"endCapId": "[uuid]",
"__meta": [
{
"sourceRange": [
56,
78,
0
]
}
]
},
"part001": {
"type": "Solid",
"type": "Solid",
@ -551,8 +754,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
316,
332,
319,
335,
0
],
"tag": null,
@ -562,8 +765,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
338,
354,
341,
357,
0
],
"tag": null,
@ -573,8 +776,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
360,
377,
363,
380,
0
],
"tag": null,
@ -584,8 +787,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [
383,
391,
386,
394,
0
],
"tag": null,
@ -600,8 +803,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
316,
332,
319,
335,
0
]
},
@ -620,8 +823,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
338,
354,
341,
357,
0
]
},
@ -640,8 +843,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
360,
377,
363,
380,
0
]
},
@ -660,8 +863,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
383,
391,
386,
394,
0
]
},
@ -901,8 +1104,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__meta": [
{
"sourceRange": [
248,
279,
255,
282,
0
]
}
@ -921,8 +1124,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__geoMeta": {
"id": "[uuid]",
"sourceRange": [
285,
310,
288,
313,
0
]
}
@ -930,8 +1133,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__meta": [
{
"sourceRange": [
285,
310,
288,
313,
0
]
}
@ -943,8 +1146,8 @@ description: Program memory after executing sketch_on_face_start.kcl
"__meta": [
{
"sourceRange": [
285,
310,
288,
313,
0
]
}