use lexington::{Any,Lexer,Match,Matcher,Scanner,Token,Within}; use lexington::{ShiftReduceParser,ShiftReduceRule}; /// A simple definition of the components of an S-expression. #[derive(Copy,Clone,Debug,PartialEq)] enum Kind { WhiteSpace, LeftBrace, RightBrace, Symbol } /// A simple definition of an S-expression. #[derive(Clone,Debug,PartialEq)] enum SExp<'a> { Symbol(&'a str), List(Vec>) } /// Parse an input string into an S-expression, or produce an error. fn parse<'a>(input: &'a str) -> Result { // [ \n\t]+ let whitespace = Any([' ','\n','\t']).one_or_more(); // [0..9a..zA..Z_]+ let symbol = Within('0'..='9').or(Within('a'..='z')) .or(Within('A'..='Z')).or('_').one_or_more(); // Construct scanner let scanner = Match(whitespace,Kind::WhiteSpace) .and_match(symbol,Kind::Symbol) .and_match('(',Kind::LeftBrace) .and_match(')',Kind::RightBrace); // Construct lexer. let lexer = Lexer::new(input,scanner); // Rule for combining s-expressions let reduction_rule = |mut l:SExp<'a>,r:SExp<'a>| { match &mut l { SExp::List(vs) => { vs.push(r); Ok(l) } _ => Err(()) } }; // Rule for parsing strings into numbers ShiftReduceParser::new() .apply(reduction_rule) .terminate(Kind::Symbol,|tok| SExp::Symbol(&input[tok.range()])) .skip(Kind::WhiteSpace) .open(Kind::LeftBrace, SExp::List(Vec::new())) .close(Kind::RightBrace) .parse(lexer) } /// macro_rules! list{ ( $($a:expr),* ) => { { let mut v = Vec::new(); $( v.push(SExp::Symbol($a)); )* SExp::List(v) } } } /// Identifiers differents kinds of error. #[derive(Debug,PartialEq)] enum ParseError { UnexpectedEndOfFile, UnexpectedToken(Kind) } fn check_ok(input: &str, expected: SExp) { let actual = parse(input).unwrap(); // Check go what was expected assert_eq!(actual,expected); } fn check_err(input: &str, _expecting: ParseError) { let actual = parse(input).unwrap_err(); // Check what we got // assert_eq!(actual,expecting); assert_eq!(actual,()); } use SExp::*; #[test] fn lisp_01() { check_ok("x",Symbol("x")); } #[test] fn lisp_02() { check_ok("901",Symbol("901")); } #[test] fn lisp_03() { check_ok("0x01",Symbol("0x01")); } #[test] fn lisp_04() { check_ok("_hello",Symbol("_hello")); } #[test] fn lisp_05() { check_ok(" xyz",Symbol("xyz")); } #[test] fn lisp_06() { check_ok("()",list![]); } #[test] fn lisp_07() { check_ok("(x)",list!["x"]); } #[test] fn lisp_08() { check_ok("( x)",list!["x"]); } #[test] fn lisp_09() { check_ok("(x )",list!["x"]); } #[test] fn lisp_10() { check_ok("(x y)",list!["x","y"]); } #[test] fn lisp_11() { check_ok("(())",List(vec![List(vec![])])); } #[test] fn lisp_12() { check_ok("((x))",List(vec![list!["x"]])); } #[test] fn lisp_13() { check_ok("(x (y))",List(vec![Symbol("x"),List(vec![Symbol("y")])])); } #[test] fn lisp_14() { check_ok("((x) y)",List(vec![List(vec![Symbol("x")]),Symbol("y")])); } #[test] fn lisp_15() { check_err("",ParseError::UnexpectedEndOfFile); } #[test] fn lisp_16() { check_err(" ",ParseError::UnexpectedEndOfFile); } #[test] fn lisp_17() { check_err("(",ParseError::UnexpectedEndOfFile); } #[test] fn lisp_18() { check_err("(",ParseError::UnexpectedEndOfFile); } #[test] fn lisp_19() { check_err("(()",ParseError::UnexpectedEndOfFile); } #[test] fn lisp_20() { check_err(")",ParseError::UnexpectedToken(Kind::RightBrace)); }