2023-11-08 20:23:59 -06:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
2023-09-12 14:59:40 -07:00
|
|
|
use anyhow::Result;
|
2023-11-08 20:23:59 -06:00
|
|
|
use kcl_lib::{engine::EngineManager, std::StdLib};
|
2023-09-12 14:59:40 -07:00
|
|
|
|
|
|
|
/// Executes a kcl program and takes a snapshot of the result.
|
|
|
|
/// This returns the bytes of the snapshot.
|
|
|
|
async fn execute_and_snapshot(code: &str) -> Result<image::DynamicImage> {
|
|
|
|
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
|
|
|
let http_client = reqwest::Client::builder()
|
|
|
|
.user_agent(user_agent)
|
|
|
|
// For file conversions we need this to be long.
|
|
|
|
.timeout(std::time::Duration::from_secs(600))
|
|
|
|
.connect_timeout(std::time::Duration::from_secs(60));
|
|
|
|
let ws_client = reqwest::Client::builder()
|
|
|
|
.user_agent(user_agent)
|
|
|
|
// For file conversions we need this to be long.
|
|
|
|
.timeout(std::time::Duration::from_secs(600))
|
|
|
|
.connect_timeout(std::time::Duration::from_secs(60))
|
2023-09-20 19:35:37 -07:00
|
|
|
.connection_verbose(true)
|
2023-09-12 14:59:40 -07:00
|
|
|
.tcp_keepalive(std::time::Duration::from_secs(600))
|
|
|
|
.http1_only();
|
|
|
|
|
|
|
|
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
|
|
|
|
|
|
|
// Create the client.
|
|
|
|
let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
|
|
|
|
|
|
|
let ws = client
|
|
|
|
.modeling()
|
|
|
|
.commands_ws(None, None, None, None, Some(false))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
// Create a temporary file to write the output to.
|
|
|
|
let output_file = std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
|
|
|
|
|
2023-09-24 20:01:17 -05:00
|
|
|
let tokens = kcl_lib::token::lexer(code);
|
2023-09-12 14:59:40 -07:00
|
|
|
let parser = kcl_lib::parser::Parser::new(tokens);
|
|
|
|
let program = parser.ast()?;
|
|
|
|
let mut mem: kcl_lib::executor::ProgramMemory = Default::default();
|
2023-09-20 18:27:08 -07:00
|
|
|
let engine = kcl_lib::engine::EngineConnection::new(ws).await?;
|
2023-10-05 14:27:48 -07:00
|
|
|
let planes = kcl_lib::executor::DefaultPlanes::new(&engine).await?;
|
2023-11-08 20:23:59 -06:00
|
|
|
let ctx = kcl_lib::executor::ExecutorContext {
|
|
|
|
engine,
|
|
|
|
planes,
|
|
|
|
stdlib: Arc::new(StdLib::default()),
|
|
|
|
};
|
2023-10-05 14:27:48 -07:00
|
|
|
let _ = kcl_lib::executor::execute(program, &mut mem, kcl_lib::executor::BodyType::Root, &ctx).await?;
|
2023-09-12 14:59:40 -07:00
|
|
|
|
|
|
|
// Send a snapshot request to the engine.
|
2023-10-05 14:27:48 -07:00
|
|
|
let resp = ctx
|
|
|
|
.engine
|
2023-09-20 18:27:08 -07:00
|
|
|
.send_modeling_cmd(
|
2023-09-17 21:57:43 -07:00
|
|
|
uuid::Uuid::new_v4(),
|
|
|
|
kcl_lib::executor::SourceRange::default(),
|
|
|
|
kittycad::types::ModelingCmd::TakeSnapshot {
|
|
|
|
format: kittycad::types::ImageFormat::Png,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await?;
|
2023-09-12 14:59:40 -07:00
|
|
|
|
2023-09-15 20:45:28 -07:00
|
|
|
if let kittycad::types::OkWebSocketResponseData::Modeling {
|
|
|
|
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
|
|
|
|
} = &resp
|
|
|
|
{
|
|
|
|
// Save the snapshot locally.
|
|
|
|
std::fs::write(&output_file, &data.contents.0)?;
|
|
|
|
} else {
|
|
|
|
anyhow::bail!("Unexpected response from engine: {:?}", resp);
|
|
|
|
}
|
2023-09-12 14:59:40 -07:00
|
|
|
|
|
|
|
// Read the output file.
|
|
|
|
let actual = image::io::Reader::open(output_file).unwrap().decode().unwrap();
|
|
|
|
Ok(actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-09-17 21:57:43 -07:00
|
|
|
async fn serial_test_execute_with_function_sketch() {
|
2023-09-13 11:42:09 -07:00
|
|
|
let code = r#"fn box = (h, l, w) => {
|
2023-10-05 14:27:48 -07:00
|
|
|
const myBox = startSketchOn('XY')
|
|
|
|
|> startProfileAt([0,0], %)
|
2023-09-12 14:59:40 -07:00
|
|
|
|> line([0, l], %)
|
|
|
|
|> line([w, 0], %)
|
|
|
|
|> line([0, -l], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(h, %)
|
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
const fnBox = box(3, 6, 10)
|
|
|
|
|
|
|
|
show(fnBox)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/function_sketch.png", &result, 0.999);
|
2023-09-12 14:59:40 -07:00
|
|
|
}
|
|
|
|
|
2023-09-19 14:20:14 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn serial_test_execute_with_function_sketch_with_position() {
|
|
|
|
let code = r#"fn box = (p, h, l, w) => {
|
2023-10-05 14:27:48 -07:00
|
|
|
const myBox = startSketchOn('XY')
|
|
|
|
|> startProfileAt(p, %)
|
2023-09-19 14:20:14 -07:00
|
|
|
|> line([0, l], %)
|
|
|
|
|> line([w, 0], %)
|
|
|
|
|> line([0, -l], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(h, %)
|
|
|
|
|
|
|
|
return myBox
|
|
|
|
}
|
|
|
|
|
|
|
|
show(box([0,0], 3, 6, 10))"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image(
|
|
|
|
"tests/executor/outputs/function_sketch_with_position.png",
|
|
|
|
&result,
|
|
|
|
0.999,
|
|
|
|
);
|
2023-09-19 14:20:14 -07:00
|
|
|
}
|
|
|
|
|
2023-09-12 14:59:40 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-09-17 21:57:43 -07:00
|
|
|
async fn serial_test_execute_with_angled_line() {
|
2023-10-05 14:27:48 -07:00
|
|
|
let code = r#"const part001 = startSketchOn('XY')
|
|
|
|
|> startProfileAt([4.83, 12.56], %)
|
2023-09-12 14:59:40 -07:00
|
|
|
|> line([15.1, 2.48], %)
|
|
|
|
|> line({ to: [3.15, -9.85], tag: 'seg01' }, %)
|
|
|
|
|> line([-15.17, -4.1], %)
|
|
|
|
|> angledLine([segAng('seg01', %), 12.35], %)
|
|
|
|
|> line([-13.02, 10.03], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(4, %)
|
|
|
|
|
|
|
|
show(part001)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/angled_line.png", &result, 0.999);
|
2023-09-12 14:59:40 -07:00
|
|
|
}
|
2023-09-15 20:45:28 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-09-17 21:57:43 -07:00
|
|
|
async fn serial_test_execute_parametric_example() {
|
2023-09-15 20:45:28 -07:00
|
|
|
let code = r#"const sigmaAllow = 35000 // psi
|
|
|
|
const width = 9 // inch
|
|
|
|
const p = 150 // Force on shelf - lbs
|
|
|
|
const distance = 6 // inches
|
|
|
|
const FOS = 2
|
|
|
|
|
|
|
|
const leg1 = 5 // inches
|
|
|
|
const leg2 = 8 // inches
|
|
|
|
const thickness = sqrt(distance * p * FOS * 6 / sigmaAllow / width) // inches
|
2023-10-05 14:27:48 -07:00
|
|
|
const bracket = startSketchOn('XY')
|
|
|
|
|> startProfileAt([0, 0], %)
|
2023-09-15 20:45:28 -07:00
|
|
|
|> line([0, leg1], %)
|
|
|
|
|> line([leg2, 0], %)
|
|
|
|
|> line([0, -thickness], %)
|
|
|
|
|> line([-leg2 + thickness, 0], %)
|
|
|
|
|> line([0, -leg1 + thickness], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(width, %)
|
|
|
|
|
|
|
|
show(bracket)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/parametric.png", &result, 0.999);
|
2023-09-15 20:45:28 -07:00
|
|
|
}
|
2023-09-18 17:31:11 -07:00
|
|
|
|
2023-10-02 16:14:09 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn serial_test_execute_parametric_with_tan_arc_example() {
|
|
|
|
let code = r#"const sigmaAllow = 15000 // psi
|
|
|
|
const width = 11 // inch
|
|
|
|
const p = 150 // Force on shelf - lbs
|
|
|
|
const distance = 12 // inches
|
|
|
|
const FOS = 2
|
|
|
|
const thickness = sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))
|
|
|
|
const filletR = thickness * 2
|
|
|
|
const shelfMountL = 9
|
|
|
|
const wallMountL = 8
|
|
|
|
|
|
|
|
const bracket = startSketchAt([0, 0])
|
|
|
|
|> line([0, wallMountL], %)
|
2023-10-12 11:50:54 -05:00
|
|
|
|> tangentialArc({
|
2023-10-02 16:14:09 -07:00
|
|
|
radius: filletR,
|
|
|
|
offset: 90
|
|
|
|
}, %)
|
|
|
|
|> line([-shelfMountL, 0], %)
|
|
|
|
|> line([0, -thickness], %)
|
|
|
|
|> line([shelfMountL, 0], %)
|
2023-10-12 11:50:54 -05:00
|
|
|
|> tangentialArc({
|
2023-10-02 16:14:09 -07:00
|
|
|
radius: filletR - thickness,
|
|
|
|
offset: -90
|
|
|
|
}, %)
|
|
|
|
|> line([0, -wallMountL], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(width, %)
|
|
|
|
|
|
|
|
show(bracket)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/parametric_with_tan_arc.png", &result, 0.999);
|
2023-10-02 16:14:09 -07:00
|
|
|
}
|
|
|
|
|
2023-09-18 17:31:11 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn serial_test_execute_engine_error_return() {
|
2023-10-05 14:27:48 -07:00
|
|
|
let code = r#"const part001 = startSketchOn('XY')
|
|
|
|
|> startProfileAt([5.5229, 5.25217], %)
|
2023-09-18 17:31:11 -07:00
|
|
|
|> line([10.50433, -1.19122], %)
|
|
|
|
|> line([8.01362, -5.48731], %)
|
|
|
|
|> line([-1.02877, -6.76825], %)
|
|
|
|
|> line([-11.53311, 2.81559], %)
|
|
|
|
|> extrude(4, %)
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await;
|
|
|
|
assert!(result.is_err());
|
|
|
|
assert_eq!(
|
|
|
|
result.err().unwrap().to_string(),
|
2023-10-05 14:27:48 -07:00
|
|
|
r#"engine: KclErrorDetails { source_ranges: [SourceRange([222, 235])], message: "Modeling command failed: Some([ApiError { error_code: BadRequest, message: \"The path is not closed. Solid2D construction requires a closed path!\" }])" }"#,
|
2023-09-18 17:31:11 -07:00
|
|
|
);
|
|
|
|
}
|
2023-09-19 14:20:14 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
#[ignore] // ignore until more stack fixes
|
|
|
|
async fn serial_test_execute_pipes_on_pipes() {
|
|
|
|
let code = include_str!("inputs/pipes_on_pipes.kcl");
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/pipes_on_pipes.png", &result, 0.999);
|
2023-09-19 14:20:14 -07:00
|
|
|
}
|
2023-09-19 16:05:53 -07:00
|
|
|
|
2023-11-08 15:06:41 -06:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn serial_test_execute_cylinder() {
|
|
|
|
let code = include_str!("inputs/cylinder.kcl");
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/cylinder.png", &result, 0.999);
|
|
|
|
}
|
|
|
|
|
2023-09-20 19:35:37 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn serial_test_execute_kittycad_svg() {
|
|
|
|
let code = include_str!("inputs/kittycad_svg.kcl");
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/kittycad_svg.png", &result, 0.999);
|
2023-09-20 19:35:37 -07:00
|
|
|
}
|
|
|
|
|
2023-09-19 16:05:53 -07:00
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_member_expression_sketch_group() {
|
2023-09-19 16:05:53 -07:00
|
|
|
let code = r#"fn cube = (pos, scale) => {
|
2023-10-05 14:27:48 -07:00
|
|
|
const sg = startSketchOn('XY')
|
|
|
|
|> startProfileAt(pos, %)
|
2023-09-19 16:05:53 -07:00
|
|
|
|> line([0, scale], %)
|
|
|
|
|> line([scale, 0], %)
|
|
|
|
|> line([0, -scale], %)
|
|
|
|
|
|
|
|
return sg
|
|
|
|
}
|
|
|
|
|
|
|
|
const b1 = cube([0,0], 10)
|
|
|
|
const b2 = cube([3,3], 4)
|
|
|
|
|
|
|
|
const pt1 = b1.value[0]
|
|
|
|
const pt2 = b2.value[0]
|
|
|
|
|
|
|
|
show(b1)
|
|
|
|
show(b2)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
|
|
|
twenty_twenty::assert_image(
|
|
|
|
"tests/executor/outputs/member_expression_sketch_group.png",
|
|
|
|
&result,
|
|
|
|
1.0,
|
|
|
|
);
|
|
|
|
}
|
2023-09-25 12:14:41 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_close_arc() {
|
2023-09-25 12:14:41 -07:00
|
|
|
let code = r#"const center = [0,0]
|
|
|
|
const radius = 40
|
|
|
|
const height = 3
|
|
|
|
|
2023-10-05 14:27:48 -07:00
|
|
|
const body = startSketchOn('XY')
|
|
|
|
|> startProfileAt([center[0]+radius, center[1]], %)
|
2023-09-25 12:14:41 -07:00
|
|
|
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(height, %)
|
|
|
|
|
|
|
|
show(body)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/close_arc.png", &result, 0.999);
|
2023-09-25 12:14:41 -07:00
|
|
|
}
|
2023-09-25 15:25:58 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_negative_args() {
|
2023-09-25 15:25:58 -07:00
|
|
|
let code = r#"const width = 5
|
|
|
|
const height = 10
|
|
|
|
const length = 12
|
|
|
|
|
|
|
|
fn box = (sk1, sk2, scale) => {
|
2023-10-05 14:27:48 -07:00
|
|
|
const boxSketch = startSketchOn('XY')
|
|
|
|
|> startProfileAt([sk1, sk2], %)
|
2023-09-25 15:25:58 -07:00
|
|
|
|> line([0, scale], %)
|
|
|
|
|> line([scale, 0], %)
|
|
|
|
|> line([0, -scale], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(scale, %)
|
|
|
|
return boxSketch
|
|
|
|
}
|
|
|
|
|
|
|
|
box(0, 0, 5)
|
|
|
|
box(10, 23, 8)
|
|
|
|
let thing = box(-12, -15, 10)
|
|
|
|
box(-20, -5, 10)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/negative_args.png", &result, 0.999);
|
2023-09-25 15:25:58 -07:00
|
|
|
}
|
2023-09-29 14:41:14 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_basic_tangential_arc() {
|
2023-09-29 14:41:14 -07:00
|
|
|
let code = r#"const boxSketch = startSketchAt([0, 0])
|
|
|
|
|> line([0, 10], %)
|
2023-10-12 11:50:54 -05:00
|
|
|
|> tangentialArc({radius: 5, offset: 90}, %)
|
2023-09-29 14:41:14 -07:00
|
|
|
|> line([5, -15], %)
|
|
|
|
|> extrude(10, %)
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
2023-10-12 11:50:54 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc.png", &result, 0.999);
|
2023-09-29 14:41:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_basic_tangential_arc_with_point() {
|
2023-09-29 14:41:14 -07:00
|
|
|
let code = r#"const boxSketch = startSketchAt([0, 0])
|
|
|
|
|> line([0, 10], %)
|
2023-10-12 11:50:54 -05:00
|
|
|
|> tangentialArc([-5, 5], %)
|
2023-09-29 14:41:14 -07:00
|
|
|
|> line([5, -15], %)
|
|
|
|
|> extrude(10, %)
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
2023-10-12 11:50:54 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_with_point.png", &result, 0.999);
|
2023-09-29 14:41:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_basic_tangential_arc_to() {
|
2023-09-29 14:41:14 -07:00
|
|
|
let code = r#"const boxSketch = startSketchAt([0, 0])
|
|
|
|
|> line([0, 10], %)
|
2023-10-12 11:50:54 -05:00
|
|
|
|> tangentialArcTo([-5, 15], %)
|
2023-09-29 14:41:14 -07:00
|
|
|
|> line([5, -15], %)
|
|
|
|
|> extrude(10, %)
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
2023-10-12 11:50:54 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_to.png", &result, 0.999);
|
2023-09-29 14:41:14 -07:00
|
|
|
}
|
2023-10-05 14:27:48 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_different_planes_same_drawing() {
|
2023-10-05 14:27:48 -07:00
|
|
|
let code = r#"const width = 5
|
|
|
|
const height = 10
|
|
|
|
const length = 12
|
|
|
|
|
|
|
|
fn box = (sk1, sk2, scale, plane) => {
|
|
|
|
const boxsketch = startSketchOn(plane)
|
|
|
|
|> startProfileAt([sk1, sk2], %)
|
|
|
|
|> line([0, scale], %)
|
|
|
|
|> line([scale, 0], %)
|
|
|
|
|> line([0, -scale], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(scale, %)
|
|
|
|
return boxsketch
|
|
|
|
}
|
|
|
|
|
|
|
|
box(0, 0, 5, 'xy')
|
|
|
|
box(10, 23, 8, 'xz')
|
|
|
|
box(30, 43, 18, '-xy')
|
|
|
|
let thing = box(-12, -15, 10, 'yz')
|
|
|
|
box(-20, -5, 10, 'xy')"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image(
|
|
|
|
"tests/executor/outputs/different_planes_same_drawing.png",
|
|
|
|
&result,
|
|
|
|
0.999,
|
|
|
|
);
|
2023-10-05 14:27:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2023-10-12 11:50:54 -05:00
|
|
|
async fn serial_test_lots_of_planes() {
|
2023-10-05 14:27:48 -07:00
|
|
|
let code = r#"const sigmaAllow = 15000 // psi
|
|
|
|
const width = 11 // inch
|
|
|
|
const p = 150 // Force on shelf - lbs
|
|
|
|
const distance = 12 // inches
|
|
|
|
const FOS = 2
|
|
|
|
const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))
|
|
|
|
const filletR = thickness * 2
|
|
|
|
const shelfMountL = 9
|
|
|
|
const wallMountL = 8
|
|
|
|
|
|
|
|
const bracket = startSketchOn('XY')
|
|
|
|
|> startProfileAt([0, 0], %)
|
|
|
|
|> line([0, wallMountL], %)
|
2023-10-12 11:50:54 -05:00
|
|
|
|> tangentialArc({ radius: filletR, offset: 90 }, %)
|
2023-10-05 14:27:48 -07:00
|
|
|
|> line([-shelfMountL, 0], %)
|
|
|
|
|> line([0, -thickness], %)
|
|
|
|
|> line([shelfMountL, 0], %)
|
2023-10-12 11:50:54 -05:00
|
|
|
|> tangentialArc({
|
2023-10-05 14:27:48 -07:00
|
|
|
radius: filletR - thickness,
|
|
|
|
offset: -90
|
|
|
|
}, %)
|
|
|
|
|> line([0, -wallMountL], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(width, %)
|
|
|
|
|
|
|
|
show(bracket)
|
|
|
|
const part001 = startSketchOn('XY')
|
|
|
|
|> startProfileAt([-15.53, -10.28], %)
|
|
|
|
|> line([10.49, -2.08], %)
|
|
|
|
|> line([10.42, 8.47], %)
|
|
|
|
|> line([-19.16, 5.1], %)
|
|
|
|
|> close(%)
|
|
|
|
|> extrude(4, %)
|
|
|
|
|
|
|
|
const part002 = startSketchOn('-XZ')
|
|
|
|
|> startProfileAt([-9.35, 19.18], %)
|
|
|
|
|> line([32.14, -2.47], %)
|
|
|
|
|> line([8.39, -3.73], %)
|
|
|
|
|> close(%)
|
|
|
|
|
|
|
|
const part003 = startSketchOn('-XZ')
|
|
|
|
|> startProfileAt([13.82, 16.51], %)
|
|
|
|
|> line([-6.24, -30.82], %)
|
|
|
|
|> line([8.39, -3.73], %)
|
|
|
|
|> close(%)
|
|
|
|
|
|
|
|
const part004 = startSketchOn('YZ')
|
|
|
|
|> startProfileAt([19.04, 20.22], %)
|
|
|
|
|> line([9.44, -30.16], %)
|
|
|
|
|> line([8.39, -3.73], %)
|
|
|
|
|> close(%)
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/lots_of_planes.png", &result, 0.999);
|
2023-10-05 14:27:48 -07:00
|
|
|
}
|
2023-10-13 12:02:46 -07:00
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn serial_test_holes() {
|
|
|
|
let code = r#"fn circle = (pos, radius) => {
|
|
|
|
const sg = startSketchOn('XY')
|
|
|
|
|> startProfileAt(pos, %)
|
|
|
|
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|
|
|
|
|> close(%)
|
|
|
|
|
|
|
|
return sg
|
|
|
|
}
|
|
|
|
|
|
|
|
const square = startSketchOn('XY')
|
|
|
|
|> startProfileAt([0, 0], %)
|
|
|
|
|> line([0, 10], %)
|
|
|
|
|> line([10, 0], %)
|
|
|
|
|> line([0, -10], %)
|
|
|
|
|> close(%)
|
|
|
|
|> hole(circle([2, 2], .5), %)
|
|
|
|
|> hole(circle([2, 8], .5), %)
|
|
|
|
|> extrude(2, %)
|
|
|
|
|
|
|
|
show(square)
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/holes.png", &result, 0.999);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
async fn serial_test_rounded_with_holes() {
|
|
|
|
let code = r#"fn circle = (pos, radius) => {
|
|
|
|
const sg = startSketchOn('XY')
|
|
|
|
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
|
|
|> arc({
|
|
|
|
angle_end: 360,
|
|
|
|
angle_start: 0,
|
|
|
|
radius: radius
|
|
|
|
}, %)
|
|
|
|
|> close(%)
|
|
|
|
return sg
|
|
|
|
}
|
|
|
|
|
|
|
|
fn roundedRectangle = (pos, w, l, cornerRadius) => {
|
|
|
|
const rr = startSketchOn('XY')
|
|
|
|
|> startProfileAt([pos[0] - w/2, 0], %)
|
|
|
|
|> lineTo([pos[0] - w/2, pos[1] - l/2 + cornerRadius], %)
|
|
|
|
|> tangentialArcTo([pos[0] - w/2 + cornerRadius, pos[1] - l/2], %)
|
|
|
|
|> lineTo([pos[0] + w/2 - cornerRadius, pos[1] - l/2], %)
|
|
|
|
|> tangentialArcTo([pos[0] + w/2, pos[1] - l/2 + cornerRadius], %)
|
|
|
|
|> lineTo([pos[0] + w/2, pos[1] + l/2 - cornerRadius], %)
|
|
|
|
|> tangentialArcTo([pos[0] + w/2 - cornerRadius, pos[1] + l/2], %)
|
|
|
|
|> lineTo([pos[0] - w/2 + cornerRadius, pos[1] + l/2], %)
|
|
|
|
|> tangentialArcTo([pos[0] - w/2, pos[1] + l/2 - cornerRadius], %)
|
|
|
|
|> close(%)
|
|
|
|
return rr
|
|
|
|
}
|
|
|
|
|
|
|
|
const holeRadius = 1
|
|
|
|
const holeIndex = 6
|
|
|
|
|
|
|
|
const part = roundedRectangle([0, 0], 20, 20, 4)
|
|
|
|
|> hole(circle([-holeIndex, holeIndex], holeRadius), %)
|
|
|
|
|> hole(circle([holeIndex, holeIndex], holeRadius), %)
|
|
|
|
|> hole(circle([-holeIndex, -holeIndex], holeRadius), %)
|
|
|
|
|> hole(circle([holeIndex, -holeIndex], holeRadius), %)
|
|
|
|
|> extrude(2, %)
|
|
|
|
|
|
|
|
show(part)"#;
|
|
|
|
|
|
|
|
let result = execute_and_snapshot(code).await.unwrap();
|
|
|
|
twenty_twenty::assert_image("tests/executor/outputs/rounded_with_holes.png", &result, 0.999);
|
|
|
|
}
|