1pub mod apl;
4pub mod j;
5
6use crate::error::{Error, ErrorKind, Result};
7use crate::fmt::FmtOpts;
8use crate::ir::{ParamSpec, Program};
9use crate::verb::{Agreement, Tol};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum Lang {
13 J,
14 Apl,
15}
16
17impl Lang {
18 pub fn from_name(name: &str) -> Option<Lang> {
19 match name.to_ascii_lowercase().as_str() {
20 "j" => Some(Lang::J),
21 "apl" => Some(Lang::Apl),
22 _ => None,
23 }
24 }
25}
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
33pub enum NestedModel {
34 #[default]
35 Floating,
36 Grounded,
37}
38
39#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
44pub enum FirstDisclose {
45 #[default]
46 UpIsFirst,
47 UpIsMix,
48}
49
50#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
56pub enum IndexForm {
57 #[default]
58 ScalarPerAxis,
59 AxisVectors,
60}
61
62#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
68pub enum DfnResult {
69 #[default]
70 LastSentence,
71 FirstNonAssignment,
72}
73
74#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
80pub enum DefaultArg {
81 #[default]
82 Eager,
83 Lazy,
84}
85
86#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
93pub enum ComplexOrder {
94 #[default]
95 RealThenImaginary,
96 MagnitudeThenAngle,
97}
98
99#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
108pub enum NestedGrade {
109 #[default]
110 Apl2,
111 TotalOrder,
112}
113
114#[derive(Clone, Copy, Debug, PartialEq)]
128pub struct Dialect {
129 pub index_origin: Option<i64>,
131 pub comparison_tolerance: Option<f64>,
133 pub nested_model: NestedModel,
134 pub first_disclose: FirstDisclose,
135 pub index_form: IndexForm,
136 pub dfn_result: DfnResult,
137 pub default_arg: DefaultArg,
138 pub complex_order: ComplexOrder,
139 pub nested_grade: NestedGrade,
140 pub trains: bool,
146}
147
148impl Default for Dialect {
149 fn default() -> Dialect {
150 Dialect::gnu_apl()
151 }
152}
153
154impl Dialect {
155 pub fn gnu_apl() -> Dialect {
161 Dialect {
162 index_origin: None,
163 comparison_tolerance: None,
164 nested_model: NestedModel::Floating,
165 first_disclose: FirstDisclose::UpIsFirst,
166 index_form: IndexForm::ScalarPerAxis,
167 dfn_result: DfnResult::LastSentence,
168 default_arg: DefaultArg::Eager,
169 complex_order: ComplexOrder::RealThenImaginary,
170 nested_grade: NestedGrade::Apl2,
171 trains: true,
172 }
173 }
174
175 pub fn j() -> Dialect {
179 Dialect::default()
180 }
181
182 pub fn rules(&self, lang: Lang) -> Result<Rules> {
189 let refuse = |what: &str| -> Error {
192 Error::new(
193 ErrorKind::NotYet,
194 format!("{what} (the reading of another APL dialect) is not supported yet"),
195 None,
196 )
197 .note("libjay implements the APL2/ISO line, which is the one its oracle verifies")
198 };
199 if let Some(ct) = self.comparison_tolerance && !(ct.is_finite() && ct >= 0.0) {
200 return Err(Error::new(
201 ErrorKind::Domain,
202 "the comparison tolerance must be a finite value at or above zero",
203 None,
204 ));
205 }
206 match self.nested_model {
207 NestedModel::Floating => {}
208 NestedModel::Grounded => return Err(refuse("a grounded nested array model")),
209 }
210 match self.first_disclose {
211 FirstDisclose::UpIsFirst => {}
212 FirstDisclose::UpIsMix => return Err(refuse("↑ as mix and ⊃ as first")),
213 }
214 match self.index_form {
215 IndexForm::ScalarPerAxis => {}
216 IndexForm::AxisVectors => return Err(refuse("⌷ over index vectors")),
217 }
218 match self.dfn_result {
219 DfnResult::LastSentence => {}
220 DfnResult::FirstNonAssignment => {
221 return Err(refuse("a dfn that answers with its first non-assignment sentence"))
222 }
223 }
224 match self.default_arg {
225 DefaultArg::Eager => {}
226 DefaultArg::Lazy => return Err(refuse("a lazy ⍺← default")),
227 }
228 match self.complex_order {
229 ComplexOrder::RealThenImaginary => {}
230 ComplexOrder::MagnitudeThenAngle => {
231 return Err(refuse("grading complex values by magnitude and angle"))
232 }
233 }
234 match self.nested_grade {
235 NestedGrade::Apl2 => {}
236 NestedGrade::TotalOrder => {
237 return Err(refuse("a total array ordering for a nested grade"))
238 }
239 }
240 let origin = match lang {
241 Lang::J => 0,
242 Lang::Apl => self.index_origin.unwrap_or(1),
243 };
244 let ct = self.comparison_tolerance.unwrap_or(match lang {
245 Lang::J => Tol::J.ct,
246 Lang::Apl => Tol::APL.ct,
247 });
248 Ok(Rules {
249 lang,
250 origin,
251 ct,
252 nested_model: self.nested_model,
253 first_disclose: self.first_disclose,
254 index_form: self.index_form,
255 dfn_result: self.dfn_result,
256 default_arg: self.default_arg,
257 complex_order: self.complex_order,
258 nested_grade: self.nested_grade,
259 trains: self.trains,
260 })
261 }
262}
263
264#[derive(Clone, Copy, Debug, PartialEq)]
269pub struct Rules {
270 pub lang: Lang,
271 pub origin: i64,
273 pub ct: f64,
277 pub nested_model: NestedModel,
278 pub first_disclose: FirstDisclose,
279 pub index_form: IndexForm,
280 pub dfn_result: DfnResult,
281 pub default_arg: DefaultArg,
282 pub complex_order: ComplexOrder,
283 pub nested_grade: NestedGrade,
284 pub trains: bool,
285}
286
287impl Rules {
288 pub fn tol(&self) -> Tol {
290 Tol { ct: self.ct, by_smaller: self.lang == Lang::J }
291 }
292
293 pub fn dialect(&self) -> Dialect {
296 Dialect {
297 index_origin: Some(self.origin),
298 comparison_tolerance: Some(self.ct),
299 nested_model: self.nested_model,
300 first_disclose: self.first_disclose,
301 index_form: self.index_form,
302 dfn_result: self.dfn_result,
303 default_arg: self.default_arg,
304 complex_order: self.complex_order,
305 nested_grade: self.nested_grade,
306 trains: self.trains,
307 }
308 }
309}
310
311impl Default for Rules {
312 fn default() -> Rules {
314 Dialect::default().rules(Lang::J).expect("J's defaults are implemented")
315 }
316}
317
318#[derive(Clone, Debug)]
321pub struct SourceParts {
322 pub display: String,
323 pub segments: Vec<Segment>,
324 pub param_names: Vec<String>,
325}
326
327#[derive(Clone, Debug)]
328pub enum Segment {
329 Text { text: String, offset: usize },
331 Param { index: usize, offset: usize, len: usize },
333}
334
335impl SourceParts {
336 pub fn from_parts(parts: &[&str], names: &[&str]) -> SourceParts {
340 assert_eq!(parts.len(), names.len() + 1, "N parts need N-1 holes");
341 let mut display = String::new();
342 let mut segments = Vec::new();
343 let mut param_names: Vec<String> = Vec::new();
344 for (i, part) in parts.iter().enumerate() {
345 if !part.is_empty() {
346 segments.push(Segment::Text { text: (*part).to_string(), offset: display.len() });
347 display.push_str(part);
348 }
349 if i < names.len() {
350 let name = names[i];
351 let index = param_names
352 .iter()
353 .position(|n| n == name)
354 .unwrap_or_else(|| {
355 param_names.push(name.to_string());
356 param_names.len() - 1
357 });
358 let shown = format!("{{{name}}}");
359 segments.push(Segment::Param { index, offset: display.len(), len: shown.len() });
360 display.push_str(&shown);
361 }
362 }
363 SourceParts { display, segments, param_names }
364 }
365
366 pub fn from_source(src: &str) -> Result<SourceParts> {
369 let bytes = src.as_bytes();
370 let mut parts: Vec<String> = vec![String::new()];
371 let mut names: Vec<String> = Vec::new();
372 let mut in_quote = false;
373 let mut i = 0;
374 while i < src.len() {
375 let ch = src[i..].chars().next().unwrap();
376 if ch == '\'' {
377 in_quote = !in_quote;
378 parts.last_mut().unwrap().push(ch);
379 i += 1;
380 continue;
381 }
382 if ch == '{' && !in_quote {
383 let rest = &src[i + 1..];
387 if let Some(end) = rest.find('}') {
388 let name = &rest[..end];
389 if is_identifier(name) {
390 names.push(name.to_string());
391 parts.push(String::new());
392 i += 2 + end;
393 continue;
394 }
395 }
396 }
397 parts.last_mut().unwrap().push(ch);
398 i += ch.len_utf8();
399 }
400 let _ = bytes;
401 let part_refs: Vec<&str> = parts.iter().map(|s| s.as_str()).collect();
402 let name_refs: Vec<&str> = names.iter().map(|s| s.as_str()).collect();
403 Ok(SourceParts::from_parts(&part_refs, &name_refs))
404 }
405}
406
407fn is_identifier(s: &str) -> bool {
408 let mut chars = s.chars();
409 match chars.next() {
410 Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
411 _ => return false,
412 }
413 chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
414}
415
416pub fn compile(lang: Lang, source: &str, dialect: &Dialect) -> Result<Program> {
418 let sp = SourceParts::from_source(source)?;
419 compile_source_parts(lang, sp, dialect)
420}
421
422pub fn compile_parts(
424 lang: Lang,
425 parts: &[&str],
426 names: &[&str],
427 dialect: &Dialect,
428) -> Result<Program> {
429 compile_source_parts(lang, SourceParts::from_parts(parts, names), dialect)
430}
431
432fn compile_source_parts(lang: Lang, sp: SourceParts, dialect: &Dialect) -> Result<Program> {
433 let rules = dialect.rules(lang)?;
434 let tol = rules.tol();
435 let (mut stmts, agreement, fmt) = match lang {
436 Lang::J => (j::parse(&sp)?, Agreement::LeadingPrefix, FmtOpts::J),
437 Lang::Apl => (apl::parse(&sp, rules)?, Agreement::ExactOrScalar, FmtOpts::APL),
438 };
439 for stmt in &stmts {
443 crate::verb::check_nesting(stmt.depth(), stmt.span())?;
444 }
445 crate::fuse::pass(&mut stmts, tol);
446 let params = sp.param_names.into_iter().map(|name| ParamSpec { name }).collect();
447 Ok(Program { stmts, params, display_src: sp.display, agreement, fmt, rules })
448}