Make most top-level modules in KCL private (#4478)

* Make ast module private

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Make most other modules private

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Expand API to support CLI, Python bindings, and LSP crate

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2024-11-20 15:19:25 +13:00
committed by GitHub
parent 1a9926be8a
commit d8ce5ad8bd
50 changed files with 568 additions and 646 deletions

View File

@ -1737,18 +1737,6 @@ dependencies = [
"zip", "zip",
] ]
[[package]]
name = "kcl-macros"
version = "0.1.0"
dependencies = [
"databake",
"kcl-lib",
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]] [[package]]
name = "kcl-test-server" name = "kcl-test-server"
version = "0.1.16" version = "0.1.16"

View File

@ -68,7 +68,6 @@ debug = "line-tables-only"
members = [ members = [
"derive-docs", "derive-docs",
"kcl", "kcl",
"kcl-macros",
"kcl-test-server", "kcl-test-server",
"kcl-to-core", "kcl-to-core",
] ]

View File

@ -173,11 +173,11 @@ fn do_stdlib_inner(
quote! { quote! {
let code_blocks = vec![#(#cb),*]; let code_blocks = vec![#(#cb),*];
code_blocks.iter().map(|cb| { code_blocks.iter().map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}).collect::<Vec<String>>() }).collect::<Vec<String>>()
} }
} else { } else {
@ -748,8 +748,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
quote! { quote! {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn #test_name_mock() { async fn #test_name_mock() {
let program = crate::parser::top_level_parse(#code_block).unwrap(); let program = crate::Program::parse(#code_block).unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())), engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())),
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
@ -758,7 +757,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default()).await.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]

View File

@ -2,8 +2,7 @@
mod test_examples_someFn { mod test_examples_someFn {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_someFn0() { async fn test_mock_example_someFn0() {
let program = crate::parser::top_level_parse("someFn()").unwrap(); let program = crate::Program::parse("someFn()").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -15,7 +14,9 @@ mod test_examples_someFn {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -111,10 +112,10 @@ impl crate::docs::StdLibFn for SomeFn {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,8 +2,7 @@
mod test_examples_someFn { mod test_examples_someFn {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_someFn0() { async fn test_mock_example_someFn0() {
let program = crate::parser::top_level_parse("someFn()").unwrap(); let program = crate::Program::parse("someFn()").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -15,7 +14,9 @@ mod test_examples_someFn {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -111,10 +112,10 @@ impl crate::docs::StdLibFn for SomeFn {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,9 +3,7 @@ mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show0() { async fn test_mock_example_show0() {
let program = let program =
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nshow") crate::Program::parse("This is another code block.\nyes sirrr.\nshow").unwrap();
.unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -17,7 +15,9 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -36,9 +36,7 @@ mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show1() { async fn test_mock_example_show1() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -50,7 +48,9 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -149,10 +149,10 @@ impl crate::docs::StdLibFn for Show {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,9 +2,7 @@
mod test_examples_show { mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show0() { async fn test_mock_example_show0() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -16,7 +14,9 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Show {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,9 +3,7 @@ mod test_examples_my_func {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_my_func0() { async fn test_mock_example_my_func0() {
let program = let program =
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmyFunc") crate::Program::parse("This is another code block.\nyes sirrr.\nmyFunc").unwrap();
.unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -17,7 +15,9 @@ mod test_examples_my_func {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -36,9 +36,7 @@ mod test_examples_my_func {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_my_func1() { async fn test_mock_example_my_func1() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nmyFunc").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nmyFunc").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -50,7 +48,9 @@ mod test_examples_my_func {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -149,10 +149,10 @@ impl crate::docs::StdLibFn for MyFunc {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,9 +3,7 @@ mod test_examples_line_to {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_line_to0() { async fn test_mock_example_line_to0() {
let program = let program =
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nlineTo") crate::Program::parse("This is another code block.\nyes sirrr.\nlineTo").unwrap();
.unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -17,7 +15,9 @@ mod test_examples_line_to {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -36,9 +36,7 @@ mod test_examples_line_to {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_line_to1() { async fn test_mock_example_line_to1() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nlineTo").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nlineTo").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -50,7 +48,9 @@ mod test_examples_line_to {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -157,10 +157,10 @@ impl crate::docs::StdLibFn for LineTo {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -3,8 +3,7 @@ mod test_examples_min {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_min0() { async fn test_mock_example_min0() {
let program = let program =
crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmin").unwrap(); crate::Program::parse("This is another code block.\nyes sirrr.\nmin").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -16,7 +15,9 @@ mod test_examples_min {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -35,9 +36,7 @@ mod test_examples_min {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_min1() { async fn test_mock_example_min1() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nmin").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nmin").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -49,7 +48,9 @@ mod test_examples_min {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -148,10 +149,10 @@ impl crate::docs::StdLibFn for Min {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,9 +2,7 @@
mod test_examples_show { mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show0() { async fn test_mock_example_show0() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -16,7 +14,9 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Show {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,9 +2,7 @@
mod test_examples_import { mod test_examples_import {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_import0() { async fn test_mock_example_import0() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nimport").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -16,7 +14,9 @@ mod test_examples_import {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Import {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,9 +2,7 @@
mod test_examples_import { mod test_examples_import {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_import0() { async fn test_mock_example_import0() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nimport").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -16,7 +14,9 @@ mod test_examples_import {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Import {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,9 +2,7 @@
mod test_examples_import { mod test_examples_import {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_import0() { async fn test_mock_example_import0() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nimport").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -16,7 +14,9 @@ mod test_examples_import {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Import {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,9 +2,7 @@
mod test_examples_show { mod test_examples_show {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_show0() { async fn test_mock_example_show0() {
let program = let program = crate::Program::parse("This is code.\nIt does other shit.\nshow").unwrap();
crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -16,7 +14,9 @@ mod test_examples_show {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -112,10 +112,10 @@ impl crate::docs::StdLibFn for Show {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -2,8 +2,7 @@
mod test_examples_some_function { mod test_examples_some_function {
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_mock_example_some_function0() { async fn test_mock_example_some_function0() {
let program = crate::parser::top_level_parse("someFunction()").unwrap(); let program = crate::Program::parse("someFunction()").unwrap();
let id_generator = crate::executor::IdGenerator::default();
let ctx = crate::executor::ExecutorContext { let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new( engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new() crate::engine::conn_mock::EngineConnection::new()
@ -15,7 +14,9 @@ mod test_examples_some_function {
settings: Default::default(), settings: Default::default(),
context_type: crate::executor::ContextType::Mock, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None, id_generator, None).await.unwrap(); ctx.run(&program, &mut crate::ExecState::default())
.await
.unwrap();
} }
#[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[tokio::test(flavor = "multi_thread", worker_threads = 5)]
@ -106,10 +107,10 @@ impl crate::docs::StdLibFn for SomeFunction {
code_blocks code_blocks
.iter() .iter()
.map(|cb| { .map(|cb| {
let program = crate::parser::top_level_parse(cb).unwrap(); let program = crate::Program::parse(cb).unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default(); let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false; options.insert_final_newline = false;
program.recast(&options, 0) program.ast.recast(&options, 0)
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }

View File

@ -1,21 +0,0 @@
[package]
name = "kcl-macros"
description = "Macro for compiling KCL to its AST during Rust compile-time"
version = "0.1.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
databake = "0.1.8"
kcl-lib = { path = "../kcl" }
proc-macro2 = "1"
quote = "1"
syn = { version = "2.0.87", features = ["full"] }
[dev-dependencies]
pretty_assertions = "1.4.1"

View File

@ -1,22 +0,0 @@
//! This crate contains macros for parsing KCL at Rust compile-time.
use databake::*;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, LitStr};
/// Parses KCL into its AST at compile-time.
/// This macro takes exactly one argument: A string literal containing KCL.
/// # Examples
/// ```
/// extern crate alloc;
/// use kcl_compile_macro::parse_kcl;
/// let ast: kcl_lib::ast::types::Program = parse_kcl!("const y = 4");
/// ```
#[proc_macro]
pub fn parse(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
let kcl_src = input.value();
let ast = kcl_lib::parser::top_level_parse(&kcl_src).unwrap();
let ast_struct = ast.bake(&Default::default());
quote!(#ast_struct).into()
}

View File

@ -1,60 +0,0 @@
extern crate alloc;
use kcl_lib::ast::types::{
BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, ModuleId, Node, Program, VariableDeclaration,
VariableDeclarator, VariableKind,
};
use kcl_macros::parse;
use pretty_assertions::assert_eq;
#[test]
fn basic() {
let actual = parse!("const y = 4");
let module_id = ModuleId::default();
let expected = Node {
inner: Program {
body: vec![BodyItem::VariableDeclaration(Box::new(Node::new(
VariableDeclaration {
declarations: vec![Node::new(
VariableDeclarator {
id: Node::new(
Identifier {
name: "y".to_owned(),
digest: None,
},
6,
7,
module_id,
),
init: Expr::Literal(Box::new(Node::new(
Literal {
value: LiteralValue::IInteger(4),
raw: "4".to_owned(),
digest: None,
},
10,
11,
module_id,
))),
digest: None,
},
6,
11,
module_id,
)],
visibility: ItemVisibility::Default,
kind: VariableKind::Const,
digest: None,
},
0,
11,
module_id,
)))],
non_code_meta: Default::default(),
digest: None,
},
start: 0,
end: 11,
module_id,
};
assert_eq!(expected, actual);
}

View File

@ -15,7 +15,7 @@ use hyper::{
service::{make_service_fn, service_fn}, service::{make_service_fn, service_fn},
Body, Error, Response, Server, Body, Error, Response, Server,
}; };
use kcl_lib::{ast::types::ModuleId, executor::ExecutorContext, settings::types::UnitLength, test_server::RequestBody}; use kcl_lib::{test_server::RequestBody, ExecState, ExecutorContext, Program, UnitLength};
use tokio::{ use tokio::{
sync::{mpsc, oneshot}, sync::{mpsc, oneshot},
task::JoinHandle, task::JoinHandle,
@ -157,21 +157,18 @@ async fn snapshot_endpoint(body: Bytes, state: ExecutorContext) -> Response<Body
Err(e) => return bad_request(format!("Invalid request JSON: {e}")), Err(e) => return bad_request(format!("Invalid request JSON: {e}")),
}; };
let RequestBody { kcl_program, test_name } = body; let RequestBody { kcl_program, test_name } = body;
let module_id = ModuleId::default();
let parser = match kcl_lib::token::lexer(&kcl_program, module_id) { let program = match Program::parse(&kcl_program) {
Ok(ts) => kcl_lib::parser::Parser::new(ts),
Err(e) => return bad_request(format!("tokenization error: {e}")),
};
let program = match parser.ast() {
Ok(pr) => pr, Ok(pr) => pr,
Err(e) => return bad_request(format!("Parse error: {e}")), Err(e) => return bad_request(format!("Parse error: {e}")),
}; };
eprintln!("Executing {test_name}"); eprintln!("Executing {test_name}");
let mut id_generator = kcl_lib::executor::IdGenerator::default(); let mut exec_state = ExecState::default();
// This is a shitty source range, I don't know what else to use for it though. // This is a shitty source range, I don't know what else to use for it though.
// There's no actual KCL associated with this reset_scene call. // There's no actual KCL associated with this reset_scene call.
if let Err(e) = state if let Err(e) = state
.reset_scene(&mut id_generator, kcl_lib::executor::SourceRange::default()) .reset_scene(&mut exec_state, kcl_lib::SourceRange::default())
.await .await
{ {
return kcl_err(e); return kcl_err(e);
@ -179,7 +176,7 @@ async fn snapshot_endpoint(body: Bytes, state: ExecutorContext) -> Response<Body
// Let users know if the test is taking a long time. // Let users know if the test is taking a long time.
let (done_tx, done_rx) = oneshot::channel::<()>(); let (done_tx, done_rx) = oneshot::channel::<()>();
let timer = time_until(done_rx); let timer = time_until(done_rx);
let snapshot = match state.execute_and_prepare_snapshot(&program, id_generator, None).await { let snapshot = match state.execute_and_prepare_snapshot(&program, &mut exec_state).await {
Ok(sn) => sn, Ok(sn) => sn,
Err(e) => return kcl_err(e), Err(e) => return kcl_err(e),
}; };

View File

@ -1,9 +1,8 @@
use anyhow::Result; use anyhow::Result;
use indexmap::IndexMap; use indexmap::IndexMap;
use kcl_lib::{ use kcl_lib::{
engine::ExecutionKind, exec::{DefaultPlanes, IdGenerator},
errors::KclError, ExecutionKind, KclError,
executor::{DefaultPlanes, IdGenerator},
}; };
use kittycad_modeling_cmds::{ use kittycad_modeling_cmds::{
self as kcmc, self as kcmc,
@ -23,8 +22,8 @@ const NEED_PLANES: bool = true;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EngineConnection { pub struct EngineConnection {
batch: Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::executor::SourceRange)>>>, batch: Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>>,
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>>, batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>>,
core_test: Arc<Mutex<String>>, core_test: Arc<Mutex<String>>,
default_planes: Arc<RwLock<Option<DefaultPlanes>>>, default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
execution_kind: Arc<Mutex<ExecutionKind>>, execution_kind: Arc<Mutex<ExecutionKind>>,
@ -354,12 +353,12 @@ fn codegen_cpp_repl_uuid_setters(reps_id: &str, entity_ids: &[uuid::Uuid]) -> St
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl kcl_lib::engine::EngineManager for EngineConnection { impl kcl_lib::EngineManager for EngineConnection {
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::executor::SourceRange)>>> { fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>> {
self.batch.clone() self.batch.clone()
} }
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>> { fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>> {
self.batch_end.clone() self.batch_end.clone()
} }
@ -378,7 +377,7 @@ impl kcl_lib::engine::EngineManager for EngineConnection {
async fn default_planes( async fn default_planes(
&self, &self,
id_generator: &mut IdGenerator, id_generator: &mut IdGenerator,
source_range: kcl_lib::executor::SourceRange, source_range: kcl_lib::SourceRange,
) -> Result<DefaultPlanes, KclError> { ) -> Result<DefaultPlanes, KclError> {
if NEED_PLANES { if NEED_PLANES {
{ {
@ -400,7 +399,7 @@ impl kcl_lib::engine::EngineManager for EngineConnection {
async fn clear_scene_post_hook( async fn clear_scene_post_hook(
&self, &self,
_id_generator: &mut IdGenerator, _id_generator: &mut IdGenerator,
_source_range: kcl_lib::executor::SourceRange, _source_range: kcl_lib::SourceRange,
) -> Result<(), KclError> { ) -> Result<(), KclError> {
Ok(()) Ok(())
} }
@ -408,9 +407,9 @@ impl kcl_lib::engine::EngineManager for EngineConnection {
async fn inner_send_modeling_cmd( async fn inner_send_modeling_cmd(
&self, &self,
id: uuid::Uuid, id: uuid::Uuid,
_source_range: kcl_lib::executor::SourceRange, _source_range: kcl_lib::SourceRange,
cmd: WebSocketRequest, cmd: WebSocketRequest,
_id_to_source_range: std::collections::HashMap<uuid::Uuid, kcl_lib::executor::SourceRange>, _id_to_source_range: std::collections::HashMap<uuid::Uuid, kcl_lib::SourceRange>,
) -> Result<WebSocketResponse, KclError> { ) -> Result<WebSocketResponse, KclError> {
match cmd { match cmd {
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {

View File

@ -1,5 +1,5 @@
use anyhow::Result; use anyhow::Result;
use kcl_lib::executor::{ExecutorContext, IdGenerator}; use kcl_lib::{ExecState, ExecutorContext};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -7,21 +7,15 @@ mod conn_mock_core;
///Converts the given kcl code to an engine test ///Converts the given kcl code to an engine test
pub async fn kcl_to_engine_core(code: &str) -> Result<String> { pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
let program = kcl_lib::parser::top_level_parse(code)?; let program = kcl_lib::Program::parse(code)?;
let result = Arc::new(Mutex::new("".into())); let result = Arc::new(Mutex::new("".into()));
let ref_result = Arc::clone(&result); let ref_result = Arc::clone(&result);
let ctx = ExecutorContext { let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new(
engine: Arc::new(Box::new(
crate::conn_mock_core::EngineConnection::new(ref_result).await?, crate::conn_mock_core::EngineConnection::new(ref_result).await?,
)), )));
fs: Arc::new(kcl_lib::fs::FileManager::new()), ctx.run(&program, &mut ExecState::default()).await?;
stdlib: Arc::new(kcl_lib::std::StdLib::new()),
settings: Default::default(),
context_type: kcl_lib::executor::ContextType::MockCustomForwarded,
};
let _memory = ctx.run(&program, None, IdGenerator::default(), None).await?;
let result = result.lock().expect("mutex lock").clone(); let result = result.lock().expect("mutex lock").clone();
Ok(result) Ok(result)

View File

@ -1,12 +1,5 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
pub fn bench_lex(c: &mut Criterion) {
let module_id = kcl_lib::ast::types::ModuleId::default();
c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM, module_id)));
c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM, module_id)));
c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM, module_id)));
}
pub fn bench_parse(c: &mut Criterion) { pub fn bench_parse(c: &mut Criterion) {
for (name, file) in [ for (name, file) in [
("pipes_on_pipes", PIPES_PROGRAM), ("pipes_on_pipes", PIPES_PROGRAM),
@ -16,23 +9,15 @@ pub fn bench_parse(c: &mut Criterion) {
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM), ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
("koch snowflake", LSYSTEM_KOCH_SNOWFLAKE_PROGRAM), ("koch snowflake", LSYSTEM_KOCH_SNOWFLAKE_PROGRAM),
] { ] {
let module_id = kcl_lib::ast::types::ModuleId::default();
let tokens = kcl_lib::token::lexer(file, module_id).unwrap();
c.bench_function(&format!("parse_{name}"), move |b| { c.bench_function(&format!("parse_{name}"), move |b| {
let tok = tokens.clone();
b.iter(move || { b.iter(move || {
let parser = kcl_lib::parser::Parser::new(tok.clone()); black_box(kcl_lib::Program::parse(file).unwrap());
black_box(parser.ast().unwrap());
}) })
}); });
} }
} }
fn lex(program: &str, module_id: kcl_lib::ast::types::ModuleId) { criterion_group!(benches, bench_parse);
black_box(kcl_lib::token::lexer(program, module_id).unwrap());
}
criterion_group!(benches, bench_lex, bench_parse);
criterion_main!(benches); criterion_main!(benches);
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl"); const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");

View File

@ -1,32 +1,7 @@
use iai::black_box; use iai::black_box;
pub fn parse(program: &str) { pub fn parse(program: &str) {
let module_id = kcl_lib::ast::types::ModuleId::default(); black_box(kcl_lib::Program::parse(program).unwrap());
let tokens = kcl_lib::token::lexer(program, module_id).unwrap();
let tok = tokens.clone();
let parser = kcl_lib::parser::Parser::new(tok.clone());
black_box(parser.ast().unwrap());
}
fn lex_kitt() {
let module_id = kcl_lib::ast::types::ModuleId::default();
black_box(kcl_lib::token::lexer(KITT_PROGRAM, module_id).unwrap());
}
fn lex_pipes() {
let module_id = kcl_lib::ast::types::ModuleId::default();
black_box(kcl_lib::token::lexer(PIPES_PROGRAM, module_id).unwrap());
}
fn lex_cube() {
let module_id = kcl_lib::ast::types::ModuleId::default();
black_box(kcl_lib::token::lexer(CUBE_PROGRAM, module_id).unwrap());
}
fn lex_math() {
let module_id = kcl_lib::ast::types::ModuleId::default();
black_box(kcl_lib::token::lexer(MATH_PROGRAM, module_id).unwrap());
}
fn lex_lsystem() {
let module_id = kcl_lib::ast::types::ModuleId::default();
black_box(kcl_lib::token::lexer(LSYSTEM_PROGRAM, module_id).unwrap());
} }
fn parse_kitt() { fn parse_kitt() {
@ -46,11 +21,6 @@ fn parse_lsystem() {
} }
iai::main! { iai::main! {
lex_kitt,
lex_pipes,
lex_cube,
lex_math,
lex_lsystem,
parse_kitt, parse_kitt,
parse_pipes, parse_pipes,
parse_cube, parse_cube,

View File

@ -9,7 +9,7 @@ pub fn bench_digest(c: &mut Criterion) {
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM), ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
("lsystem", LSYSTEM_PROGRAM), ("lsystem", LSYSTEM_PROGRAM),
] { ] {
let prog = kcl_lib::parser::top_level_parse(file).unwrap(); let prog = kcl_lib::Program::parse(file).unwrap();
c.bench_function(&format!("digest_{name}"), move |b| { c.bench_function(&format!("digest_{name}"), move |b| {
let prog = prog.clone(); let prog = prog.clone();

View File

@ -1,5 +1,5 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use kcl_lib::{settings::types::UnitLength::Mm, test_server}; use kcl_lib::{test_server, UnitLength::Mm};
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
pub fn bench_execute(c: &mut Criterion) { pub fn bench_execute(c: &mut Criterion) {

View File

@ -3,7 +3,7 @@ use iai::black_box;
async fn execute_server_rack_heavy() { async fn execute_server_rack_heavy() {
let code = SERVER_RACK_HEAVY_PROGRAM; let code = SERVER_RACK_HEAVY_PROGRAM;
black_box( black_box(
kcl_lib::test_server::execute_and_snapshot(code, kcl_lib::settings::types::UnitLength::Mm) kcl_lib::test_server::execute_and_snapshot(code, kcl_lib::UnitLength::Mm)
.await .await
.unwrap(), .unwrap(),
); );
@ -12,7 +12,7 @@ async fn execute_server_rack_heavy() {
async fn execute_server_rack_lite() { async fn execute_server_rack_lite() {
let code = SERVER_RACK_LITE_PROGRAM; let code = SERVER_RACK_LITE_PROGRAM;
black_box( black_box(
kcl_lib::test_server::execute_and_snapshot(code, kcl_lib::settings::types::UnitLength::Mm) kcl_lib::test_server::execute_and_snapshot(code, kcl_lib::UnitLength::Mm)
.await .await
.unwrap(), .unwrap(),
); );

View File

@ -1,5 +1,5 @@
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use kcl_lib::lsp::test_util::kcl_lsp_server; use kcl_lib::kcl_lsp_server;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use tower_lsp::LanguageServer; use tower_lsp::LanguageServer;

View File

@ -1,5 +1,5 @@
use iai::black_box; use iai::black_box;
use kcl_lib::lsp::test_util::kcl_lsp_server; use kcl_lib::kcl_lsp_server;
use tower_lsp::LanguageServer; use tower_lsp::LanguageServer;
async fn kcl_lsp_semantic_tokens(code: &str) { async fn kcl_lsp_semantic_tokens(code: &str) {

View File

@ -9,11 +9,12 @@ use kittycad_modeling_cmds as kcmc;
use crate::{ use crate::{
ast::types::{ ast::types::{
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, PipeExpression, PipeSubstitution, ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, PipeExpression, PipeSubstitution,
Program, VariableDeclarator, VariableDeclarator,
}, },
engine::EngineManager, engine::EngineManager,
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::{Point2d, SourceRange}, executor::{Point2d, SourceRange},
Program,
}; };
use super::types::{ModuleId, Node}; use super::types::{ModuleId, Node};
@ -22,13 +23,13 @@ type Point3d = kcmc::shared::Point3d<f64>;
#[derive(Debug)] #[derive(Debug)]
/// The control point data for a curve or line. /// The control point data for a curve or line.
pub struct ControlPointData { struct ControlPointData {
/// The control points for the curve or line. /// The control points for the curve or line.
pub points: Vec<Point3d>, points: Vec<Point3d>,
/// The command that created this curve or line. /// The command that created this curve or line.
pub command: PathCommand, _command: PathCommand,
/// The id of the curve or line. /// The id of the curve or line.
pub id: uuid::Uuid, _id: uuid::Uuid,
} }
const EPSILON: f64 = 0.015625; // or 2^-6 const EPSILON: f64 = 0.015625; // or 2^-6
@ -37,7 +38,7 @@ const EPSILON: f64 = 0.015625; // or 2^-6
/// a move or a new line. /// a move or a new line.
pub async fn modify_ast_for_sketch( pub async fn modify_ast_for_sketch(
engine: &Arc<Box<dyn EngineManager>>, engine: &Arc<Box<dyn EngineManager>>,
program: &mut Node<Program>, program: &mut Program,
module_id: ModuleId, module_id: ModuleId,
// The name of the sketch. // The name of the sketch.
sketch_name: &str, sketch_name: &str,
@ -50,7 +51,7 @@ pub async fn modify_ast_for_sketch(
// If it is, we cannot modify it. // If it is, we cannot modify it.
// Get the information about the sketch. // Get the information about the sketch.
if let Some(ast_sketch) = program.get_variable(sketch_name) { if let Some(ast_sketch) = program.ast.get_variable(sketch_name) {
let constraint_level = match ast_sketch { let constraint_level = match ast_sketch {
super::types::Definition::Variable(var) => var.get_constraint_level(), super::types::Definition::Variable(var) => var.get_constraint_level(),
super::types::Definition::Import(import) => import.get_constraint_level(), super::types::Definition::Import(import) => import.get_constraint_level(),
@ -130,8 +131,8 @@ pub async fn modify_ast_for_sketch(
control_points.push(ControlPointData { control_points.push(ControlPointData {
points: data.control_points.clone(), points: data.control_points.clone(),
command: segment.command, _command: segment.command,
id: (*command_id).into(), _id: (*command_id).into(),
}); });
} }
} }
@ -179,12 +180,12 @@ pub async fn modify_ast_for_sketch(
)?; )?;
// Add the sketch back to the program. // Add the sketch back to the program.
program.replace_variable(sketch_name, sketch); program.ast.replace_variable(sketch_name, sketch);
let recasted = program.recast(&FormatOptions::default(), 0); let recasted = program.ast.recast(&FormatOptions::default(), 0);
// Re-parse the ast so we get the correct source ranges. // Re-parse the ast so we get the correct source ranges.
*program = crate::parser::parse(&recasted, module_id)?; *program = crate::parser::parse(&recasted, module_id)?.into();
Ok(recasted) Ok(recasted)
} }

View File

@ -223,7 +223,7 @@ impl Node<Program> {
/// Check the provided Program for any lint findings. /// Check the provided Program for any lint findings.
pub fn lint<'a, RuleT>(&'a self, rule: RuleT) -> Result<Vec<crate::lint::Discovered>> pub fn lint<'a, RuleT>(&'a self, rule: RuleT) -> Result<Vec<crate::lint::Discovered>>
where where
RuleT: crate::lint::rule::Rule<'a>, RuleT: crate::lint::Rule<'a>,
{ {
let v = Arc::new(Mutex::new(vec![])); let v = Arc::new(Mutex::new(vec![]));
crate::walk::walk(self, &|node: crate::walk::Node<'a>| { crate::walk::walk(self, &|node: crate::walk::Node<'a>| {

View File

@ -1,4 +1,5 @@
//! Core dump related structures and functions. //! Core dump related structures and functions.
#![allow(dead_code)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub mod local; pub mod local;

View File

@ -26,14 +26,14 @@ type Point3D = kcmc::shared::Point3d<f64>;
use crate::{ use crate::{
ast::types::{ ast::types::{
BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, ModuleId, Node, NodeRef, Program, TagDeclarator, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, ModuleId, Node, NodeRef, TagDeclarator, TagNode,
TagNode,
}, },
engine::{EngineManager, ExecutionKind}, engine::{EngineManager, ExecutionKind},
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
fs::{FileManager, FileSystem}, fs::{FileManager, FileSystem},
settings::types::UnitLength, settings::types::UnitLength,
std::{FnAsArg, StdLib}, std::{FnAsArg, StdLib},
Program,
}; };
/// State for executing a program. /// State for executing a program.
@ -152,6 +152,7 @@ impl ProgramMemory {
/// Find all solids in the memory that are on a specific sketch id. /// Find all solids in the memory that are on a specific sketch id.
/// This does not look inside closures. But as long as we do not allow /// This does not look inside closures. But as long as we do not allow
/// mutation of variables in KCL, closure memory should be a subset of this. /// mutation of variables in KCL, closure memory should be a subset of this.
#[allow(clippy::vec_box)]
pub fn find_solids_on_sketch(&self, sketch_id: uuid::Uuid) -> Vec<Box<Solid>> { pub fn find_solids_on_sketch(&self, sketch_id: uuid::Uuid) -> Vec<Box<Solid>> {
self.environments self.environments
.iter() .iter()
@ -537,6 +538,7 @@ impl Geometry {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[allow(clippy::vec_box)]
pub enum Geometries { pub enum Geometries {
Sketches(Vec<Box<Sketch>>), Sketches(Vec<Box<Sketch>>),
Solids(Vec<Box<Solid>>), Solids(Vec<Box<Solid>>),
@ -555,6 +557,7 @@ impl From<Geometry> for Geometries {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")] #[serde(tag = "type", rename_all = "camelCase")]
#[allow(clippy::vec_box)]
pub enum SketchSet { pub enum SketchSet {
Sketch(Box<Sketch>), Sketch(Box<Sketch>),
Sketches(Vec<Box<Sketch>>), Sketches(Vec<Box<Sketch>>),
@ -635,6 +638,7 @@ impl From<Box<Sketch>> for Vec<Box<Sketch>> {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")] #[serde(tag = "type", rename_all = "camelCase")]
#[allow(clippy::vec_box)]
pub enum SolidSet { pub enum SolidSet {
Solid(Box<Solid>), Solid(Box<Solid>),
Solids(Vec<Box<Solid>>), Solids(Vec<Box<Solid>>),
@ -2189,6 +2193,70 @@ impl ExecutorContext {
}) })
} }
#[cfg(not(target_arch = "wasm32"))]
pub async fn new_mock() -> Self {
ExecutorContext {
engine: Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new().await.unwrap(),
)),
fs: Arc::new(FileManager::new()),
stdlib: Arc::new(StdLib::new()),
settings: Default::default(),
context_type: ContextType::Mock,
}
}
#[cfg(target_arch = "wasm32")]
pub async fn new(
engine_manager: crate::engine::conn_wasm::EngineCommandManager,
fs_manager: crate::fs::wasm::FileSystemManager,
units: UnitLength,
) -> Result<Self, String> {
Ok(ExecutorContext {
engine: Arc::new(Box::new(
crate::engine::conn_wasm::EngineConnection::new(engine_manager)
.await
.map_err(|e| format!("{:?}", e))?,
)),
fs: Arc::new(FileManager::new(fs_manager)),
stdlib: Arc::new(StdLib::new()),
settings: ExecutorSettings {
units,
..Default::default()
},
context_type: ContextType::Live,
})
}
#[cfg(target_arch = "wasm32")]
pub async fn new_mock(fs_manager: crate::fs::wasm::FileSystemManager, units: UnitLength) -> Result<Self, String> {
Ok(ExecutorContext {
engine: Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new()
.await
.map_err(|e| format!("{:?}", e))?,
)),
fs: Arc::new(FileManager::new(fs_manager)),
stdlib: Arc::new(StdLib::new()),
settings: ExecutorSettings {
units,
..Default::default()
},
context_type: ContextType::Mock,
})
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_forwarded_mock(engine: Arc<Box<dyn EngineManager>>) -> Self {
ExecutorContext {
engine,
fs: Arc::new(FileManager::new()),
stdlib: Arc::new(StdLib::new()),
settings: Default::default(),
context_type: ContextType::MockCustomForwarded,
}
}
/// Create a new default executor context. /// Create a new default executor context.
/// With a kittycad client. /// With a kittycad client.
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment /// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
@ -2212,9 +2280,17 @@ impl ExecutorContext {
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment /// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
/// variables. /// variables.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub async fn new_with_default_client(settings: ExecutorSettings) -> Result<Self> { pub async fn new_with_default_client(units: UnitLength) -> Result<Self> {
// Create the client. // Create the client.
let ctx = Self::new_with_client(settings, None, None).await?; let ctx = Self::new_with_client(
ExecutorSettings {
units,
..Default::default()
},
None,
None,
)
.await?;
Ok(ctx) Ok(ctx)
} }
@ -2242,48 +2318,31 @@ impl ExecutorContext {
pub async fn reset_scene( pub async fn reset_scene(
&self, &self,
id_generator: &mut IdGenerator, exec_state: &mut ExecState,
source_range: crate::executor::SourceRange, source_range: crate::executor::SourceRange,
) -> Result<()> { ) -> Result<()> {
self.engine.clear_scene(id_generator, source_range).await?; self.engine
.clear_scene(&mut exec_state.id_generator, source_range)
.await?;
Ok(()) Ok(())
} }
/// Perform the execution of a program. /// Perform the execution of a program.
/// You can optionally pass in some initialization memory. /// You can optionally pass in some initialization memory.
/// Kurt uses this for partial execution. /// Kurt uses this for partial execution.
pub async fn run( pub async fn run(&self, program: &Program, exec_state: &mut ExecState) -> Result<(), KclError> {
&self, self.run_with_session_data(program, exec_state).await?;
program: NodeRef<'_, crate::ast::types::Program>, Ok(())
memory: Option<ProgramMemory>,
id_generator: IdGenerator,
project_directory: Option<String>,
) -> Result<ExecState, KclError> {
self.run_with_session_data(program, memory, id_generator, project_directory)
.await
.map(|x| x.0)
} }
/// Perform the execution of a program. /// Perform the execution of a program.
/// You can optionally pass in some initialization memory. /// You can optionally pass in some initialization memory.
/// Kurt uses this for partial execution. /// Kurt uses this for partial execution.
pub async fn run_with_session_data( pub async fn run_with_session_data(
&self, &self,
program: NodeRef<'_, crate::ast::types::Program>, program: &Program,
memory: Option<ProgramMemory>, exec_state: &mut ExecState,
id_generator: IdGenerator, ) -> Result<Option<ModelingSessionData>, KclError> {
project_directory: Option<String>,
) -> Result<(ExecState, Option<ModelingSessionData>), KclError> {
let memory = if let Some(memory) = memory {
memory.clone()
} else {
Default::default()
};
let mut exec_state = ExecState {
memory,
id_generator,
project_directory,
..Default::default()
};
// TODO: Use the top-level file's path. // TODO: Use the top-level file's path.
exec_state.add_module(std::path::PathBuf::from("")); exec_state.add_module(std::path::PathBuf::from(""));
// Before we even start executing the program, set the units. // Before we even start executing the program, set the units.
@ -2304,10 +2363,10 @@ impl ExecutorContext {
) )
.await?; .await?;
self.inner_execute(program, &mut exec_state, crate::executor::BodyType::Root) self.inner_execute(&program.ast, exec_state, crate::executor::BodyType::Root)
.await?; .await?;
let session_data = self.engine.get_session_data(); let session_data = self.engine.get_session_data();
Ok((exec_state, session_data)) Ok(session_data)
} }
/// Execute an AST's program. /// Execute an AST's program.
@ -2558,23 +2617,15 @@ impl ExecutorContext {
/// Execute the program, then get a PNG screenshot. /// Execute the program, then get a PNG screenshot.
pub async fn execute_and_prepare_snapshot( pub async fn execute_and_prepare_snapshot(
&self, &self,
program: NodeRef<'_, Program>, program: &Program,
id_generator: IdGenerator, exec_state: &mut ExecState,
project_directory: Option<String>,
) -> Result<TakeSnapshot> { ) -> Result<TakeSnapshot> {
self.execute_and_prepare(program, id_generator, project_directory) self.execute_and_prepare(program, exec_state).await
.await
.map(|(_state, snap)| snap)
} }
/// Execute the program, return the interpreter and outputs. /// Execute the program, return the interpreter and outputs.
pub async fn execute_and_prepare( pub async fn execute_and_prepare(&self, program: &Program, exec_state: &mut ExecState) -> Result<TakeSnapshot> {
&self, self.run(program, exec_state).await?;
program: NodeRef<'_, Program>,
id_generator: IdGenerator,
project_directory: Option<String>,
) -> Result<(ExecState, TakeSnapshot)> {
let state = self.run(program, None, id_generator, project_directory).await?;
// Zoom to fit. // Zoom to fit.
self.engine self.engine
@ -2607,7 +2658,7 @@ impl ExecutorContext {
else { else {
anyhow::bail!("Unexpected response from engine: {:?}", resp); anyhow::bail!("Unexpected response from engine: {:?}", resp);
}; };
Ok((state, contents)) Ok(contents)
} }
} }
@ -2714,7 +2765,7 @@ mod tests {
use crate::ast::types::{Identifier, Node, Parameter}; use crate::ast::types::{Identifier, Node, Parameter};
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> { pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
let program = crate::parser::top_level_parse(code)?; let program = Program::parse(code)?;
let ctx = ExecutorContext { let ctx = ExecutorContext {
engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)), engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
@ -2723,7 +2774,8 @@ mod tests {
settings: Default::default(), settings: Default::default(),
context_type: ContextType::Mock, context_type: ContextType::Mock,
}; };
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?; let mut exec_state = ExecState::default();
ctx.run(&program, &mut exec_state).await?;
Ok(exec_state.memory) Ok(exec_state.memory)
} }

View File

@ -13,26 +13,112 @@ macro_rules! println {
} }
} }
pub mod ast; mod ast;
pub mod coredump; mod coredump;
pub mod docs; mod docs;
pub mod engine; mod engine;
pub mod errors; mod errors;
pub mod executor; mod executor;
pub mod fs; mod fs;
mod function_param; mod function_param;
pub mod lint; pub mod lint;
pub mod lsp; mod lsp;
pub mod parser; mod parser;
pub mod settings; mod settings;
#[cfg(test)] #[cfg(test)]
mod simulation_tests; mod simulation_tests;
pub mod std; mod std;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub mod test_server; pub mod test_server;
pub mod thread; mod thread;
pub mod token; mod token;
mod unparser; mod unparser;
pub mod walk; mod walk;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub mod wasm; mod wasm;
pub use ast::modify::modify_ast_for_sketch;
pub use ast::types::{FormatOptions, ModuleId};
pub use coredump::CoreDump;
pub use engine::{EngineManager, ExecutionKind};
pub use errors::KclError;
pub use executor::{ExecState, ExecutorContext, ExecutorSettings, SourceRange};
pub use lsp::copilot::Backend as CopilotLspBackend;
pub use lsp::kcl::Backend as KclLspBackend;
pub use lsp::kcl::Server as KclLspServerSubCommand;
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
pub use token::lexer;
// Rather than make executor public and make lots of it pub(crate), just re-export into a new module.
// Ideally we wouldn't export these things at all, they should only be used for testing.
pub mod exec {
pub use crate::executor::{DefaultPlanes, IdGenerator, KclValue, PlaneType, ProgramMemory, Sketch};
}
#[cfg(target_arch = "wasm32")]
pub mod wasm_engine {
pub use crate::coredump::wasm::{CoreDumpManager, CoreDumper};
pub use crate::engine::conn_wasm::{EngineCommandManager, EngineConnection};
pub use crate::fs::wasm::FileSystemManager;
}
#[cfg(not(target_arch = "wasm32"))]
pub mod native_engine {
pub use crate::engine::conn::EngineConnection;
}
pub mod std_utils {
pub use crate::std::utils::{get_tangential_arc_to_info, is_points_ccw_wasm, TangentialArcInfoInput};
}
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Program {
#[serde(flatten)]
ast: ast::types::Node<ast::types::Program>,
}
#[cfg(any(test, feature = "lsp-test-util"))]
pub use lsp::test_util::copilot_lsp_server;
#[cfg(any(test, feature = "lsp-test-util"))]
pub use lsp::test_util::kcl_lsp_server;
impl Program {
pub fn parse(input: &str) -> Result<Program, KclError> {
let module_id = ModuleId::default();
let tokens = token::lexer(input, module_id)?;
let parser = parser::Parser::new(tokens);
let ast = parser.ast()?;
Ok(Program { ast })
}
/// Deserialize the ast from a stringified json
pub fn compute_digest(&mut self) -> ast::types::digest::Digest {
self.ast.compute_digest()
}
pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
self.ast.lint_all()
}
pub fn lint<'a>(&'a self, rule: impl lint::Rule<'a>) -> Result<Vec<lint::Discovered>, anyhow::Error> {
self.ast.lint(rule)
}
pub fn recast(&self) -> String {
// Use the default options until we integrate into the UI the ability to change them.
self.ast.recast(&Default::default(), 0)
}
pub fn recast_with_options(&self, options: &FormatOptions) -> String {
self.ast.recast(options, 0)
}
}
impl From<ast::types::Node<ast::types::Program>> for Program {
fn from(ast: ast::types::Node<ast::types::Program>) -> Program {
Self { ast }
}
}

View File

@ -2,7 +2,6 @@ mod camel_case;
mod offset_plane; mod offset_plane;
mod std_lib_args; mod std_lib_args;
#[allow(unused_imports)]
pub use camel_case::{lint_object_properties, lint_variables, Z0001}; pub use camel_case::{lint_object_properties, lint_variables, Z0001};
pub use offset_plane::{lint_should_be_offset_plane, Z0003}; pub use offset_plane::{lint_should_be_offset_plane, Z0003};
pub use std_lib_args::{lint_call_expressions, Z0002}; pub use std_lib_args::{lint_call_expressions, Z0002};

View File

@ -1,4 +1,4 @@
pub mod checks; pub mod checks;
pub mod rule; mod rule;
pub use rule::{Discovered, Finding}; pub use rule::{Discovered, Finding, Rule};

View File

@ -1,4 +1,5 @@
//! The copilot lsp server for ghost text. //! The copilot lsp server for ghost text.
#![allow(dead_code)]
pub mod cache; pub mod cache;
pub mod types; pub mod types;
@ -26,6 +27,7 @@ use tower_lsp::{
use crate::lsp::{ use crate::lsp::{
backend::Backend as _, backend::Backend as _,
copilot::cache::CopilotCache,
copilot::types::{ copilot::types::{
CopilotAcceptCompletionParams, CopilotCompletionResponse, CopilotCompletionTelemetry, CopilotEditorInfo, CopilotAcceptCompletionParams, CopilotCompletionResponse, CopilotCompletionTelemetry, CopilotEditorInfo,
CopilotLspCompletionParams, CopilotRejectCompletionParams, DocParams, CopilotLspCompletionParams, CopilotRejectCompletionParams, DocParams,
@ -131,6 +133,38 @@ impl crate::lsp::backend::Backend for Backend {
} }
impl Backend { impl Backend {
#[cfg(target_arch = "wasm32")]
pub fn new_wasm(
client: tower_lsp::Client,
fs: crate::fs::wasm::FileSystemManager,
zoo_client: kittycad::Client,
dev_mode: bool,
) -> Self {
Self::new(client, crate::fs::FileManager::new(fs), zoo_client, dev_mode)
}
pub fn new(
client: tower_lsp::Client,
fs: crate::fs::FileManager,
zoo_client: kittycad::Client,
dev_mode: bool,
) -> Self {
Self {
client,
fs: Arc::new(fs),
workspace_folders: Default::default(),
code_map: Default::default(),
editor_info: Arc::new(RwLock::new(CopilotEditorInfo::default())),
cache: Arc::new(CopilotCache::new()),
telemetry: Default::default(),
zoo_client,
is_initialized: Default::default(),
diagnostics_map: Default::default(),
dev_mode,
}
}
/// Get completions from the kittycad api. /// Get completions from the kittycad api.
pub async fn get_completions(&self, language: String, prompt: String, suffix: String) -> Result<Vec<String>> { pub async fn get_completions(&self, language: String, prompt: String, suffix: String) -> Result<Vec<String>> {
let body = kittycad::types::KclCodeCompletionRequest { let body = kittycad::types::KclCodeCompletionRequest {

View File

@ -1,4 +1,5 @@
//! Functions for the `kcl` lsp server. //! Functions for the `kcl` lsp server.
#![allow(dead_code)]
use std::{ use std::{
collections::HashMap, collections::HashMap,
@ -40,11 +41,11 @@ use tower_lsp::{
}; };
use crate::{ use crate::{
ast::types::{Expr, ModuleId, Node, NodeRef, VariableKind}, ast::types::{Expr, ModuleId, Node, VariableKind},
executor::{IdGenerator, SourceRange},
lsp::{backend::Backend as _, util::IntoDiagnostic}, lsp::{backend::Backend as _, util::IntoDiagnostic},
parser::PIPE_OPERATOR, parser::PIPE_OPERATOR,
token::TokenType, token::TokenType,
ExecState, Program, SourceRange,
}; };
lazy_static::lazy_static! { lazy_static::lazy_static! {
@ -122,6 +123,73 @@ pub struct Backend {
pub is_initialized: Arc<RwLock<bool>>, pub is_initialized: Arc<RwLock<bool>>,
} }
impl Backend {
#[cfg(target_arch = "wasm32")]
pub fn new_wasm(
client: Client,
executor_ctx: Option<crate::executor::ExecutorContext>,
fs: crate::fs::wasm::FileSystemManager,
zoo_client: kittycad::Client,
can_send_telemetry: bool,
) -> Result<Self, String> {
Self::with_file_manager(
client,
executor_ctx,
crate::fs::FileManager::new(fs),
zoo_client,
can_send_telemetry,
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new(
client: Client,
executor_ctx: Option<crate::executor::ExecutorContext>,
zoo_client: kittycad::Client,
can_send_telemetry: bool,
) -> Result<Self, String> {
Self::with_file_manager(
client,
executor_ctx,
crate::fs::FileManager::new(),
zoo_client,
can_send_telemetry,
)
}
fn with_file_manager(
client: Client,
executor_ctx: Option<crate::executor::ExecutorContext>,
fs: crate::fs::FileManager,
zoo_client: kittycad::Client,
can_send_telemetry: bool,
) -> Result<Self, String> {
let stdlib = crate::std::StdLib::new();
let stdlib_completions = get_completions_from_stdlib(&stdlib).map_err(|e| e.to_string())?;
let stdlib_signatures = get_signatures_from_stdlib(&stdlib).map_err(|e| e.to_string())?;
Ok(Self {
client,
fs: Arc::new(fs),
stdlib_completions,
stdlib_signatures,
zoo_client,
can_send_telemetry,
can_execute: Arc::new(RwLock::new(executor_ctx.is_some())),
executor_ctx: Arc::new(RwLock::new(executor_ctx)),
workspace_folders: Default::default(),
token_map: Default::default(),
ast_map: Default::default(),
memory_map: Default::default(),
code_map: Default::default(),
diagnostics_map: Default::default(),
symbols_map: Default::default(),
semantic_tokens_map: Default::default(),
is_initialized: Default::default(),
})
}
}
// Implement the shared backend trait for the language server. // Implement the shared backend trait for the language server.
#[async_trait::async_trait] #[async_trait::async_trait]
impl crate::lsp::backend::Backend for Backend { impl crate::lsp::backend::Backend for Backend {
@ -289,7 +357,7 @@ impl crate::lsp::backend::Backend for Backend {
// Execute the code if we have an executor context. // Execute the code if we have an executor context.
// This function automatically executes if we should & updates the diagnostics if we got // This function automatically executes if we should & updates the diagnostics if we got
// errors. // errors.
if self.execute(&params, &ast).await.is_err() { if self.execute(&params, &ast.into()).await.is_err() {
return; return;
} }
@ -572,7 +640,7 @@ impl Backend {
self.client.publish_diagnostics(params.uri.clone(), items, None).await; self.client.publish_diagnostics(params.uri.clone(), items, None).await;
} }
async fn execute(&self, params: &TextDocumentItem, ast: NodeRef<'_, crate::ast::types::Program>) -> Result<()> { async fn execute(&self, params: &TextDocumentItem, ast: &Program) -> Result<()> {
// Check if we can execute. // Check if we can execute.
if !self.can_execute().await { if !self.can_execute().await {
return Ok(()); return Ok(());
@ -589,17 +657,15 @@ impl Backend {
return Ok(()); return Ok(());
} }
let mut id_generator = IdGenerator::default(); let mut exec_state = ExecState::default();
// Clear the scene, before we execute so it's not fugly as shit. // Clear the scene, before we execute so it's not fugly as shit.
executor_ctx executor_ctx
.engine .engine
.clear_scene(&mut id_generator, SourceRange::default()) .clear_scene(&mut exec_state.id_generator, SourceRange::default())
.await?; .await?;
let exec_state = match executor_ctx.run(ast, None, id_generator, None).await { if let Err(err) = executor_ctx.run(ast, &mut exec_state).await {
Ok(exec_state) => exec_state,
Err(err) => {
self.memory_map.remove(params.uri.as_str()); self.memory_map.remove(params.uri.as_str());
self.add_to_diagnostics(params, &[err], false).await; self.add_to_diagnostics(params, &[err], false).await;
@ -607,7 +673,6 @@ impl Backend {
// string. // string.
return Err(anyhow::anyhow!("failed to execute code")); return Err(anyhow::anyhow!("failed to execute code"));
} }
};
self.memory_map self.memory_map
.insert(params.uri.to_string(), exec_state.memory.clone()); .insert(params.uri.to_string(), exec_state.memory.clone());

View File

@ -12,6 +12,7 @@ pub(crate) mod parser_impl;
pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%"; pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
pub const PIPE_OPERATOR: &str = "|>"; pub const PIPE_OPERATOR: &str = "|>";
#[cfg(test)]
/// Parse the given KCL code into an AST. This is the top-level. /// Parse the given KCL code into an AST. This is the top-level.
pub fn top_level_parse(code: &str) -> Result<Node<Program>, KclError> { pub fn top_level_parse(code: &str) -> Result<Node<Program>, KclError> {
let module_id = ModuleId::default(); let module_id = ModuleId::default();

View File

@ -9,7 +9,6 @@ use serde::{Deserialize, Serialize};
use validator::{Validate, ValidateRange}; use validator::{Validate, ValidateRange};
const DEFAULT_THEME_COLOR: f64 = 264.5; const DEFAULT_THEME_COLOR: f64 = 264.5;
pub const DEFAULT_PROJECT_KCL_FILE: &str = "main.kcl";
const DEFAULT_PROJECT_NAME_TEMPLATE: &str = "project-$nnn"; const DEFAULT_PROJECT_NAME_TEMPLATE: &str = "project-$nnn";
/// High level configuration. /// High level configuration.

View File

@ -92,7 +92,8 @@ async fn execute(test_name: &str, render_to_png: bool) {
}; };
// Run the program. // Run the program.
let exec_res = crate::test_server::execute_and_snapshot_ast(ast, crate::settings::types::UnitLength::Mm).await; let exec_res =
crate::test_server::execute_and_snapshot_ast(ast.into(), crate::settings::types::UnitLength::Mm).await;
match exec_res { match exec_res {
Ok((program_memory, png)) => { Ok((program_memory, png)) => {
if render_to_png { if render_to_png {

View File

@ -4,7 +4,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
ast::types::{BodyItem, Expr, FunctionExpression, Node, Program}, ast::types::{FunctionExpression, Program},
docs::{StdLibFn, StdLibFnData}, docs::{StdLibFn, StdLibFnData},
}; };
@ -77,18 +77,3 @@ impl Serialize for Box<dyn KclStdLibFn> {
self.to_json().unwrap().serialize(serializer) self.to_json().unwrap().serialize(serializer)
} }
} }
/// Parse a KCL program. Expect it to have a single body item, which is a function.
/// Return the program and its single function.
/// Return None if those expectations aren't met.
pub fn extract_function(source: &str) -> Option<(Node<Program>, crate::ast::types::BoxNode<FunctionExpression>)> {
let src = crate::parser::top_level_parse(source).ok()?;
assert_eq!(src.body.len(), 1);
let BodyItem::ExpressionStatement(expr) = src.body.last()? else {
panic!("expected expression statement");
};
let Expr::FunctionExpression(function) = expr.expression.clone() else {
panic!("expected function expr");
};
Some((src, function))
}

View File

@ -48,8 +48,6 @@ pub type StdFn = fn(
Args, Args,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send + '_>>; ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send + '_>>;
pub type FnMap = HashMap<String, StdFn>;
lazy_static! { lazy_static! {
static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![ static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
Box::new(LegLen), Box::new(LegLen),

View File

@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};
#[ts(export)] #[ts(export)]
pub struct Uint(f64); pub struct Uint(f64);
#[allow(dead_code)]
impl Uint { impl Uint {
pub fn new(value: f64) -> Self { pub fn new(value: f64) -> Self {
if value < 0.0 { if value < 0.0 {

View File

@ -53,20 +53,6 @@ pub fn delta(from_angle: Angle, to_angle: Angle) -> Angle {
Angle::default() Angle::default()
} }
pub fn clockwise_sign(points: &[Point2d]) -> i32 {
let mut sum = 0.0;
for i in 0..points.len() {
let current_point = points[i];
let next_point = points[(i + 1) % points.len()];
sum += (next_point.x - current_point.x) * (next_point.y + current_point.y);
}
if sum >= 0.0 {
1
} else {
-1
}
}
pub fn normalize_rad(angle: f64) -> f64 { pub fn normalize_rad(angle: f64) -> f64 {
let draft = angle % (2.0 * PI); let draft = angle % (2.0 * PI);
if draft < 0.0 { if draft < 0.0 {
@ -76,32 +62,6 @@ pub fn normalize_rad(angle: f64) -> f64 {
} }
} }
/// Calculates the distance between two points.
///
/// # Examples
///
/// ```
/// use kcl_lib::executor::Point2d;
///
/// assert_eq!(
/// kcl_lib::std::utils::distance_between_points(Point2d::ZERO, Point2d { x: 0.0, y: 5.0 }),
/// 5.0
/// );
/// assert_eq!(
/// kcl_lib::std::utils::distance_between_points(Point2d::ZERO, Point2d { x: 3.0, y: 4.0 }),
/// 5.0
/// );
/// ```
#[allow(dead_code)]
pub fn distance_between_points(point_a: Point2d, point_b: Point2d) -> f64 {
let x1 = point_a.x;
let y1 = point_a.y;
let x2 = point_b.x;
let y2 = point_b.y;
((y2 - y1).powi(2) + (x2 - x1).powi(2)).sqrt()
}
pub fn calculate_intersection_of_two_lines(line1: &[Point2d; 2], line2_angle: f64, line2_point: Point2d) -> Point2d { pub fn calculate_intersection_of_two_lines(line1: &[Point2d; 2], line2_angle: f64, line2_point: Point2d) -> Point2d {
let line2_point_b = Point2d { let line2_point_b = Point2d {
x: line2_point.x + f64::cos(line2_angle.to_radians()) * 10.0, x: line2_point.x + f64::cos(line2_angle.to_radians()) * 10.0,
@ -563,6 +523,7 @@ pub struct TangentialArcInfoInput {
} }
/// Structure to hold the output data from calculating tangential arc information. /// Structure to hold the output data from calculating tangential arc information.
#[allow(dead_code)]
pub struct TangentialArcInfoOutput { pub struct TangentialArcInfoOutput {
/// The center point of the arc. /// The center point of the arc.
pub center: Coords2d, pub center: Coords2d,

View File

@ -1,9 +1,9 @@
//! Types used to send data to the test server. //! Types used to send data to the test server.
use crate::{ use crate::{
ast::types::{Node, Program}, executor::{new_zoo_client, ExecutorContext, ExecutorSettings, ProgramMemory},
executor::{new_zoo_client, ExecutorContext, ExecutorSettings, IdGenerator, ProgramMemory},
settings::types::UnitLength, settings::types::UnitLength,
ExecState, Program,
}; };
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
@ -17,14 +17,14 @@ pub struct RequestBody {
/// This returns the bytes of the snapshot. /// This returns the bytes of the snapshot.
pub async fn execute_and_snapshot(code: &str, units: UnitLength) -> anyhow::Result<image::DynamicImage> { pub async fn execute_and_snapshot(code: &str, units: UnitLength) -> anyhow::Result<image::DynamicImage> {
let ctx = new_context(units, true).await?; let ctx = new_context(units, true).await?;
let program = crate::parser::top_level_parse(code)?; let program = Program::parse(code)?;
do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap) do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap)
} }
/// Executes a kcl program and takes a snapshot of the result. /// Executes a kcl program and takes a snapshot of the result.
/// This returns the bytes of the snapshot. /// This returns the bytes of the snapshot.
pub async fn execute_and_snapshot_ast( pub async fn execute_and_snapshot_ast(
ast: Node<Program>, ast: Program,
units: UnitLength, units: UnitLength,
) -> anyhow::Result<(ProgramMemory, image::DynamicImage)> { ) -> anyhow::Result<(ProgramMemory, image::DynamicImage)> {
let ctx = new_context(units, true).await?; let ctx = new_context(units, true).await?;
@ -35,15 +35,16 @@ pub async fn execute_and_snapshot_ast(
pub async fn execute_and_snapshot_no_auth(code: &str, units: UnitLength) -> anyhow::Result<image::DynamicImage> { pub async fn execute_and_snapshot_no_auth(code: &str, units: UnitLength) -> anyhow::Result<image::DynamicImage> {
let ctx = new_context(units, false).await?; let ctx = new_context(units, false).await?;
let program = crate::parser::top_level_parse(code)?; let program = Program::parse(code)?;
do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap) do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap)
} }
async fn do_execute_and_snapshot( async fn do_execute_and_snapshot(
ctx: &ExecutorContext, ctx: &ExecutorContext,
program: Node<Program>, program: Program,
) -> anyhow::Result<(crate::executor::ExecState, image::DynamicImage)> { ) -> anyhow::Result<(crate::executor::ExecState, image::DynamicImage)> {
let (exec_state, snapshot) = ctx.execute_and_prepare(&program, IdGenerator::default(), None).await?; let mut exec_state = ExecState::default();
let snapshot = ctx.execute_and_prepare(&program, &mut exec_state).await?;
// Create a temporary file to write the output to. // 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())); let output_file = std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));

View File

@ -1,13 +1,15 @@
//! An implementation of threads that works in wasm with promises and other platforms with tokio. //! An implementation of threads that works in wasm with promises and other platforms with tokio.
#![allow(dead_code)]
#![allow(unused_imports)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub mod local; mod local;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub use local::JoinHandle; pub use local::JoinHandle;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[cfg(not(test))] #[cfg(not(test))]
pub mod wasm; mod wasm;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[cfg(not(test))] #[cfg(not(test))]

View File

@ -1,18 +1,10 @@
//! Wasm bindings for `kcl`. //! Wasm bindings for `kcl`.
use std::{ use std::{str::FromStr, sync::Arc};
str::FromStr,
sync::{Arc, RwLock},
};
use futures::stream::TryStreamExt; use futures::stream::TryStreamExt;
use gloo_utils::format::JsValueSerdeExt; use gloo_utils::format::JsValueSerdeExt;
use kcl_lib::{ use kcl_lib::{CoreDump, EngineManager, ExecState, ModuleId, Program};
ast::types::{ModuleId, Node, Program},
coredump::CoreDump,
engine::EngineManager,
executor::ExecutorSettings,
};
use tower_lsp::{LspService, Server}; use tower_lsp::{LspService, Server};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@ -23,54 +15,32 @@ pub async fn execute_wasm(
memory_str: &str, memory_str: &str,
id_generator_str: &str, id_generator_str: &str,
units: &str, units: &str,
engine_manager: kcl_lib::engine::conn_wasm::EngineCommandManager, engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
fs_manager: kcl_lib::fs::wasm::FileSystemManager, fs_manager: kcl_lib::wasm_engine::FileSystemManager,
project_directory: Option<String>, project_directory: Option<String>,
is_mock: bool, is_mock: bool,
) -> Result<JsValue, String> { ) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
// deserialize the ast from a stringified json
let program: Node<Program> = serde_json::from_str(program_str).map_err(|e| e.to_string())?; let program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let memory: kcl_lib::executor::ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?; let memory: kcl_lib::exec::ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?;
let id_generator: kcl_lib::executor::IdGenerator = let id_generator: kcl_lib::exec::IdGenerator = serde_json::from_str(id_generator_str).map_err(|e| e.to_string())?;
serde_json::from_str(id_generator_str).map_err(|e| e.to_string())?;
let units = kcl_lib::settings::types::UnitLength::from_str(units).map_err(|e| e.to_string())?;
let engine: std::sync::Arc<Box<dyn kcl_lib::engine::EngineManager>> = if is_mock { let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
Arc::new(Box::new( let ctx = if is_mock {
kcl_lib::engine::conn_mock::EngineConnection::new() kcl_lib::ExecutorContext::new_mock(fs_manager, units).await?
.await
.map_err(|e| format!("{:?}", e))?,
))
} else { } else {
Arc::new(Box::new( kcl_lib::ExecutorContext::new(engine_manager, fs_manager, units).await?
kcl_lib::engine::conn_wasm::EngineConnection::new(engine_manager)
.await
.map_err(|e| format!("{:?}", e))?,
))
};
let fs = Arc::new(kcl_lib::fs::FileManager::new(fs_manager));
let context_type = if is_mock {
kcl_lib::executor::ContextType::Mock
} else {
kcl_lib::executor::ContextType::Live
};
let ctx = kcl_lib::executor::ExecutorContext {
engine,
fs,
stdlib: std::sync::Arc::new(kcl_lib::std::StdLib::new()),
settings: ExecutorSettings {
units,
..Default::default()
},
context_type,
}; };
let exec_state = ctx let mut exec_state = ExecState {
.run(&program, Some(memory), id_generator, project_directory) memory,
.await id_generator,
.map_err(String::from)?; project_directory,
..ExecState::default()
};
ctx.run(&program, &mut exec_state).await.map_err(String::from)?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
@ -84,7 +54,7 @@ pub async fn execute_wasm(
pub async fn kcl_lint(program_str: &str) -> Result<JsValue, JsValue> { pub async fn kcl_lint(program_str: &str) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let program: Node<Program> = serde_json::from_str(program_str).map_err(|e| e.to_string())?; let program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let mut findings = vec![]; let mut findings = vec![];
for discovered_finding in program.lint_all().into_iter().flatten() { for discovered_finding in program.lint_all().into_iter().flatten() {
findings.push(discovered_finding); findings.push(discovered_finding);
@ -96,16 +66,16 @@ pub async fn kcl_lint(program_str: &str) -> Result<JsValue, JsValue> {
// wasm_bindgen wrapper for creating default planes // wasm_bindgen wrapper for creating default planes
#[wasm_bindgen] #[wasm_bindgen]
pub async fn make_default_planes( pub async fn make_default_planes(
engine_manager: kcl_lib::engine::conn_wasm::EngineCommandManager, engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
) -> Result<JsValue, String> { ) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
// deserialize the ast from a stringified json // deserialize the ast from a stringified json
let engine = kcl_lib::engine::conn_wasm::EngineConnection::new(engine_manager) let engine = kcl_lib::wasm_engine::EngineConnection::new(engine_manager)
.await .await
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
let default_planes = engine let default_planes = engine
.new_default_planes(&mut kcl_lib::executor::IdGenerator::default(), Default::default()) .new_default_planes(&mut kcl_lib::exec::IdGenerator::default(), Default::default())
.await .await
.map_err(String::from)?; .map_err(String::from)?;
@ -117,13 +87,13 @@ pub async fn make_default_planes(
// wasm_bindgen wrapper for modifying the grid // wasm_bindgen wrapper for modifying the grid
#[wasm_bindgen] #[wasm_bindgen]
pub async fn modify_grid( pub async fn modify_grid(
engine_manager: kcl_lib::engine::conn_wasm::EngineCommandManager, engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
hidden: bool, hidden: bool,
) -> Result<(), String> { ) -> Result<(), String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
// deserialize the ast from a stringified json // deserialize the ast from a stringified json
let engine = kcl_lib::engine::conn_wasm::EngineConnection::new(engine_manager) let engine = kcl_lib::wasm_engine::EngineConnection::new(engine_manager)
.await .await
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
engine.modify_grid(hidden).await.map_err(String::from)?; engine.modify_grid(hidden).await.map_err(String::from)?;
@ -134,7 +104,7 @@ pub async fn modify_grid(
// wasm_bindgen wrapper for execute // wasm_bindgen wrapper for execute
#[wasm_bindgen] #[wasm_bindgen]
pub async fn modify_ast_for_sketch_wasm( pub async fn modify_ast_for_sketch_wasm(
manager: kcl_lib::engine::conn_wasm::EngineCommandManager, manager: kcl_lib::wasm_engine::EngineCommandManager,
program_str: &str, program_str: &str,
sketch_name: &str, sketch_name: &str,
plane_type: &str, plane_type: &str,
@ -142,19 +112,18 @@ pub async fn modify_ast_for_sketch_wasm(
) -> Result<JsValue, String> { ) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
// deserialize the ast from a stringified json let mut program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let mut program: Node<Program> = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
let plane: kcl_lib::executor::PlaneType = serde_json::from_str(plane_type).map_err(|e| e.to_string())?; let plane: kcl_lib::exec::PlaneType = serde_json::from_str(plane_type).map_err(|e| e.to_string())?;
let engine: Arc<Box<dyn EngineManager>> = Arc::new(Box::new( let engine: Arc<Box<dyn EngineManager>> = Arc::new(Box::new(
kcl_lib::engine::conn_wasm::EngineConnection::new(manager) kcl_lib::wasm_engine::EngineConnection::new(manager)
.await .await
.map_err(|e| format!("{:?}", e))?, .map_err(|e| format!("{:?}", e))?,
)); ));
let module_id = ModuleId::default(); let module_id = ModuleId::default();
let _ = kcl_lib::ast::modify::modify_ast_for_sketch( let _ = kcl_lib::modify_ast_for_sketch(
&engine, &engine,
&mut program, &mut program,
module_id, module_id,
@ -196,7 +165,7 @@ pub fn lexer_wasm(js: &str) -> Result<JsValue, JsError> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let module_id = ModuleId::default(); let module_id = ModuleId::default();
let tokens = kcl_lib::token::lexer(js, module_id).map_err(JsError::from)?; let tokens = kcl_lib::lexer(js, module_id).map_err(JsError::from)?;
Ok(JsValue::from_serde(&tokens)?) Ok(JsValue::from_serde(&tokens)?)
} }
@ -204,10 +173,7 @@ pub fn lexer_wasm(js: &str) -> Result<JsValue, JsError> {
pub fn parse_wasm(js: &str) -> Result<JsValue, String> { pub fn parse_wasm(js: &str) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let module_id = ModuleId::default(); let program = Program::parse(js).map_err(String::from)?;
let tokens = kcl_lib::token::lexer(js, module_id).map_err(String::from)?;
let parser = kcl_lib::parser::Parser::new(tokens);
let program = parser.ast().map_err(String::from)?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
JsValue::from_serde(&program).map_err(|e| e.to_string()) JsValue::from_serde(&program).map_err(|e| e.to_string())
@ -219,19 +185,15 @@ pub fn parse_wasm(js: &str) -> Result<JsValue, String> {
pub fn recast_wasm(json_str: &str) -> Result<JsValue, JsError> { pub fn recast_wasm(json_str: &str) -> Result<JsValue, JsError> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
// deserialize the ast from a stringified json let program: Program = serde_json::from_str(json_str).map_err(JsError::from)?;
let program: kcl_lib::ast::types::Program = serde_json::from_str(json_str).map_err(JsError::from)?; Ok(JsValue::from_serde(&program.recast())?)
// Use the default options until we integrate into the UI the ability to change them.
let result = program.recast(&Default::default(), 0);
Ok(JsValue::from_serde(&result)?)
} }
#[wasm_bindgen] #[wasm_bindgen]
pub struct ServerConfig { pub struct ServerConfig {
into_server: js_sys::AsyncIterator, into_server: js_sys::AsyncIterator,
from_server: web_sys::WritableStream, from_server: web_sys::WritableStream,
fs: kcl_lib::fs::wasm::FileSystemManager, fs: kcl_lib::wasm_engine::FileSystemManager,
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -240,7 +202,7 @@ impl ServerConfig {
pub fn new( pub fn new(
into_server: js_sys::AsyncIterator, into_server: js_sys::AsyncIterator,
from_server: web_sys::WritableStream, from_server: web_sys::WritableStream,
fs: kcl_lib::fs::wasm::FileSystemManager, fs: kcl_lib::wasm_engine::FileSystemManager,
) -> Self { ) -> Self {
Self { Self {
into_server, into_server,
@ -260,7 +222,7 @@ impl ServerConfig {
#[wasm_bindgen] #[wasm_bindgen]
pub async fn kcl_lsp_run( pub async fn kcl_lsp_run(
config: ServerConfig, config: ServerConfig,
engine_manager: Option<kcl_lib::engine::conn_wasm::EngineCommandManager>, engine_manager: Option<kcl_lib::wasm_engine::EngineCommandManager>,
units: &str, units: &str,
token: String, token: String,
baseurl: String, baseurl: String,
@ -273,74 +235,37 @@ pub async fn kcl_lsp_run(
fs, fs,
} = config; } = config;
let stdlib = kcl_lib::std::StdLib::new();
let stdlib_completions = kcl_lib::lsp::kcl::get_completions_from_stdlib(&stdlib).map_err(|e| e.to_string())?;
let stdlib_signatures = kcl_lib::lsp::kcl::get_signatures_from_stdlib(&stdlib).map_err(|e| e.to_string())?;
let mut zoo_client = kittycad::Client::new(token);
zoo_client.set_base_url(baseurl.as_str());
let file_manager = Arc::new(kcl_lib::fs::FileManager::new(fs));
let executor_ctx = if let Some(engine_manager) = engine_manager { let executor_ctx = if let Some(engine_manager) = engine_manager {
let units = kcl_lib::settings::types::UnitLength::from_str(units).map_err(|e| e.to_string())?; let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
let engine = kcl_lib::engine::conn_wasm::EngineConnection::new(engine_manager) Some(kcl_lib::ExecutorContext::new(engine_manager, fs.clone(), units).await?)
.await
.map_err(|e| format!("{:?}", e))?;
Some(kcl_lib::executor::ExecutorContext {
engine: Arc::new(Box::new(engine)),
fs: file_manager.clone(),
stdlib: std::sync::Arc::new(stdlib),
settings: ExecutorSettings {
units,
..Default::default()
},
context_type: kcl_lib::executor::ContextType::Live,
})
} else { } else {
None None
}; };
let mut zoo_client = kittycad::Client::new(token);
zoo_client.set_base_url(baseurl.as_str());
// Check if we can send telememtry for this user. // Check if we can send telememtry for this user.
let privacy_settings = match zoo_client.users().get_privacy_settings().await { let can_send_telemetry = match zoo_client.users().get_privacy_settings().await {
Ok(privacy_settings) => privacy_settings, Ok(privacy_settings) => privacy_settings.can_train_on_data,
Err(err) => { Err(err) => {
// In the case of dev we don't always have a sub set, but prod we should. // In the case of dev we don't always have a sub set, but prod we should.
if err if err
.to_string() .to_string()
.contains("The modeling app subscription type is missing.") .contains("The modeling app subscription type is missing.")
{ {
kittycad::types::PrivacySettings { true
can_train_on_data: true,
}
} else { } else {
return Err(err.to_string().into()); return Err(err.to_string().into());
} }
} }
}; };
let (service, socket) = LspService::build(|client| kcl_lib::lsp::kcl::Backend { let (service, socket) = LspService::build(|client| {
client, kcl_lib::KclLspBackend::new_wasm(client, executor_ctx, fs, zoo_client, can_send_telemetry).unwrap()
fs: file_manager,
workspace_folders: Default::default(),
stdlib_completions,
stdlib_signatures,
token_map: Default::default(),
ast_map: Default::default(),
memory_map: Default::default(),
code_map: Default::default(),
diagnostics_map: Default::default(),
symbols_map: Default::default(),
semantic_tokens_map: Default::default(),
zoo_client,
can_send_telemetry: privacy_settings.can_train_on_data,
can_execute: Arc::new(tokio::sync::RwLock::new(executor_ctx.is_some())),
executor_ctx: Arc::new(tokio::sync::RwLock::new(executor_ctx)),
is_initialized: Default::default(),
}) })
.custom_method("kcl/updateUnits", kcl_lib::lsp::kcl::Backend::update_units) .custom_method("kcl/updateUnits", kcl_lib::KclLspBackend::update_units)
.custom_method("kcl/updateCanExecute", kcl_lib::lsp::kcl::Backend::update_can_execute) .custom_method("kcl/updateCanExecute", kcl_lib::KclLspBackend::update_can_execute)
.finish(); .finish();
let input = wasm_bindgen_futures::stream::JsStream::from(into_server); let input = wasm_bindgen_futures::stream::JsStream::from(into_server);
@ -383,39 +308,21 @@ pub async fn copilot_lsp_run(config: ServerConfig, token: String, baseurl: Strin
let mut zoo_client = kittycad::Client::new(token); let mut zoo_client = kittycad::Client::new(token);
zoo_client.set_base_url(baseurl.as_str()); zoo_client.set_base_url(baseurl.as_str());
let file_manager = Arc::new(kcl_lib::fs::FileManager::new(fs)); let dev_mode = if baseurl == "https://api.dev.zoo.dev" {
let (service, socket) = LspService::build(|client| kcl_lib::lsp::copilot::Backend {
client,
fs: file_manager,
workspace_folders: Default::default(),
code_map: Default::default(),
editor_info: Arc::new(RwLock::new(kcl_lib::lsp::copilot::types::CopilotEditorInfo::default())),
cache: Arc::new(kcl_lib::lsp::copilot::cache::CopilotCache::new()),
telemetry: Default::default(),
zoo_client,
is_initialized: Default::default(),
diagnostics_map: Default::default(),
dev_mode: if baseurl == "https://api.dev.zoo.dev" {
true true
} else { } else {
false false
}, };
})
.custom_method("copilot/setEditorInfo", kcl_lib::lsp::copilot::Backend::set_editor_info) let (service, socket) =
LspService::build(|client| kcl_lib::CopilotLspBackend::new_wasm(client, fs, zoo_client, dev_mode))
.custom_method("copilot/setEditorInfo", kcl_lib::CopilotLspBackend::set_editor_info)
.custom_method( .custom_method(
"copilot/getCompletions", "copilot/getCompletions",
kcl_lib::lsp::copilot::Backend::get_completions_cycling, kcl_lib::CopilotLspBackend::get_completions_cycling,
)
.custom_method(
"copilot/notifyAccepted",
kcl_lib::lsp::copilot::Backend::accept_completion,
)
.custom_method(
"copilot/notifyRejected",
kcl_lib::lsp::copilot::Backend::reject_completions,
) )
.custom_method("copilot/notifyAccepted", kcl_lib::CopilotLspBackend::accept_completion)
.custom_method("copilot/notifyRejected", kcl_lib::CopilotLspBackend::reject_completions)
.finish(); .finish();
let input = wasm_bindgen_futures::stream::JsStream::from(into_server); let input = wasm_bindgen_futures::stream::JsStream::from(into_server);
@ -442,7 +349,7 @@ pub async fn copilot_lsp_run(config: ServerConfig, token: String, baseurl: Strin
pub fn is_points_ccw(points: &[f64]) -> i32 { pub fn is_points_ccw(points: &[f64]) -> i32 {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
kcl_lib::std::utils::is_points_ccw_wasm(points) kcl_lib::std_utils::is_points_ccw_wasm(points)
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -479,7 +386,7 @@ pub fn get_tangential_arc_to_info(
) -> TangentialArcInfoOutputWasm { ) -> TangentialArcInfoOutputWasm {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let result = kcl_lib::std::utils::get_tangential_arc_to_info(kcl_lib::std::utils::TangentialArcInfoInput { let result = kcl_lib::std_utils::get_tangential_arc_to_info(kcl_lib::std_utils::TangentialArcInfoInput {
arc_start_point: [arc_start_point_x, arc_start_point_y], arc_start_point: [arc_start_point_x, arc_start_point_y],
arc_end_point: [arc_end_point_x, arc_end_point_y], arc_end_point: [arc_end_point_x, arc_end_point_y],
tan_previous_point: [tan_previous_point_x, tan_previous_point_y], tan_previous_point: [tan_previous_point_x, tan_previous_point_y],
@ -503,7 +410,7 @@ pub fn get_tangential_arc_to_info(
pub fn program_memory_init() -> Result<JsValue, String> { pub fn program_memory_init() -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let memory = kcl_lib::executor::ProgramMemory::default(); let memory = kcl_lib::exec::ProgramMemory::default();
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
@ -512,10 +419,10 @@ pub fn program_memory_init() -> Result<JsValue, String> {
/// Get a coredump. /// Get a coredump.
#[wasm_bindgen] #[wasm_bindgen]
pub async fn coredump(core_dump_manager: kcl_lib::coredump::wasm::CoreDumpManager) -> Result<JsValue, String> { pub async fn coredump(core_dump_manager: kcl_lib::wasm_engine::CoreDumpManager) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let core_dumper = kcl_lib::coredump::wasm::CoreDumper::new(core_dump_manager); let core_dumper = kcl_lib::wasm_engine::CoreDumper::new(core_dump_manager);
let dump = core_dumper.dump().await.map_err(|e| e.to_string())?; let dump = core_dumper.dump().await.map_err(|e| e.to_string())?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
@ -528,7 +435,7 @@ pub async fn coredump(core_dump_manager: kcl_lib::coredump::wasm::CoreDumpManage
pub fn default_app_settings() -> Result<JsValue, String> { pub fn default_app_settings() -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let settings = kcl_lib::settings::types::Configuration::default(); let settings = kcl_lib::Configuration::default();
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
@ -540,8 +447,7 @@ pub fn default_app_settings() -> Result<JsValue, String> {
pub fn parse_app_settings(toml_str: &str) -> Result<JsValue, String> { pub fn parse_app_settings(toml_str: &str) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let settings = kcl_lib::settings::types::Configuration::backwards_compatible_toml_parse(&toml_str) let settings = kcl_lib::Configuration::backwards_compatible_toml_parse(&toml_str).map_err(|e| e.to_string())?;
.map_err(|e| e.to_string())?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
@ -553,7 +459,7 @@ pub fn parse_app_settings(toml_str: &str) -> Result<JsValue, String> {
pub fn default_project_settings() -> Result<JsValue, String> { pub fn default_project_settings() -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let settings = kcl_lib::settings::types::project::ProjectConfiguration::default(); let settings = kcl_lib::ProjectConfiguration::default();
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
@ -565,8 +471,8 @@ pub fn default_project_settings() -> Result<JsValue, String> {
pub fn parse_project_settings(toml_str: &str) -> Result<JsValue, String> { pub fn parse_project_settings(toml_str: &str) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let settings = kcl_lib::settings::types::project::ProjectConfiguration::backwards_compatible_toml_parse(&toml_str) let settings =
.map_err(|e| e.to_string())?; kcl_lib::ProjectConfiguration::backwards_compatible_toml_parse(&toml_str).map_err(|e| e.to_string())?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
@ -578,7 +484,7 @@ pub fn parse_project_settings(toml_str: &str) -> Result<JsValue, String> {
pub fn serialize_project_settings(val: JsValue) -> Result<JsValue, String> { pub fn serialize_project_settings(val: JsValue) -> Result<JsValue, String> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
let config: kcl_lib::settings::types::Configuration = val.into_serde().map_err(|e| e.to_string())?; let config: kcl_lib::Configuration = val.into_serde().map_err(|e| e.to_string())?;
let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?; let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?;

View File

@ -1,6 +1,6 @@
use kcl_lib::{ use kcl_lib::{
settings::types::UnitLength,
test_server::{execute_and_snapshot, execute_and_snapshot_no_auth}, test_server::{execute_and_snapshot, execute_and_snapshot_no_auth},
UnitLength,
}; };
/// The minimum permissible difference between asserted twenty-twenty images. /// The minimum permissible difference between asserted twenty-twenty images.

View File

@ -1,20 +1,17 @@
use anyhow::Result; use anyhow::Result;
use kcl_lib::{ use kcl_lib::{
ast::{ exec::{KclValue, PlaneType},
modify::modify_ast_for_sketch, modify_ast_for_sketch, ExecState, ExecutorContext, ModuleId, Program, SourceRange,
types::{ModuleId, Node, Program},
},
executor::{ExecutorContext, IdGenerator, KclValue, PlaneType, SourceRange},
}; };
use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit, shared::Point3d, ModelingCmd}; use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit, shared::Point3d, ModelingCmd};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
/// Setup the engine and parse code for an ast. /// Setup the engine and parse code for an ast.
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node<Program>, ModuleId, uuid::Uuid)> { async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, ModuleId, uuid::Uuid)> {
let module_id = ModuleId::default(); let program = Program::parse(code)?;
let program = kcl_lib::parser::parse(code, module_id)?; let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
let ctx = kcl_lib::executor::ExecutorContext::new_with_default_client(Default::default()).await?; let mut exec_state = ExecState::default();
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?; ctx.run(&program, &mut exec_state).await?;
// We need to get the sketch ID. // We need to get the sketch ID.
// Get the sketch ID from memory. // Get the sketch ID from memory.
@ -56,7 +53,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node<Program>
) )
.await?; .await?;
Ok((ctx, program, module_id, sketch_id)) Ok((ctx, program, ModuleId::default(), sketch_id))
} }
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]