use lexington::{Lexer,Match,Matcher,Scanner,Within};
use lexington::{ShiftReduceParser,ShiftReduceRule};
use Expr::*;
use PartExpr::*;
#[derive(Copy,Clone,Debug,PartialEq)]
enum Kind {
LeftBrace,
RightBrace,
Plus,
Star,
Number,
Identifier,
Eof
}
#[derive(Clone,Debug,PartialEq)]
enum Expr<'a> {
Int(isize),
Var(&'a str),
Sum(Vec<Expr<'a>>),
Product(Vec<Expr<'a>>),
}
impl<'a> Expr<'a> {
pub fn take(&mut self) -> Expr<'a> {
let mut res : Expr<'a> = Int(0);
std::mem::swap(self,&mut res);
res
}
}
#[derive(Clone,Debug,PartialEq)]
enum PartExpr<'a> {
Empty,
Open(Expr<'a>),
Closed(Expr<'a>)
}
impl<'a> PartExpr<'a> {
fn unwrap(self) -> Expr<'a> {
match self {
Closed(t) => t,
_ => { panic!("cannot unwrap incomplete expression"); }
}
}
fn close(self) -> PartExpr<'a> {
match self {
Open(t) => Closed(t),
_ => self
}
}
fn into_sum(&mut self) -> Result<bool,()> {
match self {
Open(Sum(_)) => Ok(true),
Closed(e) => { *self = Open(Sum(vec![e.take()])); Ok(true)}
_ => Ok(false)
}
}
fn into_product(&mut self) -> Result<bool,()> {
match self {
Open(Product(_)) => Ok(true),
Closed(e) => { *self = Open(Product(vec![e.take()])); Ok(true)}
_ => Ok(false)
}
}
}
fn scanner() -> impl Scanner<Item=char,Token=Kind> {
let number = Within('0'..='9').one_or_more();
let identifier_start = Within('a'..='z')
.or(Within('A'..='Z')).or('_');
let identifier_rest = Within('0'..='9').or(Within('a'..='z'))
.or(Within('A'..='Z')).or('_').zero_or_more();
let identifier = identifier_start.then(identifier_rest);
Match(number,Kind::Number)
.and_match(identifier,Kind::Identifier)
.and_match('+',Kind::Plus)
.and_match('*',Kind::Star)
.and_match('(',Kind::LeftBrace)
.and_match(')',Kind::RightBrace)
.eof(Kind::Eof)
}
fn parse<'a>(input: &'a str) -> Result<Expr<'a>,()> {
let scanner = scanner();
let lexer = Lexer::new(input,scanner);
let reduction_rule = |mut l:PartExpr<'a>,r:PartExpr<'a>| {
match (&mut l,&r) {
(Empty,_) => Ok(r),
(Open(Sum(es)),_) => { es.push(r.unwrap()); Ok(l) }
(Open(Product(es)),_) => { es.push(r.unwrap()); Ok(l) }
(_,_) => todo!("Reducing {l:?} <= {r:?})")
}
};
let st = ShiftReduceParser::new()
.apply(reduction_rule)
.terminate(Kind::Number,|tok| Closed(Int(input[tok.range()].parse().unwrap())))
.terminate(Kind::Identifier,|tok| Closed(Var(&input[tok.range()])))
.update_as(Kind::Plus, |t| t.into_sum())
.update_as(Kind::Star, |t| t.into_product())
.open(Kind::LeftBrace, Empty)
.close_with(Kind::RightBrace, |e| e.close())
.close_with(Kind::Eof, |e| e.close())
.first(Empty)
.parse(lexer)?;
Ok(st.unwrap())
}
fn check_ok(input: &str, expecting: Expr) {
let actual = parse(input).unwrap();
assert_eq!(actual,expecting);
}
#[test]
fn arith_01() {
check_ok("x",Var("x"));
}
#[test]
fn arith_02() {
check_ok("(x)",Var("x"));
}
#[test]
fn arith_03() {
check_ok("123",Int(123));
}
#[test]
fn arith_04() {
check_ok("(123)",Int(123));
}
#[test]
fn arith_05() {
check_ok("x+1",Sum(vec![Var("x"),Int(1)]));
}
#[test]
fn arith_06() {
check_ok("1+x",Sum(vec![Int(1),Var("x")]));
}
#[test]
fn arith_07() {
check_ok("x+y",Sum(vec![Var("x"),Var("y")]));
}
#[test]
fn arith_08() {
check_ok("1+x+y",Sum(vec![Int(1),Var("x"),Var("y")]));
}
#[test]
fn arith_09() {
check_ok("x+2+y",Sum(vec![Var("x"),Int(2),Var("y")]));
}
#[test]
fn arith_10() {
check_ok("x+y+z",Sum(vec![Var("x"),Var("y"),Var("z")]));
}
#[test]
fn arith_11() {
check_ok("x*1",Product(vec![Var("x"),Int(1)]));
}
#[test]
fn arith_12() {
check_ok("1*x",Product(vec![Int(1),Var("x")]));
}
#[test]
fn arith_13() {
check_ok("x*y",Product(vec![Var("x"),Var("y")]));
}
#[test]
fn arith_14() {
check_ok("(x+1)*y",Product(vec![Sum(vec![Var("x"),Int(1)]),Var("y")]));
}
#[test]
fn arith_15() {
check_ok("x*(y+1)",Product(vec![Var("x"),Sum(vec![Var("y"),Int(1)])]));
}