use crate::array::{Array, Data};
use crate::verb::{Enclose, Power, Verb, WindowKind, RANK_INF};
#[derive(Clone, Debug, PartialEq)]
pub enum Ar {
Prim(String),
Noun(Array),
Derived(String, Vec<Ar>),
Train(Vec<Ar>),
}
fn chars(s: &str) -> Array {
Array::from_chars(s.chars().collect())
}
fn boxes(items: Vec<Array>) -> Array {
Array::new(vec![items.len()], Data::Box(items.into()))
}
fn pair(head: Array, body: Array) -> Array {
boxes(vec![head, body])
}
impl Ar {
pub fn to_array(&self) -> Array {
match self {
Ar::Prim(s) => chars(s),
Ar::Noun(a) => pair(chars("0"), a.clone()),
Ar::Derived(s, ops) => {
pair(chars(s), boxes(ops.iter().map(Ar::to_array).collect()))
}
Ar::Train(ops) => {
let tag = if ops.len() == 2 { "2" } else { "3" };
pair(chars(tag), boxes(ops.iter().map(Ar::to_array).collect()))
}
}
}
pub fn from_array(a: &Array) -> Option<Ar> {
if let Some(text) = text_of(a) {
return Some(Ar::Prim(text));
}
let items = a.as_boxes()?;
if a.rank() != 1 || items.len() != 2 {
return None;
}
let head = text_of(&items[0])?;
if head == "0" {
return Some(Ar::Noun(items[1].clone()));
}
let parts: Option<Vec<Ar>> = items[1].as_boxes()?.iter().map(Ar::from_array).collect();
let parts = parts?;
match head.as_str() {
"2" | "3" => Some(Ar::Train(parts)),
_ => Some(Ar::Derived(head, parts)),
}
}
}
pub fn text_of(a: &Array) -> Option<String> {
if a.rank() > 1 {
return None;
}
match a.row_major_data() {
Data::Char(v) => Some(v.as_slice().iter().collect()),
_ => None,
}
}
pub fn gerund_array(items: &[Ar]) -> Array {
boxes(items.iter().map(Ar::to_array).collect())
}
fn rank_noun(r: &[i64; 3]) -> Array {
let one = |v: i64| if v == RANK_INF { f64::INFINITY } else { v as f64 };
if r[0] == r[1] && r[1] == r[2] {
return Array::new(vec![], Data::F64(vec![one(r[0])].into()));
}
Array::from_f64(vec![one(r[0]), one(r[1]), one(r[2])])
}
fn power_noun(p: &Power) -> Option<Array> {
Some(match p {
Power::Times(n) => Array::scalar_i64(*n as i64),
Power::Converge => Array::scalar_f64(f64::INFINITY),
Power::Each(ns) => Array::from_i64(ns.iter().map(|&n| n as i64).collect()),
Power::ConvergeTrace => Array::boxed(Array::empty(crate::dtype::DType::I64)),
})
}
pub fn verb_ar(v: &Verb) -> Option<Ar> {
let der = |s: &str, ops: Vec<Ar>| Some(Ar::Derived(s.to_string(), ops));
match v {
Verb::Prim(p) if p.name == "b." => None,
Verb::Prim(p) => Some(Ar::Prim(p.name.to_string())),
Verb::Rank(inner, r) => rank_ar(inner, r),
Verb::Reduce(u) => der("/", vec![verb_ar(u)?]),
Verb::Windowed(u, WindowKind::Prefix) => der("\\", vec![verb_ar(u)?]),
Verb::Windowed(u, WindowKind::Suffix) => der("\\.", vec![verb_ar(u)?]),
Verb::Windowed(_, WindowKind::Scan) => None,
Verb::Commute(u) => der("~", vec![verb_ar(u)?]),
Verb::PowerN(u, p) => der("^:", vec![verb_ar(u)?, Ar::Noun(power_noun(p)?)]),
Verb::PowerV(u, w) => der("^:", vec![verb_ar(u)?, verb_ar(w)?]),
Verb::Fork(f, g, h) => Some(Ar::Train(vec![verb_ar(f)?, verb_ar(g)?, verb_ar(h)?])),
Verb::NounFork(n, g, h) => {
Some(Ar::Train(vec![Ar::Noun(n.clone()), verb_ar(g)?, verb_ar(h)?]))
}
Verb::Hook(f, g) => Some(Ar::Train(vec![verb_ar(f)?, verb_ar(g)?])),
Verb::Atop(f, g) => match under_ar(f, g, "&.:") {
Some(ar) => Some(ar),
None => der("@:", vec![verb_ar(f)?, verb_ar(g)?]),
},
Verb::Compose(f, g) => der("&:", vec![verb_ar(f)?, verb_ar(g)?]),
Verb::BondLeft(m, u) => der("&", vec![Ar::Noun(m.clone()), verb_ar(u)?]),
Verb::BondRight(u, n) => der("&", vec![verb_ar(u)?, Ar::Noun(n.clone())]),
Verb::Each(u, Enclose::Always) => {
der("&.", vec![verb_ar(u)?, Ar::Prim(">".to_string())])
}
Verb::Each(_, Enclose::ExceptSimpleScalar) => None,
Verb::Fit(u, n) => der("!.", vec![verb_ar(u)?, Ar::Noun(Array::scalar_f64(*n))]),
Verb::Amend(m) => der("}", vec![Ar::Noun(m.clone())]),
Verb::AmendVerb(u) => der("}", vec![verb_ar(u)?]),
Verb::Memo(u, _) => der("M.", vec![verb_ar(u)?]),
Verb::Level { u, level, spread } => der(
if *spread { "S:" } else { "L:" },
vec![verb_ar(u)?, Ar::Noun(Array::scalar_i64(*level))],
),
Verb::Characteristics(u) => der("b.", vec![verb_ar(u)?]),
Verb::Key(u) => der("/.", vec![verb_ar(u)?]),
Verb::Cut(u, n) => der(";.", vec![verb_ar(u)?, Ar::Noun(Array::scalar_i64(*n))]),
Verb::Adverse(u, w) => der("::", vec![verb_ar(u)?, verb_ar(w)?]),
Verb::WithObverse(u, w) => der(":.", vec![verb_ar(u)?, verb_ar(w)?]),
Verb::Agenda(vs, w) => {
let items: Option<Vec<Ar>> = vs.iter().map(verb_ar).collect();
der("@.", vec![Ar::Noun(gerund_array(&items?)), verb_ar(w)?])
}
Verb::Evoke(vs, n) => {
let items: Option<Vec<Ar>> = vs.iter().map(verb_ar).collect();
der(
"`:",
vec![Ar::Noun(gerund_array(&items?)), Ar::Noun(Array::scalar_i64(*n))],
)
}
Verb::SelfRef => Some(Ar::Prim("$:".to_string())),
_ => None,
}
}
fn rank_ar(inner: &Verb, r: &[i64; 3]) -> Option<Ar> {
let der = |s: &str, ops: Vec<Ar>| Some(Ar::Derived(s.to_string(), ops));
if let Verb::Atop(f, g) = inner
&& matches!(&**g, Verb::Compose(_, u) if *r == [u.ranks()[0]; 3])
&& let Some(ar) = under_ar(f, g, "&.")
{
return Some(ar);
}
match inner {
Verb::Atop(f, g) if *r == g.ranks() => der("@", vec![verb_ar(f)?, verb_ar(g)?]),
Verb::Compose(f, g) if *r == [g.ranks()[0]; 3] => {
der("&", vec![verb_ar(f)?, verb_ar(g)?])
}
Verb::BondLeft(m, g) if *r == [g.ranks()[2]; 3] => {
der("&", vec![Ar::Noun(m.clone()), verb_ar(g)?])
}
Verb::BondRight(g, n) if *r == [g.ranks()[1]; 3] => {
der("&", vec![verb_ar(g)?, Ar::Noun(n.clone())])
}
_ => der("\"", vec![verb_ar(inner)?, Ar::Noun(rank_noun(r))]),
}
}
fn under_ar(f: &Verb, g: &Verb, spelling: &str) -> Option<Ar> {
let Verb::Compose(inner, under) = g else { return None };
let obverse = crate::frontend::j::obverse_of(under, crate::error::Span::new(0, 0)).ok()?;
if obverse.name() != f.name() {
return None;
}
Some(Ar::Derived(spelling.to_string(), vec![verb_ar(inner)?, verb_ar(under)?]))
}