sim_lib_lang_scheme/
lowering.rs1use sim_kernel::{Datum, Expr, LocatedExprTree, Origin, Result, Symbol, Term};
2
3use crate::symbols::scheme_symbol;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
10pub enum SchemeLowered {
11 Term(Term),
13 Datum(Datum),
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct LocatedSchemeLowering {
20 pub lowered: SchemeLowered,
22 pub origin: Option<Origin>,
24}
25
26pub fn lower_scheme_tree(tree: &LocatedExprTree) -> Result<LocatedSchemeLowering> {
28 Ok(LocatedSchemeLowering {
29 lowered: lower_scheme_expr(&tree.expr)?,
30 origin: tree.origin.clone(),
31 })
32}
33
34pub fn lower_scheme_expr(expr: &Expr) -> Result<SchemeLowered> {
39 match expr {
40 Expr::Nil | Expr::Bool(_) | Expr::Number(_) | Expr::String(_) | Expr::Bytes(_) => {
41 Datum::try_from(expr.clone()).map(SchemeLowered::Datum)
42 }
43 Expr::List(items) => lower_scheme_list(items),
44 Expr::Symbol(_) | Expr::Call { .. } | Expr::Block(_) => {
45 Term::lower(canonical_eval_expr(expr.clone())).map(SchemeLowered::Term)
46 }
47 _ => Term::lower(expr.clone()).map(SchemeLowered::Term),
48 }
49}
50
51fn lower_scheme_list(items: &[Expr]) -> Result<SchemeLowered> {
52 let Some(Expr::Symbol(head)) = items.first() else {
53 return Datum::try_from(Expr::List(items.to_vec())).map(SchemeLowered::Datum);
54 };
55 if head == &Symbol::new("quote") {
56 let [_, datum] = items else {
57 return Err(sim_kernel::Error::Eval(
58 "Scheme quote expects exactly one datum".to_owned(),
59 ));
60 };
61 return Datum::try_from(datum.clone()).map(SchemeLowered::Datum);
62 }
63 Term::lower(canonical_call(head.clone(), &items[1..])).map(SchemeLowered::Term)
64}
65
66fn canonical_eval_expr(expr: Expr) -> Expr {
67 match expr {
68 Expr::List(items) => match items.first() {
69 Some(Expr::Symbol(head)) => canonical_call(head.clone(), &items[1..]),
70 _ => Expr::List(items),
71 },
72 other => other,
73 }
74}
75
76fn canonical_call(head: Symbol, args: &[Expr]) -> Expr {
77 if head == Symbol::new("begin") {
78 return Expr::Block(args.iter().cloned().map(canonical_eval_expr).collect());
79 }
80 Expr::Call {
81 operator: Box::new(Expr::Symbol(scheme_symbol(&head.to_string()))),
82 args: args.iter().cloned().map(canonical_eval_expr).collect(),
83 }
84}