use crate::{esc, macros::*, un_esc};
use colored::Colorize;
use std::{borrow::Cow, fmt};
#[derive(Debug, Clone, PartialEq)]
pub enum Node {
I(i64),
F(f64),
C(char),
X(Cow<'static, str>),
A(Vec<Node>),
O(Vec<Node>),
P(Vec<Node>),
T(Vec<(Node, Node)>),
S(Box<Node>),
M {
verb: Cow<'static, str>,
rhs: Option<Box<Node>>,
},
D {
verb: Cow<'static, str>,
lhs: Box<Node>,
rhs: Option<Box<Node>>,
},
}
impl Node {
pub fn monad(v: &str, x: Node) -> Self {
Self::M {
verb: v.to_string().into(),
rhs: Some(Box::new(x)),
}
}
pub fn dyad(v: &str, x: Node, y: Node) -> Self {
Self::D {
verb: v.to_string().into(),
lhs: Box::new(x),
rhs: Some(Box::new(y)),
}
}
pub fn empty() -> Self {
Node::A(Vec::new())
}
pub fn from_str<T>(x: T) -> Self
where
String: From<T>,
{
Node::A(String::from(x).chars().map(|x| Node::C(x)).collect())
}
pub fn is_c(&self) -> bool {
match self {
Node::C(_) => true,
_ => false,
}
}
pub fn is_a(&self) -> bool {
match self {
Node::A(_) => true,
_ => false,
}
}
pub fn is_i(&self) -> bool {
match self {
Node::I(_) => true,
_ => false,
}
}
pub fn is_x(&self) -> bool {
match self {
Node::X(_) => true,
_ => false,
}
}
pub fn is_m(&self) -> bool {
match self {
Node::M { verb: _, rhs: _ } => true,
_ => false,
}
}
pub fn is_str(&self) -> bool {
match self {
Node::A(x) if x.iter().all(|x| x.is_c()) => true,
_ => false,
}
}
pub fn is_mat(&self) -> bool {
match self {
Node::A(x) if x.iter().all(|x| x.is_a()) => true,
_ => false,
}
}
pub fn is_gets(&self) -> bool {
match self {
Node::D {
verb: v,
lhs: _,
rhs: _,
} => &**v == ":",
_ => false,
}
}
pub fn un_gets(&self) -> (Node, Node) {
match self {
Node::D {
verb: v,
lhs,
rhs: Some(rhs),
} if &**v == ":" => (*lhs.clone(), *rhs.clone()),
_ => fatal!("un_gets called on non-gets {self}"),
}
}
pub fn un_c(&self) -> char {
match self {
Node::C(x) => *x,
_ => fatal!("un_c called on non-char {self}"),
}
}
pub fn un_i(&self) -> i64 {
match self {
Node::I(x) => *x,
_ => fatal!("un_i called on non-int {self}"),
}
}
pub fn un_a(&self) -> Vec<Node> {
match self {
Node::A(x) => x.clone(),
_ => fatal!("un_a called on non-vec {self}"),
}
}
pub fn un_str(&self) -> String {
match self {
a @ Node::A(x) if a.is_str() => {
x.into_iter().map(|x| x.un_c()).collect::<String>()
}
_ => fatal!("un_str called on non-str {self}"),
}
}
}
impl fmt::Display for Node {
#[allow(unreachable_patterns)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Node::I(x) => x.to_string(),
Node::F(x) => x.to_string(),
Node::C(x) => format!("\"{}\"", esc(x.to_string())),
Node::S(x) => format!("`({x})"),
Node::X(x) => x.to_string(),
Node::A(x) if x.len() == 0 => "()".to_string(),
Node::A(x) if x.len() == 1 => format!(",{}", x[0]),
a @ Node::A(_) if a.is_str() =>
format!(r#""{}""#, esc(a.un_str())),
a @ Node::A(x) if a.is_mat() => format!(
"( {}\n)",
x.iter()
.map(|x| format!("{x}"))
.collect::<Vec<_>>()
.join("\n; ")
),
Node::A(x) => format!(
"({})",
x.iter()
.map(|x| format!("{x}"))
.collect::<Vec<String>>()
.join(";")
),
Node::O(x) => format!(
"{{{}}}",
x.iter()
.map(|x| format!("{x}"))
.collect::<Vec<_>>()
.join(";")
),
Node::P(x) => format!(
"[{}]",
x.iter()
.map(|x| format!("{x}"))
.collect::<Vec<_>>()
.join(";")
),
Node::T(x) => format!(
"([{}])",
x.iter()
.map(|(k, v)| format!("{k}:{v}"))
.collect::<Vec<_>>()
.join(";")
),
Node::M {
verb: v,
rhs: Some(x),
} if x.is_m() => format!("{v} {}", *x),
Node::M {
verb: v,
rhs: Some(x),
} => format!("{v}{}", *x),
Node::M { verb: v, rhs: None } => format!("{v}"),
Node::D {
verb: v,
lhs: x,
rhs: Some(y),
} => format!("{}{v}{}", *x, *y),
Node::D {
verb: v,
lhs: x,
rhs: None,
} => format!("{}{v}", *x),
_ => "???".to_string(),
}
)
}
}
#[test]
fn fmt_node() {
for (x, y) in [
(Node::I(1), "1"),
(Node::monad("!", Node::I(1)), "!1"),
(Node::dyad("+", Node::I(1), Node::I(1)), "1+1"),
(Node::A(vec![Node::I(1), Node::I(2)]), "(1;2)"),
]
.into_iter()
{
assert_eq!(format!("{x}"), y.to_string())
}
}
#[derive(Debug, Copy, Clone)]
pub struct Pos {
pub line: usize,
pub col: usize,
}
impl Pos {
pub fn new() -> Self {
Self { line: 1, col: 1 }
}
pub fn newline(&mut self) {
self.col = 1;
self.line += 1;
}
}
impl fmt::Display for Pos {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "line {}, column {}", self.line, self.col)
}
}
#[derive(Debug, Clone)]
pub struct Tape {
src: String,
idx: usize,
pos: Pos,
}
impl Tape {
pub fn new<T>(x: T) -> Self
where
String: From<T>,
{
Self {
src: String::from(x),
idx: 0,
pos: Pos::new(),
}
}
pub fn peek(&self) -> Option<char> {
self.src.chars().nth(self.idx)
}
pub fn peek_next(&self) -> Option<char> {
self.src.chars().nth(self.idx + 1)
}
pub fn peek_n(&self, n: usize) -> Option<String> {
let mut s = String::new();
let mut i = self.idx;
for _ in 0..n {
match self.src.chars().nth(i) {
Some(x) => {
s.push(x);
i += 1;
}
None => return None,
}
}
Some(s)
}
pub fn last_n(&self, n: usize) -> Option<String> {
#[allow(unused_comparisons)]
let g = |x: &[_], i: usize| {
if i >= 0 && i < x.len() {
Some(x[i])
} else {
None
}
};
let c = self.src.chars().collect::<Vec<_>>();
let mut s = String::new();
let mut i = self.idx;
for _ in 0..n {
match g(&c, i) {
Some(x) => {
s.push(x);
i -= 1;
}
None => return None,
}
}
Some(s.chars().rev().collect::<String>())
}
pub fn prev(&self) -> Option<char> {
self.src.chars().nth(self.idx - 1)
}
pub fn inc(&mut self) {
self.idx += 1;
self.pos.col += 1;
}
pub fn newline(&mut self) {
self.pos.newline();
}
pub fn skip(&mut self, n: usize) {
for _ in 0..n {
self.inc();
}
}
pub fn next(&mut self) -> Option<char> {
let c = self.peek()?;
self.inc();
Some(c)
}
}
macro_rules! err_parse {
($x:expr, $($t:tt)*) => {{
err_fmt!("{}: {}\n{}", "'parse".cyan(), $x.pos, format!($($t)*))
}};
}
pub fn space(t: &mut Tape) {
while let Some(c) = t.peek()
&& (match c {
' ' | '\t' => true,
'\n' => {
t.newline();
true
}
_ => false,
})
{
t.inc();
}
}
fn scan_int(t: &mut Tape, s: &mut String) {
while let Some(x) = t.peek()
&& x.is_digit(10)
{
t.inc();
s.push(x);
}
}
pub fn num(t: &mut Tape) -> Result<Node, String> {
let mut s = String::new();
scan_int(t, &mut s);
match t.peek() {
Some('.') => {
s.push('.');
t.inc();
scan_int(t, &mut s);
Ok(Node::F(s.parse().unwrap()))
}
_ => Ok(Node::I(s.parse().unwrap())),
}
}
fn scan_name(t: &mut Tape, s: &mut String) {
while let Some(x) = t.peek()
&& x.is_alphabetic()
{
t.inc();
s.push(x);
}
}
fn name(t: &mut Tape) -> Result<Node, String> {
let mut s = String::new();
scan_name(t, &mut s);
loop {
match t.peek_n(2) {
Some(x) if &x == "::" => {
s += "::";
t.skip(2);
match t.peek() {
Some(x) if x.is_alphabetic() => scan_name(t, &mut s),
Some(x) => return err_parse!(t, "expected name, got {x}"),
None => {
return err_parse!(t, "expected name, got end of input")
}
}
}
Some(_) | None => break Ok(Node::X(s.into())),
}
}
}
pub fn sym(t: &mut Tape) -> Result<Node, String> {
match t.peek() {
Some('`') => {
t.inc();
Ok(Node::S(Box::new(expr(t)?)))
}
Some(c) => err_parse!(t, "expected symbol, got {c}"),
None => err_parse!(t, "expected symbol, got end of input"),
}
}
pub fn chr(t: &mut Tape) -> Result<Node, String> {
let mut v: Vec<char> = Vec::new();
match t.peek() {
Some('"') => {
t.inc();
while let Some(x) = t.peek()
&& !(x == '"' && t.prev() != Some('\\'))
{
t.inc();
v.push(x);
}
match t.peek() {
Some('"') => {
t.inc();
Ok(if v.len() > 1 {
Node::from_str(
match un_esc(v.iter().collect::<String>()) {
Ok(x) => x,
Err(e) => return err_parse!(t, "{e}"),
},
)
} else {
Node::C(v[0])
})
}
Some(c) => err_parse!(t, "unexpected \"{c}\", expected '\"'"),
None => err_parse!(t, "unexpected end of input, expected '\"'"),
}
}
Some(x) => err_parse!(t, "expected char, got \"{x}\""),
None => err_parse!(t, "expected char, got end of input"),
}
}
fn table(t: &mut Tape, x: &[Node]) -> Result<Node, String> {
if x.iter().all(|x| x.is_gets()) {
Ok(Node::T(x.into_iter().map(|x| x.un_gets()).collect()))
} else {
err_parse!(
t,
"table must contain only assignment, but the following were not:\n{}",
x.iter()
.filter(|x| !x.is_gets())
.map(|x| format!(" -> {}", format!("{x}").blue()))
.collect::<Vec<_>>()
.join("\n")
)
}
}
pub fn prn(t: &mut Tape) -> Result<Node, String> {
match t.peek() {
Some('(') if t.peek_next() == Some(')') => Ok({
t.skip(2);
Node::empty()
}),
Some('(') => {
t.inc();
let v = exprs(t)?;
match t.peek() {
Some(')') => {
t.inc();
Ok(if v.len() > 1 {
Node::A(v)
} else {
match &v[0] {
Node::P(x) => table(t, x)?,
x => x.clone(),
}
})
}
Some(c) => err_parse!(t, "unexpected \"{c}\", expected ')'"),
None => err_parse!(t, "unexpected end of input, expected ')'"),
}
}
Some(x) => err_parse!(t, "expected paren, got \"{x}\""),
None => err_parse!(t, "expected paren, got end of input"),
}
}
pub fn prg(t: &mut Tape) -> Result<Node, String> {
match t.peek() {
Some('[') if t.peek_next() == Some(']') => Ok({
t.skip(2);
Node::empty()
}),
Some('[') => {
t.inc();
let v = exprs(t)?;
match t.peek() {
Some(']') => {
t.inc();
Ok(Node::P(v))
}
Some(c) => err_parse!(t, "unexpected \"{c}\", expected ']'"),
None => err_parse!(t, "unexpected end of input, expected ']'"),
}
}
Some(x) => err_parse!(t, "expected progn, got \"{x}\""),
None => err_parse!(t, "expected progn, got end of input"),
}
}
fn lambda(t: &mut Tape) -> Result<Node, String> {
match t.peek() {
Some('{') if t.peek_next() == Some('}') => {
t.skip(2);
Ok(Node::O(Vec::new()))
}
Some('{') => {
t.inc();
let v = exprs(t)?;
match t.peek() {
Some('}') => {
t.inc();
Ok(Node::O(v))
}
Some(c) => {
err_parse!(t, "unexpected \"{c}\", expected '}}'")
}
None => err_parse!(t, "unexpected end of input, expected '}}'"),
}
}
Some(x) => err_parse!(t, "expected lambda, got \"{x}\""),
None => err_parse!(t, "expected lambda, got end of input"),
}
}
fn apply(t: &mut Tape, x: Node) -> Result<Node, String> {
let a = |x, y| Node::D {
verb: '.'.to_string().into(),
lhs: Box::new(x),
rhs: Some(Box::new(y)),
};
match t.peek() {
Some('[') if t.peek_next() == Some(']') => {
t.skip(2);
Ok(a(x, Node::empty()))
}
Some('[') => {
t.inc();
let v = exprs(t)?;
match t.peek() {
Some(']') => {
t.inc();
Ok(a(x, Node::A(v)))
}
Some(c) => err_parse!(t, "unexpected \"{c}\", expected ']'"),
None => err_parse!(t, "unexpected end of input, expected ']'"),
}
}
Some(x) => err_parse!(t, "expected application, got \"{x}\""),
None => err_parse!(t, "expected application, got end of input"),
}
}
pub static VERB_CHRS: &'static str = "~!@#$%^&*_+-=:'<>?/.,\\|";
fn verb(t: &mut Tape) -> Result<String, String> {
let mut s = String::new();
while let Some(x) = t.peek()
&& VERB_CHRS.contains(x)
{
t.inc();
s.push(x);
}
if s.len() > 0 {
space(t);
Ok(s)
} else {
err_parse!(t, "verb not found")
}
}
pub static TERM_CHRS: &'static str = ")]};";
pub fn monad(t: &mut Tape) -> Result<Node, String> {
match t.peek() {
Some(x) if VERB_CHRS.contains(x) => {
let s = verb(t)?.into();
Ok(match t.peek() {
Some(x) if TERM_CHRS.contains(x) => {
Node::M { verb: s, rhs: None }
}
None => Node::M { verb: s, rhs: None },
_ => Node::monad(&*s, expr(t)?),
})
}
Some(x) => err_parse!(t, "expected monad, got \"{x}\""),
None => err_parse!(t, "expected monad, got end of input"),
}
}
pub fn dyad(t: &mut Tape, x: Node) -> Result<Node, String> {
let v = verb(t)?.into();
Ok(match t.peek() {
Some(c) if TERM_CHRS.contains(c) => Node::D {
verb: v,
lhs: Box::new(x),
rhs: None,
},
None => Node::D {
verb: v,
lhs: Box::new(x),
rhs: None,
},
_ => Node::dyad(&*v, x, expr(t)?),
})
}
pub fn expr(t: &mut Tape) -> Result<Node, String> {
let x = match t.peek() {
Some(x) if x.is_digit(10) => num(t),
Some(x) if x.is_alphabetic() => name(t),
Some('`') => sym(t),
Some('"') => chr(t),
Some('(') => prn(t),
Some('[') => prg(t),
Some('{') => lambda(t),
Some(x) if VERB_CHRS.contains(x) => monad(t),
Some(x) => err_parse!(t, "expected expression, got '{x}'"),
None => err_parse!(t, "expected expression, got end of input"),
}?;
space(t);
match t.peek() {
Some(c) if VERB_CHRS.contains(c) => dyad(t, x),
Some('[') => apply(t, x),
_ => Ok(x),
}
}
fn _exprs(t: &mut Tape, e: Option<&str>) -> Result<Vec<Node>, String> {
let mut v = Vec::new();
space(t);
loop {
match if let Some(o) = e {
Some(t.peek_n(o.len()))
} else {
None
} {
Some(None) => break,
Some(Some(x)) if Some(&*x) == e => {
let Some(o) = e else {
return err_fmt!("e == None");
};
t.skip(o.len() - 1);
break;
}
Some(Some(_)) => v.push(expr(t)?),
None => v.push(expr(t)?),
};
match t.peek() {
Some(';') => {
t.inc();
space(t);
continue;
}
Some(_) | None => break,
}
}
space(t);
Ok(v)
}
pub fn exprs(t: &mut Tape) -> Result<Vec<Node>, String> {
_exprs(t, None)
}
pub fn exprs_til(t: &mut Tape, d: &str) -> Result<Vec<Node>, String> {
_exprs(t, Some(d))
}
pub fn txt(t: &mut Tape) -> Result<Vec<Node>, String> {
let mut v = Vec::new();
let mut s = String::new();
let p = |s: &str| Node::monad("<", Node::from_str(s));
while let Some(c) = t.peek() {
if let Some('?') = t.peek_next()
&& c == '<'
{
t.skip(2);
v.push(p(&s));
s = String::new();
let mut r = exprs_til(t, "?>")?;
v.append(&mut r);
if let Some(p) = t.last_n(2)
&& p != "?>"
{
return err_parse!(t, "txt: expected ?> to close cal block, got {p}");
}
t.skip(1);
} else {
match t.next() {
Some(x) => s.push(x),
None => (),
}
}
match t.peek() {
Some(_) => (),
None => v.push(p(&s)),
};
}
Ok(v)
}
pub fn parse<T>(x: T) -> Result<Vec<Node>, String>
where
String: From<T>,
{
let mut t = Tape::new(x);
let r = exprs(&mut t)?;
if t.idx != t.src.len() {
err_parse!(
t,
"unexpected char after expression {}: {}",
match r.last() {
Some(x) => x.clone(),
None => Node::empty(),
},
match t.peek() {
Some(c) => c.to_string(),
None => "end of input".to_string(),
}
)
} else {
Ok(r)
}
}
pub fn parse_txt<T>(x: T) -> Result<Vec<Node>, String>
where
String: From<T>,
{
let mut t = Tape::new(x);
txt(&mut t)
}
#[cfg(test)]
mod tests {
use crate::p::{parse, parse_txt, Node};
use std::borrow::Cow;
macro_rules! assert_eq_pretty {
($x:expr, $y:expr) => {{
let f = |x: &[Node]| {
x.into_iter().map(|x| format!("{x}\n")).collect::<String>()
};
let x = $x.clone();
let y = $y.clone();
println!("x: {}\ny: {}", f(&x), f(&y));
assert_eq!(x.clone(), y.clone());
}};
}
#[test]
fn _txt() {
let x = parse_txt(
r#"<p>this is text <? a:"this is cal";
<< ($1),(," "),a;
?></p>"#,
)
.unwrap();
assert_eq_pretty!(
x,
vec![
Node::monad("<", Node::from_str("<p>this is text ")),
Node::dyad(
":",
Node::X(Cow::Borrowed("a")),
Node::from_str("this is cal")
),
Node::monad(
"<<",
Node::dyad(
",",
Node::monad("$", Node::I(1)),
Node::dyad(
",",
Node::monad(",", Node::C(' ')),
Node::X(Cow::Borrowed("a"))
)
)
),
Node::monad("<", Node::from_str("</p>")),
]
);
}
#[test]
fn int() {
let x = parse("1234").unwrap();
assert_eq!(x, vec![Node::I(1234)]);
}
#[test]
fn float() {
let x = parse("12.34").unwrap();
assert_eq!(x, vec![Node::F(12.34)]);
}
#[test]
fn name() {
for (x, y) in [
("abc", Node::X(Cow::Borrowed("abc"))),
("abc::def", Node::X(Cow::Borrowed("abc::def"))),
("abc::def::ghi", Node::X(Cow::Borrowed("abc::def::ghi"))),
]
.into_iter()
{
assert_eq!(parse(x), Ok(vec![y]));
}
}
#[test]
fn chars() {
for (x, y) in [
(r#""a""#, Node::C('a')),
(
r#""abc""#,
Node::A("abc".chars().map(|x| Node::C(x)).collect()),
),
(
r#""abc\ndef""#,
Node::A("abc\ndef".chars().map(|x| Node::C(x)).collect()),
),
]
.into_iter()
{
assert_eq!(parse(x), Ok(vec![y]));
}
}
#[test]
fn exprs() {
assert_eq!(
parse("1;2;3"),
Ok([1, 2, 3].iter().map(|x| Node::I(*x)).collect())
);
}
#[test]
fn parens() {
for (x, y) in [
("(1)", Node::I(1)),
(
"(1;2;3)",
Node::A([1, 2, 3].iter().map(|x| Node::I(*x)).collect()),
),
]
.into_iter()
{
assert_eq!(parse(x), Ok(vec![y]));
}
}
#[test]
fn monads() {
let n = Box::new(Node::I(1));
for (x, y) in [
(
"!1",
Node::M {
verb: Cow::Borrowed("!"),
rhs: Some(n.clone()),
},
),
(
"->1",
Node::M {
verb: Cow::Borrowed("->"),
rhs: Some(n.clone()),
},
),
(
"!> @1",
Node::M {
verb: Cow::Borrowed("!>"),
rhs: Some(Box::new(Node::M {
verb: Cow::Borrowed("@"),
rhs: Some(n.clone()),
})),
},
),
]
.into_iter()
{
assert_eq!(parse(x), Ok(vec![y]));
}
}
#[test]
fn dyads() {
let n = Box::new(Node::I(1));
for (x, y) in [
(
"1+1",
Node::D {
verb: Cow::Borrowed("+"),
lhs: n.clone(),
rhs: Some(n.clone()),
},
),
(
"1+=1",
Node::D {
verb: Cow::Borrowed("+="),
lhs: n.clone(),
rhs: Some(n.clone()),
},
),
(
"1+= !1",
Node::D {
verb: Cow::Borrowed("+="),
lhs: n.clone(),
rhs: Some(Box::new(Node::M {
verb: Cow::Borrowed("!"),
rhs: Some(n.clone()),
})),
},
),
(
"{x+1}'1",
Node::D {
verb: Cow::Borrowed("'"),
lhs: Box::new(Node::O(vec![Node::dyad(
"+",
Node::X("x".to_string().into()),
Node::I(1),
)])),
rhs: Some(Box::new(Node::I(1))),
},
),
]
.into_iter()
{
assert_eq!(parse(x), Ok(vec![y]))
}
}
#[test]
fn trains_verbs_etc() {
for (x, y) in [
(
"(+)",
Node::M {
verb: Cow::Borrowed("+"),
rhs: None,
},
),
(
"1+",
Node::D {
verb: Cow::Borrowed("+"),
lhs: Box::new(Node::I(1)),
rhs: None,
},
),
(
"(+=):1",
Node::D {
verb: Cow::Borrowed(":"),
lhs: Box::new(Node::M {
verb: Cow::Borrowed("+="),
rhs: None,
}),
rhs: Some(Box::new(Node::I(1))),
},
),
]
.into_iter()
{
assert_eq!(parse(x), Ok(vec![y]));
}
}
#[test]
fn progn() {
for (x, y) in [
("[1]", Node::P(vec![Node::I(1)])),
(
"[1;2;3]",
Node::P([1, 2, 3].iter().map(|x| Node::I(*x)).collect()),
),
(
"([1];[2;3])",
Node::A(vec![
Node::P(vec![Node::I(1)]),
Node::P(vec![Node::I(2), Node::I(3)]),
]),
),
(
"{[a;b];a+b}",
Node::O(vec![
Node::P(vec![
Node::X("a".to_string().into()),
Node::X("b".to_string().into()),
]),
Node::dyad(
"+",
Node::X("a".to_string().into()),
Node::X("b".to_string().into()),
),
]),
),
]
.into_iter()
{
assert_eq!(parse(x), Ok(vec![y]))
}
}
}