cl-embed: Add an example, and a new sample-code (same file)

This commit is contained in:
John 2025-05-18 11:50:33 -04:00
parent 6ce27c522f
commit 47608668fa
4 changed files with 123 additions and 4 deletions

View File

@ -0,0 +1 @@
../../../../sample-code/calculator.cl

View File

@ -0,0 +1,25 @@
use cl_embed::*;
use repline::{Response, prebaked};
fn main() -> Result<(), repline::Error> {
prebaked::read_and("", "calc >", " ? >", |line| {
calc(line).map_err(Into::into)
})
}
fn calc(line: &str) -> Result<Response, EvalError> {
let mut env = Environment::new();
env.bind("line", line);
let res = conlang!(
mod expression;
use expression::{eval, parse};
let (expr, rest) = parse(line.chars(), 0);
eval(expr)
)(&mut env)?;
println!("{res}");
Ok(Response::Accept)
}

View File

@ -162,10 +162,8 @@ impl Interpret for Enum {
let idx = idx.interpret(env)?;
env.insert(*name, Some(idx))
}
(StructKind::Tuple(_), None) => eprintln!("TODO: Enum-tuple variants: {kind}"),
(StructKind::Struct(_), None) => {
eprintln!("TODO: Enum-struct members: {kind}")
}
(StructKind::Tuple(_), None) => {}
(StructKind::Struct(_), None) => {}
_ => eprintln!("Well-formedness error in {self}"),
}
}

95
sample-code/calculator.cl Normal file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env -S conlang-run
//! A simple five-function pn calculator
// TODO: enum constructors in the interpreter
struct Atom(f64);
struct Op(char, [Expr]);
enum Expr {
Atom(f64),
Op(char, [Expr]),
}
// Evaluates an expression
fn eval(expr: Expr) -> isize {
match expr {
Atom(value) => value,
Op('*', [lhs, rhs]) => eval(lhs) * eval(rhs),
Op('/', [lhs, rhs]) => eval(lhs) / eval(rhs),
Op('%', [lhs, rhs]) => eval(lhs) % eval(rhs),
Op('+', [lhs, rhs]) => eval(lhs) + eval(rhs),
Op('-', [lhs, rhs]) => eval(lhs) - eval(rhs),
Op('-', [lhs]) => - eval(lhs),
Op(other, ..rest) => {
panic("ERROR: Unknown operator: " + other)
},
other => {
println(other);
panic("ERROR: Unknown operation ^")
}
}
}
/// Parses expressions
fn parse(line: [char], power: i32) -> (Expr, [char]) {
fn map((expr, line): (Expr, [char]), f: fn(Expr) -> Expr) -> (Expr, [char]) {
(f(expr), line)
}
line = space(line);
let (lhs, line) = match line {
['0'..='9', ..] => number(line),
[op, ..rest] => {
parse(rest, pre_bp(op)).map(|lhs| Op(op, [lhs]))
},
_ => panic("Unexpected end of input"),
};
while let [op, ..rest] = space(line) {
let (before, after) = inf_bp(op);
if before < power {
break;
};
(lhs, line) = parse(rest, after).map(|rhs| Op(op, [lhs, rhs]));
};
(lhs, line)
}
fn number(line: [char]) -> (Expr, [char]) {
let value = 0.0;
while (let [first, ..rest] = line) && (let '0'..='9' = first) {
value = value * 10.0 + (first as f64 - '0' as f64);
line = rest;
};
(Atom(value), line)
}
fn space(line: [char]) -> [char] {
match line {
[' ', ..rest] => space(rest),
line => line
}
}
fn inf_bp(op: char) -> (i32, i32) {
(|x| (2 * x, 2 * x + 1))(
match op {
'*' => 2,
'/' => 2,
'%' => 2,
'+' => 1,
'-' => 1,
_ => -1,
})
}
fn pre_bp(op: char) -> i32 {
(|x| 2 * x + 1)(
match op {
'-' => 9,
_ => -1,
})
}