Skip to content
Merged
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
55 changes: 51 additions & 4 deletions crates/goth-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,13 @@ fn main() {

if let Some(expr) = args.eval {
// Evaluate expression from command line
run_expr(&expr, args.trace, args.parse_only, args.ast, args.check);
// Collect extra positional args as expression arguments
let mut expr_args: Vec<String> = Vec::new();
if let Some(ref file) = args.file {
expr_args.push(file.to_string_lossy().to_string());
}
expr_args.extend(args.program_args.iter().cloned());
run_expr(&expr, args.trace, args.parse_only, args.ast, args.check, &expr_args);
return;
} else if let Some(file) = args.file {
// Run file
Expand All @@ -141,8 +147,9 @@ fn main() {
}
}

fn run_expr(source: &str, trace: bool, parse_only: bool, show_ast: bool, check: bool) {
fn run_expr(source: &str, trace: bool, parse_only: bool, show_ast: bool, check: bool, program_args: &[String]) {
use colored::Colorize;
use goth_ast::expr::Expr;

// Parse
let parsed = match parse_expr(source) {
Expand All @@ -162,10 +169,29 @@ fn run_expr(source: &str, trace: bool, parse_only: bool, show_ast: bool, check:
// Resolve
let resolved = resolve_expr(parsed);

// Wrap expression as a lambda applied to CLI args (if any)
let final_expr = if program_args.is_empty() {
resolved
} else {
// Build: ((λ→ ... (λ→ body)) arg0 arg1 ...)
// Wrap the expression in N nested lambdas, then apply args
let arity = program_args.len();
let mut lambda_expr = resolved;
for _ in 0..arity {
lambda_expr = Expr::Lam(Box::new(lambda_expr));
}
// Apply each argument
for arg in program_args {
let arg_expr = parse_arg_to_expr(arg);
lambda_expr = Expr::app(lambda_expr, arg_expr);
}
lambda_expr
};

// Type check if --check flag is set
if check {
let mut type_checker = TypeChecker::new();
match type_checker.infer(&resolved) {
match type_checker.infer(&final_expr) {
Ok(ty) => {
println!("{}: {}", "Type".cyan(), ty);
}
Expand All @@ -187,7 +213,7 @@ fn run_expr(source: &str, trace: bool, parse_only: bool, show_ast: bool, check:
Evaluator::new()
};

match evaluator.eval(&resolved) {
match evaluator.eval(&final_expr) {
Ok(value) => {
println!("{}", value);
}
Expand Down Expand Up @@ -354,11 +380,32 @@ fn run_module_with_main(module: &goth_ast::decl::Module, trace: bool, program_ar
}
}

/// Try to parse a CLI argument as an uncertain value (e.g. "1.0±0.05" or "1.0+-0.05")
fn try_parse_uncertain(arg: &str) -> Option<goth_ast::expr::Expr> {
use goth_ast::expr::Expr;
use goth_ast::literal::Literal;

let (left, right) = arg.split_once('±')
.or_else(|| arg.split_once("+-"))?;
let val = left.parse::<f64>().ok()?;
let unc = right.parse::<f64>().ok()?;
Some(Expr::BinOp(
goth_ast::op::BinOp::PlusMinus,
Box::new(Expr::Lit(Literal::Float(val))),
Box::new(Expr::Lit(Literal::Float(unc))),
))
}

/// Parse a CLI argument string into a Goth expression
fn parse_arg_to_expr(arg: &str) -> goth_ast::expr::Expr {
use goth_ast::expr::Expr;
use goth_ast::literal::Literal;

// Try to parse as uncertain value (e.g. "1.0±0.05" or "1.0+-0.05")
if let Some(expr) = try_parse_uncertain(arg) {
return expr;
}

// Try to parse as integer first
if let Ok(n) = arg.parse::<i128>() {
return Expr::Lit(Literal::Int(n));
Expand Down
6 changes: 6 additions & 0 deletions crates/goth-eval/src/prim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,12 @@ fn compare_lt(left: Value, right: Value) -> EvalResult<Value> {
(Value::Int(a), Value::Float(b)) => Ok(Value::Bool((*a as f64) < b.0)),
(Value::Float(a), Value::Int(b)) => Ok(Value::Bool(a.0 < *b as f64)),
(Value::Char(a), Value::Char(b)) => Ok(Value::Bool(a < b)),
(Value::Uncertain { value: a, .. }, Value::Uncertain { value: b, .. }) =>
compare_lt(*a.clone(), *b.clone()),
(Value::Uncertain { value: a, .. }, _) =>
compare_lt(*a.clone(), right.clone()),
(_, Value::Uncertain { value: b, .. }) =>
compare_lt(left.clone(), *b.clone()),
_ => Err(EvalError::type_error_msg(format!("Cannot compare {} and {}", left.type_name(), right.type_name()))),
}
}
Expand Down
18 changes: 9 additions & 9 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,18 @@ File and stream I/O using the `▷` (write) operator.

## uncertainty/

First-class uncertain values with automatic error propagation.
First-class uncertain values with automatic error propagation. Uncertain values can be passed directly as CLI arguments using the `±` or `+-` notation (e.g. `"10.0±0.5"` or `"10.0+-0.5"`).

| File | Description | Example |
|------|-------------|---------|
| `measure.goth` | Create an uncertain value | `10.0 0.5 → 10 ± 0.5` |
| `add_uncertain.goth` | Addition with error propagation | `10 0.3 20 0.4 → 30 ± 0.5` |
| `mul_uncertain.goth` | Multiplication with relative error | `5 0.1 3 0.2 → 15 ± 1.04...` |
| `sqrt_uncertain.goth` | Square root with derivative propagation | `9 0.3 → 3 ± 0.05` |
| `sin_uncertain.goth` | Sine with derivative propagation | `1.0 0.1 → 0.841... ± 0.054...` |
| `chained_uncertain.goth` | Multi-step propagation: sin(√a + b) | `4 0.1 1 0.05 → ...` |

**Demonstrates:** The `±` operator, automatic propagation through `+`, `-`, `×`, `/`, `√`, `sin`, `cos`, `exp`, `ln`, etc.
| `measure.goth` | Return an uncertain value | `"10.0±0.5" → 10 ± 0.5` |
| `add_uncertain.goth` | Addition with error propagation | `"10.0±0.3" "20.0±0.4" → 30 ± 0.5` |
| `mul_uncertain.goth` | Multiplication with relative error | `"5.0±0.1" "3.0±0.2" → 15 ± 1.04...` |
| `sqrt_uncertain.goth` | Square root with derivative propagation | `"9.0±0.3" → 3 ± 0.05` |
| `sin_uncertain.goth` | Sine with derivative propagation | `"1.0±0.1" → 0.841... ± 0.054...` |
| `chained_uncertain.goth` | Multi-step propagation: sin(√a + b) | `"4.0±0.1" "1.0±0.05" → ...` |

**Demonstrates:** The `±` operator, automatic propagation through `+`, `-`, `×`, `/`, `√`, `sin`, `cos`, `exp`, `ln`, etc. Ordering comparisons (`<`, `>`, `≤`, `≥`) on uncertain values compare central values.

---

Expand Down
8 changes: 4 additions & 4 deletions examples/uncertainty/add_uncertain.goth
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Add two uncertain values with error propagation
# Input: a da b db (value±uncertainty for each)
# Input: two uncertain values (value±uncertainty each)
# Output: (a+b) ± √(da²+db²)
# De Bruijn: ₃ = a, ₂ = da, ₁ = b, ₀ = db
# De Bruijn: ₁ = first, ₀ = second
# Postcondition: propagated uncertainty ≥ max(da, db)

╭─ main : F64 F64 → F64 F64 → (F64 ± F64)
╰─ (₃ ± ₂) + (₁ ± ₀)
╭─ main : (F64 ± F64)(F64 ± F64) → (F64 ± F64)
╰─ ₁ + ₀
10 changes: 5 additions & 5 deletions examples/uncertainty/chained_uncertain.goth
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Chained uncertainty propagation through multiple operations
# Computes sin(√(a ± da) + (b ± db))
# Input: a da b db
# Computes sin(√a + b)
# Input: two uncertain values
# Output: result with fully propagated uncertainty
# De Bruijn: ₃ = a, ₂ = da, ₁ = b, ₀ = db
# De Bruijn: ₁ = first, ₀ = second
# Property: uncertainty propagates through each step automatically

╭─ main : F64 F64 → F64 F64 → (F64 ± F64)
╰─ sin (√(₃ ± ₂) + (₁ ± ₀))
╭─ main : (F64 ± F64)(F64 ± F64) → (F64 ± F64)
╰─ sin (√₁ + ₀)
13 changes: 5 additions & 8 deletions examples/uncertainty/measure.goth
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
# Create an uncertain measurement value
# Input: value uncertainty
# Return an uncertain measurement value
# Input: value±uncertainty
# Output: value ± uncertainty
# De Bruijn: ₁ = value, ₀ = uncertainty
# De Bruijn: ₀ = uncertain value

╭─ measure : F64 → F64 → (F64 ± F64)
╰─ ₁ ± ₀

╭─ main : F64 → F64 → (F64 ± F64)
╰─ measure ₁ ₀
╭─ main : (F64 ± F64) → (F64 ± F64)
╰─ ₀
8 changes: 4 additions & 4 deletions examples/uncertainty/mul_uncertain.goth
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Multiply two uncertain values with relative error propagation
# Input: a da b db (value±uncertainty for each)
# Input: two uncertain values (value±uncertainty each)
# Output: (a×b) ± |a×b|×√((da/a)²+(db/b)²)
# De Bruijn: ₃ = a, ₂ = da, ₁ = b, ₀ = db
# De Bruijn: ₁ = first, ₀ = second
# Property: relative uncertainty adds in quadrature

╭─ main : F64 F64 → F64 F64 → (F64 ± F64)
╰─ (₃ ± ₂) × (₁ ± ₀)
╭─ main : (F64 ± F64)(F64 ± F64) → (F64 ± F64)
╰─ ₁ × ₀
8 changes: 4 additions & 4 deletions examples/uncertainty/sin_uncertain.goth
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Sine of an uncertain value
# Input: x dx (value±uncertainty, x in radians)
# Input: uncertain value (value±uncertainty, x in radians)
# Output: sin(x) ± |cos(x)|×dx
# De Bruijn: ₁ = x, ₀ = dx
# De Bruijn: ₀ = uncertain value
# Property: derivative-based propagation δ(sin x) = |cos x| × δx

╭─ main : F64 F64 → (F64 ± F64)
╰─ sin (₁ ± ₀)
╭─ main : (F64 ± F64) → (F64 ± F64)
╰─ sin
8 changes: 4 additions & 4 deletions examples/uncertainty/sqrt_uncertain.goth
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Square root of an uncertain value
# Input: x dx (value±uncertainty)
# Input: uncertain value (value±uncertainty)
# Output: √x ± dx/(2√x)
# De Bruijn: ₁ = x, ₀ = dx
# De Bruijn: ₀ = uncertain value
# Precondition: x ≥ 0
# Property: derivative-based propagation δf = |f'(x)| × δx

╭─ main : F64 F64 → (F64 ± F64)
╰─ √(₁ ± ₀)
╭─ main : (F64 ± F64) → (F64 ± F64)
╰─ √
Loading