1use crate::encode::{decode_escape, encode_algol, encode_algol_tree};
6use crate::parse::{decode_algol_located_with_budget, raw_number_tag};
7use crate::pratt::{PrattParser, default_pratt_table};
8use sim_codec::{
9 CodecDefaultDecode, CodecRuntime, DecodeBudget, Decoder, Encoder, Input, LocatedDecoder,
10 Output, ReadCx, TreeDecoder, TreeEncoder, codec_value,
11};
12use sim_kernel::{
13 AbiVersion, DefaultFactory, Dependency, Error, Export, Expr, Lib, LibManifest, LibTarget,
14 Linker, LocatedExprTree, Result, SourceId, Symbol, Version, WriteCx, pratt_table_value,
15};
16use std::sync::Arc;
17
18pub struct AlgolCodec {
26 parser: PrattParser,
27}
28
29impl Default for AlgolCodec {
30 fn default() -> Self {
31 Self {
32 parser: PrattParser::new(default_pratt_table()),
33 }
34 }
35}
36
37impl Decoder for AlgolCodec {
38 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
39 let source = input.into_string()?;
40 let mut budget = DecodeBudget::new(cx.limits);
41 budget.check_input_bytes(cx.codec, source.len())?;
42 let mut tree =
43 self.parser
44 .parse_text_tree_with_budget(cx.codec, "<algol>", &source, &mut budget)?;
45 rewrite_number_domains_tree(cx, &mut tree)?;
46 decode_escape(cx, tree.expr)
47 }
48}
49
50impl LocatedDecoder for AlgolCodec {
51 fn decode_located(
52 &self,
53 cx: &mut ReadCx<'_>,
54 input: Input,
55 source_id: String,
56 ) -> Result<sim_kernel::LocatedExpr> {
57 let source = input.into_string()?;
58 let mut budget = DecodeBudget::new(cx.limits);
59 budget.check_input_bytes(cx.codec, source.len())?;
60 cx.cx
61 .sources_mut()
62 .intern_text(SourceId(source_id.clone()), &source);
63 let mut located =
64 decode_algol_located_with_budget(cx.codec, source_id, &source, &mut budget)?;
65 located.expr = rewrite_number_domains_expr(cx, located.expr)?;
66 Ok(located)
67 }
68}
69
70impl TreeDecoder for AlgolCodec {
71 fn decode_tree(
72 &self,
73 cx: &mut ReadCx<'_>,
74 input: Input,
75 source_id: String,
76 ) -> Result<LocatedExprTree> {
77 let source = input.into_string()?;
78 let mut budget = DecodeBudget::new(cx.limits);
79 budget.check_input_bytes(cx.codec, source.len())?;
80 cx.cx
81 .sources_mut()
82 .intern_text(SourceId(source_id.clone()), &source);
83 let mut tree =
84 self.parser
85 .parse_text_tree_with_budget(cx.codec, source_id, &source, &mut budget)?;
86 rewrite_number_domains_tree(cx, &mut tree)?;
87 Ok(tree)
88 }
89}
90
91fn rewrite_number_domains_expr(cx: &mut ReadCx<'_>, expr: Expr) -> Result<Expr> {
92 Ok(match expr {
93 Expr::Extension { tag, payload } if tag == raw_number_tag() => {
94 let Expr::String(raw) = *payload else {
95 return Err(Error::CodecError {
96 codec: cx.codec,
97 message: "algol number literal payload must be a string".to_owned(),
98 });
99 };
100 match cx.cx.parse_number_literal(&raw)? {
101 Some(number) => Expr::Number(number),
102 None => {
103 return Err(Error::CodecError {
104 codec: cx.codec,
105 message: format!("no number domain accepted literal {raw}"),
106 });
107 }
108 }
109 }
110 Expr::List(items) => Expr::List(
111 items
112 .into_iter()
113 .map(|item| rewrite_number_domains_expr(cx, item))
114 .collect::<Result<Vec<_>>>()?,
115 ),
116 Expr::Vector(items) => Expr::Vector(
117 items
118 .into_iter()
119 .map(|item| rewrite_number_domains_expr(cx, item))
120 .collect::<Result<Vec<_>>>()?,
121 ),
122 Expr::Map(entries) => Expr::Map(
123 entries
124 .into_iter()
125 .map(|(key, value)| {
126 Ok((
127 rewrite_number_domains_expr(cx, key)?,
128 rewrite_number_domains_expr(cx, value)?,
129 ))
130 })
131 .collect::<Result<Vec<_>>>()?,
132 ),
133 Expr::Set(items) => Expr::Set(
134 items
135 .into_iter()
136 .map(|item| rewrite_number_domains_expr(cx, item))
137 .collect::<Result<Vec<_>>>()?,
138 ),
139 Expr::Call { operator, args } => Expr::Call {
140 operator: Box::new(rewrite_number_domains_expr(cx, *operator)?),
141 args: args
142 .into_iter()
143 .map(|item| rewrite_number_domains_expr(cx, item))
144 .collect::<Result<Vec<_>>>()?,
145 },
146 Expr::Infix {
147 operator,
148 left,
149 right,
150 } => Expr::Infix {
151 operator,
152 left: Box::new(rewrite_number_domains_expr(cx, *left)?),
153 right: Box::new(rewrite_number_domains_expr(cx, *right)?),
154 },
155 Expr::Prefix { operator, arg } => Expr::Prefix {
156 operator,
157 arg: Box::new(rewrite_number_domains_expr(cx, *arg)?),
158 },
159 Expr::Postfix { operator, arg } => Expr::Postfix {
160 operator,
161 arg: Box::new(rewrite_number_domains_expr(cx, *arg)?),
162 },
163 Expr::Block(items) => Expr::Block(
164 items
165 .into_iter()
166 .map(|item| rewrite_number_domains_expr(cx, item))
167 .collect::<Result<Vec<_>>>()?,
168 ),
169 Expr::Quote { mode, expr } => Expr::Quote { mode, expr },
170 Expr::Annotated { expr, annotations } => Expr::Annotated {
171 expr: Box::new(rewrite_number_domains_expr(cx, *expr)?),
172 annotations: annotations
173 .into_iter()
174 .map(|(name, value)| Ok((name, rewrite_number_domains_expr(cx, value)?)))
175 .collect::<Result<Vec<_>>>()?,
176 },
177 Expr::Extension { tag, payload } => Expr::Extension {
178 tag,
179 payload: Box::new(rewrite_number_domains_expr(cx, *payload)?),
180 },
181 other => other,
182 })
183}
184
185fn rewrite_number_domains_tree(cx: &mut ReadCx<'_>, tree: &mut LocatedExprTree) -> Result<()> {
186 tree.expr = rewrite_number_domains_expr(cx, tree.expr.clone())?;
187 if matches!(tree.expr, Expr::Quote { .. }) {
188 return Ok(());
189 }
190 for child in &mut tree.children {
191 rewrite_number_domains_tree(cx, child)?;
192 }
193 Ok(())
194}
195
196impl Encoder for AlgolCodec {
197 fn encode(&self, cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
198 Ok(Output::Text(encode_algol(
199 expr,
200 &self.parser.operators,
201 0,
202 cx,
203 )?))
204 }
205}
206
207impl TreeEncoder for AlgolCodec {
208 fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
209 Ok(Output::Text(encode_algol_tree(
210 expr,
211 &self.parser.operators,
212 0,
213 cx,
214 )?))
215 }
216}
217
218pub struct AlgolCodecLib {
224 symbol: Symbol,
225 codec_id: sim_kernel::CodecId,
226}
227
228impl AlgolCodecLib {
229 pub fn new(id: sim_kernel::CodecId) -> Self {
231 Self {
232 symbol: Symbol::qualified("codec", "algol"),
233 codec_id: id,
234 }
235 }
236}
237
238impl Lib for AlgolCodecLib {
239 fn manifest(&self) -> LibManifest {
240 LibManifest {
241 id: self.symbol.clone(),
242 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
243 abi: AbiVersion { major: 0, minor: 1 },
244 target: LibTarget::HostRegistered,
245 requires: vec![Dependency {
246 id: Symbol::qualified("codec", "lisp"),
247 minimum_version: None,
248 }],
249 capabilities: Vec::new(),
250 exports: vec![
251 Export::Codec {
252 symbol: self.symbol.clone(),
253 codec_id: Some(self.codec_id),
254 },
255 Export::Value {
256 symbol: Symbol::qualified("pratt", "arithmetic"),
257 },
258 ],
259 }
260 }
261
262 fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
263 let _factory = DefaultFactory;
264 let expr_shape =
265 sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "AlgolSurface"))?;
266 let options_shape = sim_codec::resolve_options_shape(linker)?;
267
268 linker.codec_value(
269 self.symbol.clone(),
270 codec_value(CodecRuntime {
271 id: self.codec_id,
272 symbol: self.symbol.clone(),
273 decoder: Some(Arc::new(AlgolCodec::default())),
274 located_decoder: Some(Arc::new(AlgolCodec::default())),
275 tree_decoder: Some(Arc::new(AlgolCodec::default())),
276 encoder: Some(Arc::new(AlgolCodec::default())),
277 located_encoder: None,
278 tree_encoder: Some(Arc::new(AlgolCodec::default())),
279 expr_shape,
280 options_shape,
281 default_decode: CodecDefaultDecode::TermInEvalDatumOtherwise,
282 }),
283 )?;
284 linker.value(
285 Symbol::qualified("pratt", "arithmetic"),
286 pratt_table_value(
287 Symbol::qualified("pratt", "arithmetic"),
288 default_pratt_table(),
289 ),
290 )?;
291 Ok(())
292 }
293}