feat(engine): establish calcpad-engine workspace with Epic 1 modules

Cherry-picked and integrated the best code from 105 parallel epic
branches into a clean workspace structure:

- calcpad-engine/: Core Rust crate with lexer, parser, AST,
  interpreter, types, FFI (C ABI), pipeline, error handling,
  span tracking, eval context (from epic/1-5 base)
- calcpad-engine/src/number.rs: Arbitrary precision arithmetic
  via dashu crate, WASM-compatible, exact decimals (from epic/1-4)
- calcpad-engine/src/sheet_context.rs: SheetContext with dependency
  graph, dirty tracking, circular detection, cheap clone (from epic/1-8)
- calcpad-wasm/: Thin WASM wrapper crate via wasm-bindgen,
  delegates to calcpad-engine (from epic/1-6)
- Updated .gitignore for target/, node_modules/, build artifacts
- Fixed run-pipeline.sh for macOS compat and CalcPad phases

79 tests passing across workspace.
This commit is contained in:
C. Cassel
2026-03-17 07:54:17 -04:00
committed by C. Cassel
parent 922b591d68
commit 6a8fecd03e
26 changed files with 5046 additions and 877 deletions

View File

@@ -0,0 +1,40 @@
use crate::context::EvalContext;
use crate::interpreter::evaluate;
use crate::lexer::tokenize;
use crate::parser::parse;
use crate::span::Span;
use crate::types::CalcResult;
/// Evaluate a single line of input and return the result.
pub fn eval_line(input: &str, ctx: &mut EvalContext) -> CalcResult {
let trimmed = input.trim();
if trimmed.is_empty() {
return CalcResult::error("empty input", Span::new(0, 0));
}
let tokens = tokenize(trimmed);
// Check for text-only or comment-only lines
let has_expr = tokens.iter().any(|t| {
!matches!(
t.kind,
crate::token::TokenKind::Text(_)
| crate::token::TokenKind::Comment(_)
| crate::token::TokenKind::Eof
)
});
if !has_expr {
return CalcResult::error("no expression found", Span::new(0, trimmed.len()));
}
match parse(tokens) {
Ok(expr) => evaluate(&expr, ctx),
Err(e) => CalcResult::error(&e.message, e.span),
}
}
/// Evaluate multiple lines of input, sharing context across lines.
/// Variable assignments on earlier lines are visible to later lines.
pub fn eval_sheet(lines: &[&str], ctx: &mut EvalContext) -> Vec<CalcResult> {
lines.iter().map(|line| eval_line(line, ctx)).collect()
}