Move the wasm lib, and cleanup rust directory and all references (#5585)
* git mv src/wasm-lib rust Signed-off-by: Jess Frazelle <github@jessfraz.com> * mv wasm-lib to workspace Signed-off-by: Jess Frazelle <github@jessfraz.com> * mv kcl-lib Signed-off-by: Jess Frazelle <github@jessfraz.com> * mv derive docs Signed-off-by: Jess Frazelle <github@jessfraz.com> * resolve file paths Signed-off-by: Jess Frazelle <github@jessfraz.com> * clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> * move more shit Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix more paths Signed-off-by: Jess Frazelle <github@jessfraz.com> * make yarn build:wasm work Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix scripts Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixups Signed-off-by: Jess Frazelle <github@jessfraz.com> * better references Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix cargo ci Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix reference Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix more ci Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * cargo sort Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix script Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> * fmt Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix a dep Signed-off-by: Jess Frazelle <github@jessfraz.com> * sort Signed-off-by: Jess Frazelle <github@jessfraz.com> * remove unused deps Signed-off-by: Jess Frazelle <github@jessfraz.com> * Revert "remove unused deps" This reverts commit fbabdb062e275fd5cbc1476f8480a1afee15d972. * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * deps; Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
187
rust/kcl-lib/src/parsing/math.rs
Normal file
187
rust/kcl-lib/src/parsing/math.rs
Normal file
@ -0,0 +1,187 @@
|
||||
// TODO optimise size of CompilationError
|
||||
#![allow(clippy::result_large_err)]
|
||||
|
||||
use super::CompilationError;
|
||||
use crate::{
|
||||
parsing::ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
/// Parses a list of tokens (in infix order, i.e. as the user typed them)
|
||||
/// into a binary expression tree.
|
||||
pub fn parse(infix_tokens: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, CompilationError> {
|
||||
let rpn = postfix(infix_tokens);
|
||||
evaluate(rpn)
|
||||
}
|
||||
|
||||
/// Parses a list of tokens (in postfix order) into a binary expression tree.
|
||||
fn evaluate(rpn: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, CompilationError> {
|
||||
let source_range = source_range(&rpn);
|
||||
let mut operand_stack: Vec<BinaryPart> = Vec::new();
|
||||
let e = CompilationError::fatal(source_range, "error parsing binary math expressions");
|
||||
for item in rpn {
|
||||
let expr = match item {
|
||||
BinaryExpressionToken::Operator(operator) => {
|
||||
let Some(right) = operand_stack.pop() else {
|
||||
return Err(e);
|
||||
};
|
||||
let Some(left) = operand_stack.pop() else {
|
||||
return Err(e);
|
||||
};
|
||||
let start = left.start();
|
||||
let end = right.end();
|
||||
let module_id = left.module_id();
|
||||
|
||||
BinaryPart::BinaryExpression(Node::boxed(
|
||||
BinaryExpression {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
digest: None,
|
||||
},
|
||||
start,
|
||||
end,
|
||||
module_id,
|
||||
))
|
||||
}
|
||||
BinaryExpressionToken::Operand(o) => o,
|
||||
};
|
||||
operand_stack.push(expr)
|
||||
}
|
||||
if let Some(BinaryPart::BinaryExpression(expr)) = operand_stack.pop() {
|
||||
Ok(*expr)
|
||||
} else {
|
||||
// If this branch is used, the evaluation algorithm has a bug and must be fixed.
|
||||
// This is a programmer error, not a user error.
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn source_range(tokens: &[BinaryExpressionToken]) -> SourceRange {
|
||||
let sources: Vec<_> = tokens
|
||||
.iter()
|
||||
.filter_map(|op| match op {
|
||||
BinaryExpressionToken::Operator(_) => None,
|
||||
BinaryExpressionToken::Operand(o) => Some((o.start(), o.end(), o.module_id())),
|
||||
})
|
||||
.collect();
|
||||
match (sources.first(), sources.last()) {
|
||||
(Some((start, _, module_id)), Some((_, end, _))) => SourceRange::new(*start, *end, *module_id),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reorders tokens from infix order to postfix order.
|
||||
fn postfix(infix: Vec<BinaryExpressionToken>) -> Vec<BinaryExpressionToken> {
|
||||
let mut operator_stack: Vec<BinaryOperator> = Vec::with_capacity(infix.len());
|
||||
let mut output = Vec::with_capacity(infix.len());
|
||||
for token in infix {
|
||||
match token {
|
||||
BinaryExpressionToken::Operator(o1) => {
|
||||
// From https://en.wikipedia.org/wiki/Shunting_yard_algorithm:
|
||||
// while (
|
||||
// there is an operator o2 at the top of the operator stack which is not a left parenthesis,
|
||||
// and (o2 has greater precedence than o1 or (o1 and o2 have the same precedence and o1 is left-associative))
|
||||
// )
|
||||
// pop o2 from the operator stack into the output queue
|
||||
while let Some(o2) = operator_stack.pop() {
|
||||
if (o2.precedence() > o1.precedence())
|
||||
|| o1.precedence() == o2.precedence() && o1.associativity().is_left()
|
||||
{
|
||||
output.push(BinaryExpressionToken::Operator(o2));
|
||||
} else {
|
||||
operator_stack.push(o2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
operator_stack.push(o1);
|
||||
}
|
||||
o @ BinaryExpressionToken::Operand(_) => output.push(o),
|
||||
}
|
||||
}
|
||||
// After the while loop, pop the remaining items from the operator stack into the output queue.
|
||||
output.extend(operator_stack.into_iter().rev().map(BinaryExpressionToken::Operator));
|
||||
output
|
||||
}
|
||||
|
||||
/// Expressions are made up of operators and operands.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum BinaryExpressionToken {
|
||||
Operator(BinaryOperator),
|
||||
Operand(BinaryPart),
|
||||
}
|
||||
|
||||
impl From<BinaryPart> for BinaryExpressionToken {
|
||||
fn from(value: BinaryPart) -> Self {
|
||||
Self::Operand(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BinaryOperator> for BinaryExpressionToken {
|
||||
fn from(value: BinaryOperator) -> Self {
|
||||
Self::Operator(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
parsing::{
|
||||
ast::types::{Literal, LiteralValue},
|
||||
token::NumericSuffix,
|
||||
},
|
||||
ModuleId,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn parse_and_evaluate() {
|
||||
/// Make a literal
|
||||
fn lit(n: u8) -> BinaryPart {
|
||||
BinaryPart::Literal(Box::new(Node::new(
|
||||
Literal {
|
||||
value: LiteralValue::Number {
|
||||
value: n as f64,
|
||||
suffix: NumericSuffix::None,
|
||||
},
|
||||
raw: n.to_string(),
|
||||
digest: None,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
ModuleId::default(),
|
||||
)))
|
||||
}
|
||||
let tests: Vec<Vec<BinaryExpressionToken>> = vec![
|
||||
// 3 + 4 × 2 ÷ ( 1 − 5 ) ^ 2 ^ 3
|
||||
vec![
|
||||
lit(3).into(),
|
||||
BinaryOperator::Add.into(),
|
||||
lit(4).into(),
|
||||
BinaryOperator::Mul.into(),
|
||||
lit(2).into(),
|
||||
BinaryOperator::Div.into(),
|
||||
BinaryPart::BinaryExpression(Node::boxed(
|
||||
BinaryExpression {
|
||||
operator: BinaryOperator::Sub,
|
||||
left: lit(1),
|
||||
right: lit(5),
|
||||
digest: None,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
ModuleId::default(),
|
||||
))
|
||||
.into(),
|
||||
BinaryOperator::Pow.into(),
|
||||
lit(2).into(),
|
||||
BinaryOperator::Pow.into(),
|
||||
lit(3).into(),
|
||||
],
|
||||
];
|
||||
for infix_input in tests {
|
||||
let rpn = postfix(infix_input);
|
||||
let _tree = evaluate(rpn).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user