2023-09-12 14:59:40 -07:00
use anyhow ::Result ;
2024-02-12 12:18:37 -08:00
use kcl_lib ::engine ::EngineManager ;
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.
2024-02-20 17:55:06 -08:00
async fn execute_and_snapshot ( code : & str , units : kittycad ::types ::UnitLength ) -> Result < image ::DynamicImage > {
2023-09-12 14:59:40 -07:00
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 ) ;
2024-02-12 18:08:42 -08:00
// uncomment to use a local server
2024-02-16 16:42:01 -08:00
//client.set_base_url("http://system76-pc:8080/");
2023-09-12 14:59:40 -07:00
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 ( ) ;
2024-02-20 17:55:06 -08:00
let ctx = kcl_lib ::executor ::ExecutorContext ::new ( ws , units . clone ( ) ) . await ? ;
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
2024-02-20 17:55:06 -08:00
let ( x , y ) = kcl_lib ::std ::utils ::get_camera_zoom_magnitude_per_unit_length ( units ) ;
ctx . engine
. send_modeling_cmd (
uuid ::Uuid ::new_v4 ( ) ,
kcl_lib ::executor ::SourceRange ::default ( ) ,
kittycad ::types ::ModelingCmd ::DefaultCameraLookAt {
center : kittycad ::types ::Point3D { x : 0.0 , y : 0.0 , z : 0.0 } ,
up : kittycad ::types ::Point3D { x : 0.0 , y : 0.0 , z : 1.0 } ,
vantage : kittycad ::types ::Point3D { x : 0.0 , y : - x , z : y } ,
sequence : None ,
} ,
)
. 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 )
}
2024-02-12 18:08:42 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_sketch_on_face ( ) {
let code = r #" const part001 = startSketchOn('XY')
| > startProfileAt ( [ 11.19 , 28.35 ] , % )
| > line ( { to : [ 28.67 , - 13.25 ] , tag : " here " } , % )
| > line ( [ - 4.12 , - 22.81 ] , % )
| > line ( [ - 33.24 , 14.55 ] , % )
| > close ( % )
| > extrude ( 5 , % )
const part002 = startSketchOn ( part001 , " here " )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 10 ] , % )
| > line ( [ 10 , 0 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > close ( % )
| > extrude ( 5 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-12 18:08:42 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/sketch_on_face.png " , & result , 0.999 ) ;
}
2024-02-13 10:26:09 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_sketch_on_face_start ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
return sg
}
const part001 = cube ( [ 0 , 0 ] , 20 )
| > close ( % )
| > extrude ( 20 , % )
const part002 = startSketchOn ( part001 , " start " )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 10 ] , % )
| > line ( [ 10 , 0 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > close ( % )
| > extrude ( 5 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-13 10:26:09 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/sketch_on_face_start.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_sketch_on_face_end ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
return sg
}
const part001 = cube ( [ 0 , 0 ] , 20 )
| > close ( % )
| > extrude ( 20 , % )
const part002 = startSketchOn ( part001 , " END " )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 10 ] , % )
| > line ( [ 10 , 0 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > close ( % )
| > extrude ( 5 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-13 10:26:09 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/sketch_on_face_end.png " , & result , 0.999 ) ;
}
2024-02-26 14:54:42 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_sketch_on_face_end_negative_extrude ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
return sg
}
const part001 = cube ( [ 0 , 0 ] , 20 )
| > close ( % )
| > extrude ( 20 , % )
const part002 = startSketchOn ( part001 , " END " )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 10 ] , % )
| > line ( [ 10 , 0 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > close ( % )
| > extrude ( - 5 , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image (
" tests/executor/outputs/sketch_on_face_end_negative_extrude.png " ,
& result ,
0.999 ,
) ;
}
2024-03-05 11:52:45 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_fillet_duplicate_tags ( ) {
let code = r #" const part001 = startSketchOn('XY')
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( { to : [ 0 , 10 ] , tag : " thing " } , % )
| > line ( [ 10 , 0 ] , % )
| > line ( { to : [ 0 , - 10 ] , tag : " thing2 " } , % )
| > close ( % )
| > extrude ( 10 , % )
| > fillet ( { radius : 0.5 , tags : [ " thing " , " thing " ] } , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm ) . await ;
assert! ( result . is_err ( ) ) ;
assert_eq! (
result . err ( ) . unwrap ( ) . to_string ( ) ,
r # "type: KclErrorDetails { source_ranges: [SourceRange([227, 277])], message: "Duplicate tags are not allowed." }"# ,
) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_basic_fillet_cube_start ( ) {
let code = r #" const part001 = startSketchOn('XY')
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( { to : [ 0 , 10 ] , tag : " thing " } , % )
| > line ( [ 10 , 0 ] , % )
| > line ( { to : [ 0 , - 10 ] , tag : " thing2 " } , % )
| > close ( % )
| > extrude ( 10 , % )
| > fillet ( { radius : 2 , tags : [ " thing " , " thing2 " ] } , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/basic_fillet_cube_start.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_basic_fillet_cube_end ( ) {
let code = r #" const part001 = startSketchOn('XY')
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( { to : [ 0 , 10 ] , tag : " thing " } , % )
| > line ( [ 10 , 0 ] , % )
| > line ( { to : [ 0 , - 10 ] , tag : " thing2 " } , % )
| > close ( % )
| > extrude ( 10 , % )
| > fillet ( { radius : 2 , tags : [ " thing " , getOppositeEdge ( " thing " , % ) ] } , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/basic_fillet_cube_end.png " , & result , 0.999 ) ;
}
2024-03-07 12:35:56 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_basic_fillet_cube_close_opposite ( ) {
let code = r #" const part001 = startSketchOn('XY')
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( { to : [ 0 , 10 ] , tag : " thing " } , % )
| > line ( [ 10 , 0 ] , % )
| > line ( { to : [ 0 , - 10 ] , tag : " thing2 " } , % )
| > close ( % , " thing3 " )
| > extrude ( 10 , % )
| > fillet ( { radius : 2 , tags : [ " thing3 " , getOppositeEdge ( " thing3 " , % ) ] } , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image (
" tests/executor/outputs/basic_fillet_cube_close_opposite.png " ,
& result ,
0.999 ,
) ;
}
2024-03-05 11:52:45 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_basic_fillet_cube_next_adjacent ( ) {
let code = r #" const part001 = startSketchOn('XY')
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( { to : [ 0 , 10 ] , tag : " thing " } , % )
| > line ( { to : [ 10 , 0 ] , tag : " thing1 " } , % )
| > line ( { to : [ 0 , - 10 ] , tag : " thing2 " } , % )
| > close ( % )
| > extrude ( 10 , % )
| > fillet ( { radius : 2 , tags : [ getNextAdjacentEdge ( " thing " , % ) ] } , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image (
" tests/executor/outputs/basic_fillet_cube_next_adjacent.png " ,
& result ,
0.999 ,
) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_basic_fillet_cube_previous_adjacent ( ) {
let code = r #" const part001 = startSketchOn('XY')
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( { to : [ 0 , 10 ] , tag : " thing " } , % )
| > line ( { to : [ 10 , 0 ] , tag : " thing1 " } , % )
| > line ( { to : [ 0 , - 10 ] , tag : " thing2 " } , % )
| > close ( % )
| > extrude ( 10 , % )
| > fillet ( { radius : 2 , tags : [ getPreviousAdjacentEdge ( " thing2 " , % ) ] } , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image (
" tests/executor/outputs/basic_fillet_cube_previous_adjacent.png " ,
& result ,
0.999 ,
) ;
}
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_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 )
2024-02-20 17:55:06 -08:00
" #;
2023-09-12 14:59:40 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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
}
2024-02-20 17:55:06 -08:00
const thing = box ( [ 0 , 0 ] , 3 , 6 , 10 ) " #;
2023-09-19 14:20:14 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
2024-02-20 17:55:06 -08:00
" #;
2023-09-12 14:59:40 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
2024-02-20 17:55:06 -08:00
" #;
2023-09-15 20:45:28 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
2024-02-20 17:55:06 -08:00
" #;
2023-10-02 16:14:09 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm ) . await ;
2023-09-18 17:31:11 -07:00
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 " ) ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 " ) ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2023-11-08 15:06:41 -06:00
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 " ) ]
2024-01-09 14:58:31 -06:00
#[ ignore = " currently stack overflows " ]
2023-09-20 19:35:37 -07:00
async fn serial_test_execute_kittycad_svg ( ) {
let code = include_str! ( " inputs/kittycad_svg.kcl " ) ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 ]
2024-02-20 17:55:06 -08:00
" #;
2023-09-19 16:05:53 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2023-09-19 16:05:53 -07:00
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 , % )
2024-02-20 17:55:06 -08:00
" #;
2023-09-25 12:14:41 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 ) " #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 ' ) " #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 , % )
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 ( % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. 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 ( ) {
2024-03-01 14:23:30 -08:00
let code = r #" const square = startSketchOn('XY')
2023-10-13 12:02:46 -07:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 10 ] , % )
| > line ( [ 10 , 0 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > close ( % )
2024-03-07 14:53:37 -08:00
| > hole ( circle ( [ 2 , 2 ] , . 5 , startSketchOn ( ' XY ' ) ) , % )
| > hole ( circle ( [ 2 , 8 ] , . 5 , startSketchOn ( ' XY ' ) ) , % )
2023-10-13 12:02:46 -07:00
| > extrude ( 2 , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2023-10-13 12:02:46 -07:00
twenty_twenty ::assert_image ( " tests/executor/outputs/holes.png " , & result , 0.999 ) ;
}
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn optional_params ( ) {
let code = r #"
2024-03-01 14:23:30 -08:00
fn other_circle = ( pos , radius , tag ? ) = > {
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > arc ( { angle_end : 360 , angle_start : 0 , radius : radius } , % )
| > close ( % )
2024-02-11 15:08:54 -08:00
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
return sg
}
2024-02-11 15:08:54 -08:00
2024-03-01 14:23:30 -08:00
const thing = other_circle ( [ 2 , 2 ] , 20 )
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
twenty_twenty ::assert_image ( " tests/executor/outputs/optional_params.png " , & result , 0.999 ) ;
}
2023-10-13 12:02:46 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_rounded_with_holes ( ) {
2024-03-01 14:23:30 -08:00
let code = r #" fn tarc = (to, sketchGroup, tag?) => {
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
return tangentialArcTo ( to , sketchGroup , tag )
}
2023-10-13 12:02:46 -07:00
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 ] , % )
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
| > tarc ( [ pos [ 0 ] - w / 2 + cornerRadius , pos [ 1 ] - l / 2 ] , % , " arc0 " )
2023-10-13 12:02:46 -07:00
| > lineTo ( [ pos [ 0 ] + w / 2 - cornerRadius , pos [ 1 ] - l / 2 ] , % )
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
| > tarc ( [ pos [ 0 ] + w / 2 , pos [ 1 ] - l / 2 + cornerRadius ] , % )
2023-10-13 12:02:46 -07:00
| > lineTo ( [ pos [ 0 ] + w / 2 , pos [ 1 ] + l / 2 - cornerRadius ] , % )
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
| > tarc ( [ pos [ 0 ] + w / 2 - cornerRadius , pos [ 1 ] + l / 2 ] , % , " arc2 " )
2023-10-13 12:02:46 -07:00
| > lineTo ( [ pos [ 0 ] - w / 2 + cornerRadius , pos [ 1 ] + l / 2 ] , % )
Remove just one enum (#1096)
# Problem
This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.
Previously, if users want to put a tag on the arc, the function's parameters change type.
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```
# Solution
My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like
```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```
This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
| > tarc ( [ pos [ 0 ] - w / 2 , pos [ 1 ] + l / 2 - cornerRadius ] , % )
2023-10-13 12:02:46 -07:00
| > close ( % )
return rr
}
const holeRadius = 1
const holeIndex = 6
const part = roundedRectangle ( [ 0 , 0 ] , 20 , 20 , 4 )
2024-03-07 14:53:37 -08:00
| > hole ( circle ( [ - holeIndex , holeIndex ] , holeRadius , startSketchOn ( ' XY ' ) ) , % )
| > hole ( circle ( [ holeIndex , holeIndex ] , holeRadius , startSketchOn ( ' XY ' ) ) , % )
| > hole ( circle ( [ - holeIndex , - holeIndex ] , holeRadius , startSketchOn ( ' XY ' ) ) , % )
| > hole ( circle ( [ holeIndex , - holeIndex ] , holeRadius , startSketchOn ( ' XY ' ) ) , % )
2023-10-13 12:02:46 -07:00
| > extrude ( 2 , % )
2024-02-20 17:55:06 -08:00
" #;
2023-10-13 12:02:46 -07:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2023-10-13 12:02:46 -07:00
twenty_twenty ::assert_image ( " tests/executor/outputs/rounded_with_holes.png " , & result , 0.999 ) ;
}
2023-11-09 13:08:11 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_top_level_expression ( ) {
2024-03-07 14:53:37 -08:00
let code = r # "circle([0,0], 22, startSketchOn('XY')) |> extrude(14, %)"# ;
2023-11-09 13:08:11 -08:00
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2023-11-09 13:08:11 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/top_level_expression.png " , & result , 0.999 ) ;
}
2024-02-11 15:08:54 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_linear_basic ( ) {
2024-03-07 14:53:37 -08:00
let code = r #" const part = startSketchOn('XY')
| > circle ( [ 0 , 0 ] , 2 , % )
2024-02-28 19:18:23 -08:00
| > patternLinear ( { axis : [ 0 , 1 ] , repetitions : 12 , distance : 2 } , % )
2024-02-11 15:08:54 -08:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-11 15:08:54 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/patterns_linear_basic.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_linear_basic_3d ( ) {
2024-03-01 14:23:30 -08:00
let code = r #" const part = startSketchOn('XY')
2024-02-11 15:08:54 -08:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 1 ] , % )
| > line ( [ 1 , 0 ] , % )
| > line ( [ 0 , - 1 ] , % )
| > close ( % )
| > extrude ( 1 , % )
2024-02-28 19:18:23 -08:00
| > patternLinear ( { axis : [ 1 , 0 ] , repetitions : 3 , distance : 6 } , % )
2024-02-11 15:08:54 -08:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-11 15:08:54 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/patterns_linear_basic_3d.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_linear_basic_negative_distance ( ) {
2024-03-07 14:53:37 -08:00
let code = r #" const part = startSketchOn('XY')
| > circle ( [ 0 , 0 ] , 2 , % )
2024-02-28 19:18:23 -08:00
| > patternLinear ( { axis : [ 0 , 1 ] , repetitions : 12 , distance : - 2 } , % )
2024-02-11 15:08:54 -08:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-11 15:08:54 -08:00
twenty_twenty ::assert_image (
" tests/executor/outputs/patterns_linear_basic_negative_distance.png " ,
& result ,
0.999 ,
) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_linear_basic_negative_axis ( ) {
2024-03-07 14:53:37 -08:00
let code = r #" const part = startSketchOn('XY')
| > circle ( [ 0 , 0 ] , 2 , % )
2024-02-28 19:18:23 -08:00
| > patternLinear ( { axis : [ 0 , - 1 ] , repetitions : 12 , distance : 2 } , % )
2024-02-11 15:08:54 -08:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-11 15:08:54 -08:00
twenty_twenty ::assert_image (
" tests/executor/outputs/patterns_linear_basic_negative_axis.png " ,
& result ,
0.999 ,
) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_linear_basic_holes ( ) {
2024-03-07 14:53:37 -08:00
let code = r #" const circles = startSketchOn('XY')
| > circle ( [ 5 , 5 ] , 1 , % )
2024-02-28 19:18:23 -08:00
| > patternLinear ( { axis : [ 1 , 1 ] , repetitions : 12 , distance : 3 } , % )
2024-02-11 15:08:54 -08:00
const rectangle = startSketchOn ( ' XY ' )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 50 ] , % )
| > line ( [ 50 , 0 ] , % )
| > line ( [ 0 , - 50 ] , % )
| > close ( % )
| > hole ( circles , % )
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-11 15:08:54 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/patterns_linear_basic_holes.png " , & result , 0.999 ) ;
}
2024-02-12 12:18:37 -08:00
2024-02-13 13:20:49 -06:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_circular_basic_2d ( ) {
2024-03-07 14:53:37 -08:00
let code = r #" const part = startSketchOn('XY')
| > circle ( [ 0 , 0 ] , 2 , % )
2024-02-28 19:18:23 -08:00
| > patternCircular ( { axis : [ 0 , 1 ] , center : [ 20 , 20 , 20 ] , repetitions : 12 , arcDegrees : 210 , rotateDuplicates : true } , % )
2024-02-13 13:20:49 -06:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-13 13:20:49 -06:00
twenty_twenty ::assert_image ( " tests/executor/outputs/patterns_circular_basic_2d.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_circular_basic_3d ( ) {
2024-03-01 14:23:30 -08:00
let code = r #" const part = startSketchOn('XY')
2024-02-13 13:20:49 -06:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 1 ] , % )
| > line ( [ 1 , 0 ] , % )
| > line ( [ 0 , - 1 ] , % )
| > close ( % )
| > extrude ( 1 , % )
2024-02-28 19:18:23 -08:00
| > patternCircular ( { axis : [ 0 , 1 ] , center : [ - 20 , - 20 , - 20 ] , repetitions : 40 , arcDegrees : 360 , rotateDuplicates : false } , % )
2024-02-13 13:20:49 -06:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-13 13:20:49 -06:00
twenty_twenty ::assert_image ( " tests/executor/outputs/patterns_circular_basic_3d.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_patterns_circular_3d_tilted_axis ( ) {
2024-03-01 14:23:30 -08:00
let code = r #" const part = startSketchOn('XY')
2024-02-13 13:20:49 -06:00
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 1 ] , % )
| > line ( [ 1 , 0 ] , % )
| > line ( [ 0 , - 1 ] , % )
| > close ( % )
| > extrude ( 1 , % )
2024-02-28 19:18:23 -08:00
| > patternCircular ( { axis : [ 1 , 1 ] , center : [ 10 , 0 , 10 ] , repetitions : 10 , arcDegrees : 360 , rotateDuplicates : true } , % )
2024-02-13 13:20:49 -06:00
" #;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-13 13:20:49 -06:00
twenty_twenty ::assert_image (
" tests/executor/outputs/patterns_circular_3d_tilted_axis.png " ,
& result ,
0.999 ,
) ;
}
2024-02-12 12:18:37 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_file_doesnt_exist ( ) {
let code = r # "const model = import("thing.obj")"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm ) . await ;
2024-02-12 12:18:37 -08:00
assert! ( result . is_err ( ) ) ;
assert_eq! (
result . err ( ) . unwrap ( ) . to_string ( ) ,
r # "semantic: KclErrorDetails { source_ranges: [SourceRange([14, 33])], message: "File `thing.obj` does not exist." }"#
) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_obj_with_mtl ( ) {
let code = r # "const model = import("tests/executor/inputs/cube.obj")"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-12 12:18:37 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/import_obj_with_mtl.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_obj_with_mtl_units ( ) {
let code = r # "const model = import("tests/executor/inputs/cube.obj", {type: "obj", units: "m"})"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-12 12:18:37 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/import_obj_with_mtl_units.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_gltf_with_bin ( ) {
let code = r # "const model = import("tests/executor/inputs/cube.gltf")"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-12 12:18:37 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/import_gltf_with_bin.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_gltf_embedded ( ) {
let code = r # "const model = import("tests/executor/inputs/cube-embedded.gltf")"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-12 12:18:37 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/import_gltf_embedded.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_glb ( ) {
let code = r # "const model = import("tests/executor/inputs/cube.glb")"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-12 12:18:37 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/import_glb.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_glb_no_assign ( ) {
let code = r # "import("tests/executor/inputs/cube.glb")"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
2024-02-12 12:18:37 -08:00
twenty_twenty ::assert_image ( " tests/executor/outputs/import_glb_no_assign.png " , & result , 0.999 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_import_ext_doesnt_match ( ) {
let code = r # "const model = import("tests/executor/inputs/cube.gltf", {type: "obj", units: "m"})"# ;
2024-02-20 17:55:06 -08:00
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm ) . await ;
2024-02-12 12:18:37 -08:00
assert! ( result . is_err ( ) ) ;
assert_eq! (
result . err ( ) . unwrap ( ) . to_string ( ) ,
r # "semantic: KclErrorDetails { source_ranges: [SourceRange([14, 82])], message: "The given format does not match the file extension. Expected: `gltf`, Given: `obj`" }"#
) ;
}
2024-02-20 17:55:06 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_cube_mm ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
| > close ( % )
| > extrude ( scale , % )
return sg
}
const myCube = cube ( [ 0 , 0 ] , 10 )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/cube_mm.png " , & result , 1.0 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_cube_cm ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
| > close ( % )
| > extrude ( scale , % )
return sg
}
const myCube = cube ( [ 0 , 0 ] , 10 )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Cm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/cube_cm.png " , & result , 1.0 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_cube_m ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
| > close ( % )
| > extrude ( scale , % )
return sg
}
const myCube = cube ( [ 0 , 0 ] , 10 )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::M )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/cube_m.png " , & result , 1.0 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_cube_in ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
| > close ( % )
| > extrude ( scale , % )
return sg
}
const myCube = cube ( [ 0 , 0 ] , 10 )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::In )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/cube_in.png " , & result , 1.0 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_cube_ft ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
| > close ( % )
| > extrude ( scale , % )
return sg
}
const myCube = cube ( [ 0 , 0 ] , 10 )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Ft )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/cube_ft.png " , & result , 1.0 ) ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_cube_yd ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
| > close ( % )
| > extrude ( scale , % )
return sg
}
const myCube = cube ( [ 0 , 0 ] , 10 )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Yd )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/cube_yd.png " , & result , 1.0 ) ;
}
2024-02-22 19:07:17 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_error_sketch_on_arc_face ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > tangentialArc ( { to : [ 0 , scale ] , tag : " here " } , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
return sg
}
const part001 = cube ( [ 0 , 0 ] , 20 )
| > close ( % )
| > extrude ( 20 , % )
const part002 = startSketchOn ( part001 , " here " )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 5 , 0 ] , % )
| > line ( [ 5 , 5 ] , % )
| > line ( [ 0 , 5 ] , % )
| > close ( % )
| > extrude ( 1 , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm ) . await ;
assert! ( result . is_err ( ) ) ;
assert_eq! (
result . err ( ) . unwrap ( ) . to_string ( ) ,
r # "type: KclErrorDetails { source_ranges: [SourceRange([294, 324])], message: "Cannot sketch on a non-planar surface: `here`" }"#
) ;
}
2024-02-26 14:54:42 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_sketch_on_face_of_face ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
return sg
}
const part001 = cube ( [ 0 , 0 ] , 20 )
| > close ( % )
| > extrude ( 20 , % )
const part002 = startSketchOn ( part001 , " end " )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 10 ] , % )
| > line ( [ 10 , 0 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > close ( % )
| > extrude ( 5 , % )
const part003 = startSketchOn ( part002 , " end " )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 5 ] , % )
| > line ( [ 5 , 0 ] , % )
| > line ( [ 0 , - 5 ] , % )
| > close ( % )
| > extrude ( 5 , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/sketch_on_face_of_face.png " , & result , 1.0 ) ;
}
2024-03-05 12:46:05 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_stdlib_kcl_error_right_code_path ( ) {
let code = r #" const square = startSketchOn('XY')
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( [ 0 , 10 ] , % )
| > line ( [ 10 , 0 ] , % )
| > line ( [ 0 , - 10 ] , % )
| > close ( % )
| > hole ( circle ( [ 2 , 2 ] , . 5 ) , % )
2024-03-07 14:53:37 -08:00
| > hole ( circle ( [ 2 , 8 ] , . 5 , startSketchOn ( ' XY ' ) ) , % )
2024-03-05 12:46:05 -08:00
| > extrude ( 2 , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm ) . await ;
assert! ( result . is_err ( ) ) ;
assert_eq! (
result . err ( ) . unwrap ( ) . to_string ( ) ,
r # "semantic: KclErrorDetails { source_ranges: [SourceRange([157, 175])], message: "this function expected 3 arguments, got 2" }"#
) ;
}
2024-03-07 14:53:37 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_sketch_on_face_circle ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
return sg
}
const part001 = cube ( [ 0 , 0 ] , 20 )
| > close ( % )
| > extrude ( 20 , % )
const part002 = startSketchOn ( part001 , " end " )
| > circle ( [ 0 , 0 ] , 5 , % )
| > extrude ( 5 , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/sketch_on_face_circle.png " , & result , 1.0 ) ;
}
2024-03-07 15:35:26 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn serial_test_sketch_on_face_circle_tagged ( ) {
let code = r #" fn cube = (pos, scale) => {
const sg = startSketchOn ( ' XY ' )
| > startProfileAt ( pos , % )
| > line ( [ 0 , scale ] , % )
| > line ( [ scale , 0 ] , % )
| > line ( [ 0 , - scale ] , % )
return sg
}
const part001 = cube ( [ 0 , 0 ] , 20 )
| > close ( % )
| > extrude ( 20 , % )
const part002 = startSketchOn ( part001 , " end " )
| > circle ( [ 0 , 0 ] , 5 , % , " myCircle " )
| > extrude ( 5 , % )
" #;
let result = execute_and_snapshot ( code , kittycad ::types ::UnitLength ::Mm )
. await
. unwrap ( ) ;
twenty_twenty ::assert_image ( " tests/executor/outputs/sketch_on_face_circle_tagged.png " , & result , 1.0 ) ;
}