Compare commits
2 Commits
2469
...
kcl_prelud
Author | SHA1 | Date | |
---|---|---|---|
ddce447c0b | |||
e8769eb543 |
1
src/wasm-lib/Cargo.lock
generated
1
src/wasm-lib/Cargo.lock
generated
@ -1473,6 +1473,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"image",
|
"image",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
|
"kcl-macros",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
"kittycad-execution-plan",
|
"kittycad-execution-plan",
|
||||||
"kittycad-execution-plan-macros",
|
"kittycad-execution-plan-macros",
|
||||||
|
@ -8,6 +8,7 @@ description = "A new executor for KCL which compiles to Execution Plans"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
image = { version = "0.24.7", default-features = false, features = ["png"] }
|
image = { version = "0.24.7", default-features = false, features = ["png"] }
|
||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
|
kcl-macros = { path = "../kcl-macros/" }
|
||||||
kittycad = { workspace = true }
|
kittycad = { workspace = true }
|
||||||
kittycad-execution-plan = { workspace = true }
|
kittycad-execution-plan = { workspace = true }
|
||||||
kittycad-execution-plan-traits = { workspace = true }
|
kittycad-execution-plan-traits = { workspace = true }
|
||||||
|
@ -11,6 +11,8 @@ use kcl_lib::{
|
|||||||
ast,
|
ast,
|
||||||
ast::types::{BodyItem, FunctionExpressionParts, KclNone, LiteralValue, Program},
|
ast::types::{BodyItem, FunctionExpressionParts, KclNone, LiteralValue, Program},
|
||||||
};
|
};
|
||||||
|
extern crate alloc;
|
||||||
|
use kcl_macros::parse_file;
|
||||||
use kcl_value_group::into_single_value;
|
use kcl_value_group::into_single_value;
|
||||||
use kittycad_execution_plan::{self as ep, Destination, Instruction};
|
use kittycad_execution_plan::{self as ep, Destination, Instruction};
|
||||||
use kittycad_execution_plan_traits as ept;
|
use kittycad_execution_plan_traits as ept;
|
||||||
@ -24,9 +26,11 @@ use self::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Execute a KCL program by compiling into an execution plan, then running that.
|
/// Execute a KCL program by compiling into an execution plan, then running that.
|
||||||
pub async fn execute(ast: Program, session: &mut Option<Session>) -> Result<ep::Memory, Error> {
|
/// Include a `prelude.kcl` inlined as a `Program` struct thanks to a proc_macro `parse!`.
|
||||||
|
/// This makes additional functions available to the user.
|
||||||
|
pub async fn execute(ast_user: Program, session: &mut Option<Session>) -> Result<ep::Memory, Error> {
|
||||||
let mut planner = Planner::new();
|
let mut planner = Planner::new();
|
||||||
let (plan, _retval) = planner.build_plan(ast)?;
|
let (plan, _retval) = planner.build_plan(ast_user)?;
|
||||||
let mut mem = ep::Memory::default();
|
let mut mem = ep::Memory::default();
|
||||||
ep::execute(&mut mem, plan, session).await?;
|
ep::execute(&mut mem, plan, session).await?;
|
||||||
Ok(mem)
|
Ok(mem)
|
||||||
@ -54,7 +58,9 @@ impl Planner {
|
|||||||
|
|
||||||
/// If successful, return the KCEP instructions for executing the given program.
|
/// If successful, return the KCEP instructions for executing the given program.
|
||||||
/// If the program is a function with a return, then it also returns the KCL function's return value.
|
/// If the program is a function with a return, then it also returns the KCL function's return value.
|
||||||
fn build_plan(&mut self, program: Program) -> Result<(Vec<Instruction>, Option<EpBinding>), CompileError> {
|
fn build_plan(&mut self, ast_user: Program) -> Result<(Vec<Instruction>, Option<EpBinding>), CompileError> {
|
||||||
|
let ast_prelude = parse_file!("./prelude.kcl");
|
||||||
|
let program = ast_prelude.merge(ast_user);
|
||||||
program
|
program
|
||||||
.body
|
.body
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -32,23 +32,23 @@ impl<'a> Context<'a> {
|
|||||||
/// Unary operator macro to quickly create new bindings.
|
/// Unary operator macro to quickly create new bindings.
|
||||||
macro_rules! define_unary {
|
macro_rules! define_unary {
|
||||||
() => {};
|
() => {};
|
||||||
($h:ident$( $r:ident)*) => {
|
($fn_name:ident$( $rest:ident)*) => {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
pub struct $h;
|
pub struct $fn_name;
|
||||||
|
|
||||||
impl Callable for $h {
|
impl Callable for $fn_name {
|
||||||
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
return Err(CompileError::TooManyArgs {
|
return Err(CompileError::TooManyArgs {
|
||||||
fn_name: "$h".into(),
|
fn_name: "$fn_name".into(),
|
||||||
maximum: 1,
|
maximum: 1,
|
||||||
actual: args.len(),
|
actual: args.len(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let not_enough_args = CompileError::NotEnoughArgs {
|
let not_enough_args = CompileError::NotEnoughArgs {
|
||||||
fn_name: "$h".into(),
|
fn_name: "$fn_name".into(),
|
||||||
required: 1,
|
required: 1,
|
||||||
actual: args.len(),
|
actual: args.len(),
|
||||||
};
|
};
|
||||||
@ -61,7 +61,7 @@ macro_rules! define_unary {
|
|||||||
let instructions = vec![
|
let instructions = vec![
|
||||||
Instruction::UnaryArithmetic {
|
Instruction::UnaryArithmetic {
|
||||||
arithmetic: UnaryArithmetic {
|
arithmetic: UnaryArithmetic {
|
||||||
operation: UnaryOperation::$h,
|
operation: UnaryOperation::$fn_name,
|
||||||
operand: Operand::Reference(arg0)
|
operand: Operand::Reference(arg0)
|
||||||
},
|
},
|
||||||
destination: Destination::Address(destination)
|
destination: Destination::Address(destination)
|
||||||
@ -75,7 +75,7 @@ macro_rules! define_unary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
define_unary!($($r)*);
|
define_unary!($($rest)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,27 +115,27 @@ impl Callable for Id {
|
|||||||
/// Binary operator macro to quickly create new bindings.
|
/// Binary operator macro to quickly create new bindings.
|
||||||
macro_rules! define_binary {
|
macro_rules! define_binary {
|
||||||
() => {};
|
() => {};
|
||||||
($h:ident$( $r:ident)*) => {
|
($fn_name:ident$( $rest:ident)*) => {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
pub struct $h;
|
pub struct $fn_name;
|
||||||
|
|
||||||
impl Callable for $h {
|
impl Callable for $fn_name {
|
||||||
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||||
let len = args.len();
|
let len = args.len();
|
||||||
if len > 2 {
|
if len > 2 {
|
||||||
return Err(CompileError::TooManyArgs {
|
return Err(CompileError::TooManyArgs {
|
||||||
fn_name: "$h".into(),
|
fn_name: "$fn_name".into(),
|
||||||
maximum: 2,
|
maximum: 2,
|
||||||
actual: len,
|
actual: len,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let not_enough_args = CompileError::NotEnoughArgs {
|
let not_enough_args = CompileError::NotEnoughArgs {
|
||||||
fn_name: "$h".into(),
|
fn_name: "$fn_name".into(),
|
||||||
required: 2,
|
required: 2,
|
||||||
actual: len,
|
actual: len,
|
||||||
};
|
};
|
||||||
const ERR: &str = "cannot use composite values (e.g. array) as arguments to $h";
|
const ERR: &str = "cannot use composite values (e.g. array) as arguments to $fn_name";
|
||||||
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
|
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
|
||||||
return Err(CompileError::InvalidOperand(ERR));
|
return Err(CompileError::InvalidOperand(ERR));
|
||||||
};
|
};
|
||||||
@ -146,7 +146,7 @@ macro_rules! define_binary {
|
|||||||
Ok(EvalPlan {
|
Ok(EvalPlan {
|
||||||
instructions: vec![Instruction::BinaryArithmetic {
|
instructions: vec![Instruction::BinaryArithmetic {
|
||||||
arithmetic: BinaryArithmetic {
|
arithmetic: BinaryArithmetic {
|
||||||
operation: BinaryOperation::$h,
|
operation: BinaryOperation::$fn_name,
|
||||||
operand0: Operand::Reference(arg0),
|
operand0: Operand::Reference(arg0),
|
||||||
operand1: Operand::Reference(arg1),
|
operand1: Operand::Reference(arg1),
|
||||||
},
|
},
|
||||||
@ -157,7 +157,7 @@ macro_rules! define_binary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
define_binary!($($r)*);
|
define_binary!($($rest)*);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1442,3 +1442,23 @@ async fn cos_sin_pi() {
|
|||||||
// Constants don't live in memory.
|
// Constants don't live in memory.
|
||||||
assert_eq!(*z, constants::PI);
|
assert_eq!(*z, constants::PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn kcl_prelude() {
|
||||||
|
let program = "
|
||||||
|
let the_answer_to_the_universe_is = hey_from_one_of_the_devs_of_the_past_i_wish_you_a_great_day_and_maybe_gave_you_a_little_smile_as_youve_found_this()
|
||||||
|
";
|
||||||
|
let (_plan, scope, _) = must_plan(program);
|
||||||
|
let Some(EpBinding::Single(the_answer_to_the_universe_is)) = scope.get("the_answer_to_the_universe_is") else {
|
||||||
|
panic!(
|
||||||
|
"Unexpected binding for variable 'the_answer_to_the_universe_is': {:?}",
|
||||||
|
scope.get("the_answer_to_the_universe_is")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||||
|
.ast()
|
||||||
|
.unwrap();
|
||||||
|
let mem = crate::execute(ast, &mut None).await.unwrap();
|
||||||
|
use ept::ReadMemory;
|
||||||
|
assert_eq!(*mem.get(the_answer_to_the_universe_is).unwrap(), Primitive::from(42i64));
|
||||||
|
}
|
||||||
|
@ -8,7 +8,6 @@ use syn::{parse_macro_input, LitStr};
|
|||||||
/// This macro takes exactly one argument: A string literal containing KCL.
|
/// This macro takes exactly one argument: A string literal containing KCL.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// extern crate alloc;
|
|
||||||
/// use kcl_compile_macro::parse_kcl;
|
/// use kcl_compile_macro::parse_kcl;
|
||||||
/// let ast: kcl_lib::ast::types::Program = parse_kcl!("const y = 4");
|
/// let ast: kcl_lib::ast::types::Program = parse_kcl!("const y = 4");
|
||||||
/// ```
|
/// ```
|
||||||
@ -21,3 +20,14 @@ pub fn parse(input: TokenStream) -> TokenStream {
|
|||||||
let ast_struct = ast.bake(&Default::default());
|
let ast_struct = ast.bake(&Default::default());
|
||||||
quote!(#ast_struct).into()
|
quote!(#ast_struct).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as `parse!` but read in a file.
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn parse_file(input: TokenStream) -> TokenStream {
|
||||||
|
let file_name = parse_macro_input!(input as LitStr);
|
||||||
|
let kcl_src = std::fs::read_to_string(file_name.value()).unwrap();
|
||||||
|
let tokens = kcl_lib::token::lexer(&kcl_src);
|
||||||
|
let ast = kcl_lib::parser::Parser::new(tokens).ast().unwrap();
|
||||||
|
let ast_struct = ast.bake(&Default::default());
|
||||||
|
quote!(#ast_struct).into()
|
||||||
|
}
|
||||||
|
@ -351,6 +351,31 @@ impl Program {
|
|||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Merge two `Program`s together.
|
||||||
|
pub fn merge(self, ast_other: Program) -> Program {
|
||||||
|
let mut body_new: Vec<BodyItem> = vec![];
|
||||||
|
body_new.extend(self.body);
|
||||||
|
body_new.extend(ast_other.body);
|
||||||
|
|
||||||
|
let mut non_code_nodes_new: HashMap<usize, Vec<NonCodeNode>> = HashMap::new();
|
||||||
|
non_code_nodes_new.extend(self.non_code_meta.non_code_nodes);
|
||||||
|
non_code_nodes_new.extend(ast_other.non_code_meta.non_code_nodes);
|
||||||
|
|
||||||
|
let mut start_new: Vec<NonCodeNode> = vec![];
|
||||||
|
start_new.extend(self.non_code_meta.start);
|
||||||
|
start_new.extend(ast_other.non_code_meta.start);
|
||||||
|
|
||||||
|
Program {
|
||||||
|
start: self.start,
|
||||||
|
end: self.end + (ast_other.end - ast_other.start),
|
||||||
|
body: body_new,
|
||||||
|
non_code_meta: NonCodeMeta {
|
||||||
|
non_code_nodes: non_code_nodes_new,
|
||||||
|
start: start_new,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ValueMeta {
|
pub trait ValueMeta {
|
||||||
|
3
src/wasm-lib/prelude.kcl
Normal file
3
src/wasm-lib/prelude.kcl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn hey_from_one_of_the_devs_of_the_past_i_wish_you_a_great_day_and_maybe_gave_you_a_little_smile_as_youve_found_this = () => {
|
||||||
|
return 42
|
||||||
|
}
|
Reference in New Issue
Block a user