Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@
## 2024-05-26 - Optimize decimal conversion in Value
**Learning:** `rust_decimal::Decimal` allows efficient and direct conversion to basic types like `i64` and `f64` via `to_i64` and `to_f64` using the `rust_decimal::prelude::ToPrimitive` trait. Converting to string and then parsing `val.to_string().parse()` is an anti-pattern as it incurs heap allocation overhead, which is bad for performance. When doing integer conversions from `Decimal`, it's critical to check `val.scale() == 0` first to maintain behavioral parity with string parsing (which would reject floats).
**Action:** Always favor direct conversion traits like `rust_decimal::prelude::ToPrimitive` methods over stringification when converting `Decimal` values to native numeric types. Keep edge cases like `scale()` properties in mind when changing conversion methods.

## 2024-05-27 - Optimize Display implementations for collections
**Learning:** `fmt::Display` implementations involving collections (like Lists or Maps) can be highly inefficient if they build intermediate `String`s via `format!()` and `push_str()` inside loops. This causes unnecessary heap allocations.
**Action:** Always write directly to the `fmt::Formatter` inside a loop using `write!(f, ...)` to minimize heap allocations and improve formatting performance.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 1 addition & 5 deletions src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,7 @@ impl DescriptorManager {

fn get(&self, key: DescriptorKey) -> Option<Descriptor> {
let binding = self.store.lock().unwrap();
let value = binding.get(&key);
if value.is_none() {
return None;
}
Some(value.unwrap().clone())
binding.get(&key).cloned()
}

pub fn set_unary_descriptor(&mut self, op: String, descriptor: Arc<UnaryDescriptor>) {
Expand Down
2 changes: 1 addition & 1 deletion src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl InnerFunctionManager {
pub fn new() -> Self {
static STORE: OnceCell<Mutex<HashMap<String, Arc<InnerFunction>>>> = OnceCell::new();
let store = STORE.get_or_init(|| Mutex::new(HashMap::new()));
InnerFunctionManager { store: store }
InnerFunctionManager { store }
}

pub fn init(&mut self) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub fn execute(expr: &str, mut ctx: context::Context) -> Result<Value> {
/// let ast = parse_expression(input);
/// assert!(ast.is_ok());
/// ```
pub fn parse_expression(expr: &str) -> Result<ExprAST> {
pub fn parse_expression(expr: &str) -> Result<ExprAST<'_>> {
init();
parser::Parser::new(expr)?.parse_stmt()
}
Expand Down
8 changes: 4 additions & 4 deletions src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl InfixOpManager {
pub fn new() -> Self {
static STORE: OnceCell<Mutex<HashMap<String, InfixOpConfig>>> = OnceCell::new();
let store = STORE.get_or_init(|| Mutex::new(HashMap::new()));
InfixOpManager { store: store }
InfixOpManager { store }
}

pub fn init(&mut self) {
Expand Down Expand Up @@ -309,7 +309,7 @@ impl InfixOpManager {
let mut ans = vec![];
let binding = self.store.lock().unwrap();
for (op, InfixOpConfig(precedence, _, _, _)) in binding.iter() {
ans.push((op.clone(), precedence.clone()));
ans.push((op.clone(), *precedence));
}
ans.sort_by(|a, b| a.1.cmp(&b.1));
ans
Expand All @@ -325,7 +325,7 @@ impl PrefixOpManager {
pub fn new() -> Self {
static STORE: OnceCell<Mutex<HashMap<String, Arc<PrefixOpFunc>>>> = OnceCell::new();
let store = STORE.get_or_init(|| Mutex::new(HashMap::new()));
PrefixOpManager { store: store }
PrefixOpManager { store }
}

pub fn init(&mut self) {
Expand Down Expand Up @@ -423,7 +423,7 @@ impl PostfixOpManager {
pub fn new() -> Self {
static STORE: OnceCell<Mutex<HashMap<String, Arc<PrefixOpFunc>>>> = OnceCell::new();
let store = STORE.get_or_init(|| Mutex::new(HashMap::new()));
Self { store: store }
Self { store }
}

pub fn init(&mut self) {
Expand Down
54 changes: 26 additions & 28 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl<'a> ExprAST<'a> {
}

fn exec_unary(&self, op: &'a str, rhs: &ExprAST, ctx: &mut Context) -> Result<Value> {
PrefixOpManager::new().get(&op)?(rhs.exec(ctx)?)
PrefixOpManager::new().get(op)?(rhs.exec(ctx)?)
}

fn exec_binary(
Expand All @@ -158,15 +158,15 @@ impl<'a> ExprAST<'a> {
rhs: &ExprAST<'a>,
ctx: &mut Context,
) -> Result<Value> {
match InfixOpManager::new().get_op_type(&op)? {
match InfixOpManager::new().get_op_type(op)? {
InfixOpType::CALC => {
InfixOpManager::new().get_handler(&op)?(lhs.exec(ctx)?, rhs.exec(ctx)?)
InfixOpManager::new().get_handler(op)?(lhs.exec(ctx)?, rhs.exec(ctx)?)
}
InfixOpType::SETTER => {
let (a, b) = (lhs.exec(ctx)?, rhs.exec(ctx)?);
ctx.set_variable(
lhs.get_reference_name()?,
InfixOpManager::new().get_handler(&op)?(a, b)?,
InfixOpManager::new().get_handler(op)?(a, b)?,
);
Ok(Value::None)
}
Expand Down Expand Up @@ -262,7 +262,7 @@ impl<'a> ExprAST<'a> {
"false".into()
}
}
String(value) => "\"".to_string() + &value + "\"",
String(value) => "\"".to_string() + value + "\"",
}
}

Expand Down Expand Up @@ -292,15 +292,15 @@ impl<'a> ExprAST<'a> {
let (is, precidence) = lhs.get_precidence();
let mut tmp: String = lhs.expr();
if is && precidence < InfixOpManager::new().get_precidence(op) {
tmp = "(".to_string() + &lhs.expr() + &")".to_string();
tmp = "(".to_string() + &lhs.expr() + ")";
}
tmp
};
let right = {
let (is, precidence) = rhs.get_precidence();
let mut tmp = rhs.expr();
if is && precidence < InfixOpManager::new().get_precidence(op) {
tmp = "(".to_string() + &rhs.expr() + &")".to_string();
tmp = "(".to_string() + &rhs.expr() + ")";
}
tmp
};
Expand All @@ -318,36 +318,36 @@ impl<'a> ExprAST<'a> {
fn list_expr(&self, params: Vec<ExprAST>) -> String {
let mut s = String::from("[");
for i in 0..params.len() {
s.push_str(params[i].expr().as_str());
s.push_str(&params[i].expr());
if i < params.len() - 1 {
s.push_str(",");
s.push(',');
}
}
s.push_str("]");
s.push(']');
s
}

fn map_expr(&self, m: Vec<(ExprAST, ExprAST)>) -> String {
let mut s = String::from("{");
for i in 0..m.len() {
let (key, value) = m[i].clone();
s.push_str(key.expr().as_str());
s.push_str(":");
s.push_str(value.expr().as_str());
let (key, value) = &m[i];
s.push_str(&key.expr());
s.push(':');
s.push_str(&value.expr());
if i < m.len() - 1 {
s.push_str(",");
s.push(',');
}
}
s.push_str("}");
s.push('}');
s
}

fn chain_expr(&self, exprs: Vec<ExprAST>) -> String {
let mut s = String::new();
for i in 0..exprs.len() {
s.push_str(exprs[i].expr().as_str());
s.push_str(&exprs[i].expr());
if i < exprs.len() - 1 {
s.push_str(";");
s.push(';');
}
}
s
Expand All @@ -373,25 +373,25 @@ impl<'a> ExprAST<'a> {
op.clone(),
),
Self::List(values) => DescriptorManager::new().get_list_descriptor()(
values.into_iter().map(|v| v.describe()).collect(),
values.iter().map(|v| v.describe()).collect(),
),
Self::Map(values) => DescriptorManager::new().get_map_descriptor()(
values
.into_iter()
.iter()
.map(|value| (value.0.describe(), value.1.describe()))
.collect(),
),
Self::Function(name, values) => DescriptorManager::new()
.get_function_descriptor(name.to_string())(
name.to_string(),
values.into_iter().map(|v| v.describe()).collect(),
values.iter().map(|v| v.describe()).collect(),
),
Self::Reference(name) => DescriptorManager::new()
.get_reference_descriptor(name.to_string())(
name.to_string()
),
Self::Stmt(values) => DescriptorManager::new().get_chain_descriptor()(
values.into_iter().map(|v| v.describe()).collect(),
values.iter().map(|v| v.describe()).collect(),
),
Self::Ternary(condition, lhs, rhs) => {
DescriptorManager::new().get_ternary_descriptor()(
Expand All @@ -410,23 +410,21 @@ pub struct Parser<'a> {
}

impl<'a> Parser<'a> {
fn cur_tok(&self) -> Token {
self.tokenizer.cur_token.clone()
fn cur_tok(&self) -> Token<'_> {
self.tokenizer.cur_token
}

pub fn new(input: &'a str) -> Result<Self> {
let mut tokenizer = Tokenizer::new(input);
tokenizer.next()?;
Ok(Self {
tokenizer: tokenizer,
})
Ok(Self { tokenizer })
}

fn is_eof(&self) -> bool {
self.cur_tok().is_eof()
}

pub fn next(&mut self) -> Result<Token> {
pub fn next(&mut self) -> Result<Token<'_>> {
self.tokenizer.next()
}

Expand Down
28 changes: 14 additions & 14 deletions src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ pub struct Tokenizer<'a> {
}

impl<'a> Tokenizer<'a> {
pub fn new(input: &str) -> Tokenizer {
pub fn new(input: &str) -> Tokenizer<'_> {
Tokenizer {
input: input,
input,
chars: input.char_indices(),
cur_char: ' ',
cur_token: Token::EOF,
Expand Down Expand Up @@ -83,7 +83,7 @@ impl<'a> Tokenizer<'a> {
} else if atom == "False" || atom == "false" {
return self.bool_token(start, false);
}
return self.function_or_reference_token(atom, start);
self.function_or_reference_token(atom, start)
}

fn try_parse_op(&self, start: usize) -> bool {
Expand Down Expand Up @@ -114,10 +114,10 @@ impl<'a> Tokenizer<'a> {
None => break,
}
}
return Ok(Token::Operator(
Ok(Token::Operator(
self.input[start..self.current()].into(),
Span(start, self.current()),
));
))
}

fn parse_var(&mut self, start: usize) -> (&'a str, usize) {
Expand All @@ -136,12 +136,12 @@ impl<'a> Tokenizer<'a> {
(self.input[start..self.current()].into(), start)
}

pub fn peek(&self) -> Result<Token> {
pub fn peek(&self) -> Result<Token<'_>> {
self.clone().next()
}

pub fn expect(&mut self, op: &str) -> Result<()> {
let token = self.cur_token.clone();
let token = self.cur_token;
self.next()?;
match token {
Token::Delim(bracket, _) => {
Expand Down Expand Up @@ -268,23 +268,23 @@ impl<'a> Tokenizer<'a> {
}

fn is_digit_char(ch: char) -> bool {
return '0' <= ch && ch <= '9' || ch == '.' || ch == '-' || ch == 'e' || ch == 'E' || ch == '+';
ch.is_ascii_digit() || ch == '.' || ch == '-' || ch == 'e' || ch == 'E' || ch == '+'
}

fn is_whitespace_char(ch: char) -> bool {
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'
}

fn is_delim_char(ch: char) -> bool {
return ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '{' || ch == '}';
ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '{' || ch == '}'
}

fn is_param_char(ch: char) -> bool {
return ('0' <= ch && ch <= '9')
|| ('a' <= ch && ch <= 'z')
|| ('A' <= ch && ch <= 'Z')
ch.is_ascii_digit()
|| ch.is_ascii_lowercase()
|| ch.is_ascii_uppercase()
|| ch == '.'
|| ch == '_';
|| ch == '_'
}

#[cfg(test)]
Expand Down