diff options
author | Nadrieril | 2019-03-06 23:48:32 +0100 |
---|---|---|
committer | Nadrieril | 2019-03-06 23:48:32 +0100 |
commit | e3813e7d4e3450704c1213fd6cdff7c801ccbc34 (patch) | |
tree | 86b248b5e1c8a0053a1a447b5ff4e38c267fcb3e /dhall_core/src | |
parent | 54382cd107d1befd6015f8232716158a20db44a4 (diff) |
Finally get rid of old parser
Diffstat (limited to 'dhall_core/src')
-rw-r--r-- | dhall_core/src/grammar.lalrpop | 164 | ||||
-rw-r--r-- | dhall_core/src/grammar_util.rs | 5 | ||||
-rw-r--r-- | dhall_core/src/lexer.rs | 394 | ||||
-rw-r--r-- | dhall_core/src/lib.rs | 3 | ||||
-rw-r--r-- | dhall_core/src/parser.rs | 53 |
5 files changed, 22 insertions, 597 deletions
diff --git a/dhall_core/src/grammar.lalrpop b/dhall_core/src/grammar.lalrpop deleted file mode 100644 index 1ffe2ff..0000000 --- a/dhall_core/src/grammar.lalrpop +++ /dev/null @@ -1,164 +0,0 @@ -use std::collections::BTreeMap; -use std::iter; -use std::iter::FromIterator; - -use crate::core; -use crate::core::bx; -use crate::core::Expr::*; -use crate::core::Builtin; -use crate::core::Builtin::*; -use crate::core::BinOp::*; -use crate::grammar_util::*; -use crate::lexer::*; - -grammar<'input>; - -extern { - type Location = usize; - type Error = LexicalError; - - enum Tok<'input> { - Pi => Tok::Pi, - Lambda => Tok::Lambda, - Combine => Tok::Combine, - "->" => Tok::Arrow, - - Int => Tok::Integer(<isize>), - Nat => Tok::Natural(<usize>), - Text => Tok::Text(<String>), - Bool => Tok::Bool(<bool>), - Label => Tok::Identifier(<&'input str>), - Const => Tok::Const(<core::Const>), - Let => Tok::Keyword(Keyword::Let), - In => Tok::Keyword(Keyword::In), - If => Tok::Keyword(Keyword::If), - Then => Tok::Keyword(Keyword::Then), - Else => Tok::Keyword(Keyword::Else), - List => Tok::ListLike(ListLike::List), - Optional => Tok::ListLike(ListLike::Optional), - Builtin => Tok::Builtin(<Builtin>), - - "{" => Tok::BraceL, - "}" => Tok::BraceR, - "[" => Tok::BracketL, - "]" => Tok::BracketR, - "(" => Tok::ParenL, - ")" => Tok::ParenR, - "&&" => Tok::BoolAnd, - "||" => Tok::BoolOr, - "==" => Tok::CompareEQ, - "!=" => Tok::CompareNE, - "++" => Tok::Append, - "*" => Tok::Times, - "+" => Tok::Plus, - "," => Tok::Comma, - "." => Tok::Dot, - ":" => Tok::Ascription, - "=" => Tok::Equals, - } -} - -pub Expr: BoxExpr<'input> = { // exprA - ExprB, -}; - -ExprB: BoxExpr<'input> = { - Lambda "(" <Label> ":" <Expr> ")" "->" <ExprB> => bx(Lam(<>)), - Pi "(" <Label> ":" <Expr> ")" "->" <ExprB> => bx(Pi(<>)), - If <Expr> Then <ExprB> Else <ExprC> => bx(BoolIf(<>)), - <ExprC> "->" <ExprB> => bx(Pi("_", <>)), - Let <Label> <(":" <Expr>)?> "=" <Expr> In <ExprB> => bx(Let(<>)), - "[" <a:Elems> "]" ":" <b:ListLike> <c:ExprE> => bx(b(Some(c), a)), - <ExprC> ":" <Expr> => bx(Annot(<>)), - ExprC, -}; - -ListLike: ExprListFn<'input> = { - List => ListLit, - Optional => OptionalLit, -}; - -BoolOr: ExprOpFn<'input> = { "||" => (|x,y| BinOp(BoolOr, x, y)) }; -NaturalPlus: ExprOpFn<'input> = { "+" => (|x,y| BinOp(NaturalPlus, x, y)) }; -TextAppend: ExprOpFn<'input> = { "++" => (|x,y| BinOp(TextAppend, x, y)) }; -BoolAnd: ExprOpFn<'input> = { "&&" => (|x,y| BinOp(BoolAnd, x, y)) }; -CombineOp: ExprOpFn<'input> = { Combine => (|x,y| BinOp(Combine, x, y)) }; -NaturalTimes: ExprOpFn<'input> = { "*" => (|x,y| BinOp(NaturalTimes, x, y)) }; -BoolEQ: ExprOpFn<'input> = { "==" => (|x,y| BinOp(BoolEQ, x, y)) }; -BoolNE: ExprOpFn<'input> = { "!=" => (|x,y| BinOp(BoolNE, x, y)) }; - -Tier<NextTier, Op>: BoxExpr<'input> = { - <a:NextTier> <f:Op> <b:Tier<NextTier, Op>> => bx(f(a, b)), - // <b:Tier<NextTier, Op>> <f:Op> <a:NextTier> => bx(f(a, b)), - NextTier, -}; - -ExprC = Tier<ExprC1, BoolOr>; -ExprC1 = Tier<ExprC2, NaturalPlus>; -ExprC2 = Tier<ExprC3, TextAppend>; -ExprC3 = Tier<ExprC4, BoolAnd>; -ExprC4 = Tier<ExprC5, CombineOp>; -ExprC5 = Tier<ExprC6, NaturalTimes>; -ExprC6 = Tier<ExprC7, BoolEQ>; -ExprC7 = Tier<ExprD, BoolNE>; - -ExprD: BoxExpr<'input> = { - <v:(ExprE)+> => { - let mut it = v.into_iter(); - let f = it.next().unwrap(); - it.fold(f, |f, x| bx(App(f, x))) - } -}; - -ExprE: BoxExpr<'input> = { - <a:ExprF> <fields:("." <Label>)*> => { - fields.into_iter().fold(a, |x, f| bx(Field(x, f))) - }, -}; - -ExprF: BoxExpr<'input> = { - Nat => bx(NaturalLit(<>)), - Int => bx(IntegerLit(<>)), - Text => bx(TextLit(<>)), - Label => bx(Var(core::V(<>, 0))), // FIXME support var@n syntax - Const => bx(Const(<>)), - List => bx(Builtin(List)), - Optional => bx(Builtin(Optional)), - Builtin => bx(Builtin(<>)), - Bool => bx(BoolLit(<>)), - Record, - RecordLit, - "(" <Expr> ")", -}; - -SepBy<S, T>: iter::Chain<::std::vec::IntoIter<T>, ::std::option::IntoIter<T>> = { - <v:(<T> S)*> <last:T?> => v.into_iter().chain(last.into_iter()), -}; - -SepBy1<S, T>: iter::Chain<::std::vec::IntoIter<T>, iter::Once<T>> = { - <v:(<T> S)*> <last:T> => v.into_iter().chain(iter::once(last)), -}; - -Elems: Vec<ParsedExpr<'input>> = { - <v:SepBy<",", Expr>> => { - v.into_iter() - .map(|b| *b) - .collect::<Vec<_>>() - } -}; - -RecordLit: BoxExpr<'input> = { - "{" "=" "}" => bx(RecordLit(BTreeMap::new())), - "{" <FieldValues> "}" => bx(RecordLit(BTreeMap::from_iter(<>))), -}; - -Record: BoxExpr<'input> = { - "{" <FieldTypes> "}" => bx(Record(BTreeMap::from_iter(<>))), -}; - -FieldValues = SepBy1<",", Field<"=">>; -FieldTypes = SepBy<",", Field<":">>; - -Field<Sep>: (&'input str, ParsedExpr<'input>) = { - <a:Label> Sep <b:Expr> => (a, *b), -}; diff --git a/dhall_core/src/grammar_util.rs b/dhall_core/src/grammar_util.rs index 82528f4..73f935f 100644 --- a/dhall_core/src/grammar_util.rs +++ b/dhall_core/src/grammar_util.rs @@ -1,7 +1,4 @@ -use crate::core::{Expr, X, Import}; +use crate::core::{Expr, Import, X}; pub type ParsedExpr<'i> = Expr<'i, X, Import>; pub type BoxExpr<'i> = Box<ParsedExpr<'i>>; -pub type ExprOpFn<'i> = fn(BoxExpr<'i>, BoxExpr<'i>) -> ParsedExpr<'i>; -pub type ExprListFn<'i> = - fn(Option<BoxExpr<'i>>, Vec<ParsedExpr<'i>>) -> ParsedExpr<'i>; diff --git a/dhall_core/src/lexer.rs b/dhall_core/src/lexer.rs deleted file mode 100644 index 5fc0f05..0000000 --- a/dhall_core/src/lexer.rs +++ /dev/null @@ -1,394 +0,0 @@ -use nom::*; - -use crate::core::Builtin; -use crate::core::Builtin::*; -use crate::core::Const; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Keyword { - Let, - In, - If, - Then, - Else, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ListLike { - List, - Optional, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Tok<'i> { - Identifier(&'i str), - Keyword(Keyword), - Builtin(Builtin), - ListLike(ListLike), - Const(Const), - Bool(bool), - Integer(isize), - Natural(usize), - Text(String), - - // Symbols - BraceL, - BraceR, - BracketL, - BracketR, - ParenL, - ParenR, - Arrow, - Lambda, - Pi, - Combine, - BoolAnd, - BoolOr, - CompareEQ, - CompareNE, - Append, - Times, - Plus, - Comma, - Dot, - Ascription, - Equals, -} - -#[derive(Debug)] -pub enum LexicalError { - Error(usize, nom::simple_errors::Err<u32>), - Incomplete(nom::Needed), -} - -pub type Spanned<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>; - -#[allow(dead_code)] -fn is_identifier_first_char(c: char) -> bool { - c.is_alphabetic() || c == '_' -} - -fn is_identifier_rest_char(c: char) -> bool { - is_identifier_first_char(c) || c.is_digit(10) || c == '/' -} - -macro_rules! digits { - ($i:expr, $t:tt, $radix:expr) => {{ - let r: nom::IResult<&str, $t> = map_res!( - $i, - take_while1_s!(call!(|c: char| c.is_digit($radix))), - |s| $t::from_str_radix(s, $radix) - ); - r - }}; -} - -named!(natural<&str, usize>, digits!(usize, 10)); -named!(integral<&str, isize>, digits!(isize, 10)); -named!(integer<&str, isize>, alt!( - preceded!(tag!("-"), map!(integral, |i: isize| -i)) | - preceded!(tag!("+"), integral) -)); -named!(boolean<&str, bool>, alt!( - value!(true, tag!("True")) | - value!(false, tag!("False")) -)); - -named!(identifier<&str, &str>, recognize!(preceded!( - take_while1_s!(is_identifier_first_char), - take_while_s!(is_identifier_rest_char)) -)); - -/// Parse an identifier, ensuring a whole identifier is parsed and not just a prefix. -macro_rules! ident_tag { - ($i:expr, $tag:expr) => { - match identifier($i) { - nom::IResult::Done(i, s) => { - if s == $tag { - nom::IResult::Done(i, s) - } else { - nom::IResult::Error(error_position!( - nom::ErrorKind::Tag, - $i - )) - } - } - r => r, - } - }; -} - -fn string_escape_single(c: char) -> Option<&'static str> { - match c { - 'n' => Some("\n"), - 'r' => Some("\r"), - 't' => Some("\t"), - '"' => Some("\""), - '\'' => Some("'"), - '\\' => Some("\\"), - '0' => Some("\0"), - 'a' => Some("\x07"), - 'b' => Some("\x08"), - 'f' => Some("\x0c"), - 'v' => Some("\x0b"), - '&' => Some(""), - _ => None, - } -} - -named!(string_escape_numeric<&str, char>, map_opt!(alt!( - preceded!(tag!("x"), digits!(u32, 16)) | - preceded!(tag!("o"), digits!(u32, 8)) | - digits!(u32, 10) -), ::std::char::from_u32)); - -fn string_lit_inner(input: &str) -> nom::IResult<&str, String> { - use nom::ErrorKind; - use nom::IResult::*; ; - let mut s = String::new(); - let mut cs = input.char_indices().peekable(); - while let Some((i, c)) = cs.next() { - match c { - '"' => return nom::IResult::Done(&input[i..], s), - '\\' => match cs.next() { - Some((_, s)) if s.is_whitespace() => { - while cs.peek().map(|&(_, s)| s.is_whitespace()) - == Some(true) - { - let _ = cs.next(); - } - if cs.next().map(|p| p.1) != Some('\\') { - return Error(error_position!( - ErrorKind::Custom(4 /* FIXME */), - input - )); - } - } - Some((j, ec)) => { - if let Some(esc) = string_escape_single(ec) { - s.push_str(esc); - // FIXME Named ASCII escapes and control character escapes - } else { - match string_escape_numeric(&input[j..]) { - Done(rest, esc) => { - let &(k, _) = cs.peek().unwrap(); - // digits are always single byte ASCII characters - let consumed = input[k..].len() - rest.len(); - for _ in 0..consumed { - let _ = cs.next(); - } - s.push(esc); - } - Incomplete(s) => return Incomplete(s), - Error(e) => return Error(e), - } - } - } - _ => { - return Error(error_position!( - ErrorKind::Custom(5 /* FIXME */), - input - )); - } - }, - _ => s.push(c), - } - } - Error(error_position!(ErrorKind::Custom(3 /* FIXME */), input)) -} - -named!(string_lit<&str, String>, delimited!(tag!("\""), string_lit_inner, tag!("\""))); - -named!(keyword<&str, Keyword>, alt!( - value!(Keyword::Let, ident_tag!("let")) | - value!(Keyword::In, ident_tag!("in")) | - value!(Keyword::If, ident_tag!("if")) | - value!(Keyword::Then, ident_tag!("then")) | - value!(Keyword::Else, ident_tag!("else")) -)); - -named!(type_const<&str, Const>, alt!( - value!(Const::Type, ident_tag!("Type")) | - value!(Const::Kind, ident_tag!("Kind")) -)); - -named!(list_like<&str, ListLike>, alt!( - value!(ListLike::List, ident_tag!("List")) | - value!(ListLike::Optional, ident_tag!("Optional")) -)); - -named!(builtin<&str, Builtin>, alt!( - value!(NaturalFold, ident_tag!("Natural/fold")) | - value!(NaturalBuild, ident_tag!("Natural/build")) | - value!(NaturalIsZero, ident_tag!("Natural/isZero")) | - value!(NaturalEven, ident_tag!("Natural/even")) | - value!(NaturalOdd, ident_tag!("Natural/odd")) | - value!(NaturalShow, ident_tag!("Natural/show")) | - value!(Natural, ident_tag!("Natural")) | - value!(Integer, ident_tag!("Integer")) | - value!(Double, ident_tag!("Double")) | - value!(Text, ident_tag!("Text")) | - value!(ListBuild, ident_tag!("List/build")) | - value!(ListFold, ident_tag!("List/fold")) | - value!(ListLength, ident_tag!("List/length")) | - value!(ListHead, ident_tag!("List/head")) | - value!(ListLast, ident_tag!("List/last")) | - value!(ListIndexed, ident_tag!("List/indexed")) | - value!(ListReverse, ident_tag!("List/reverse")) | - value!(OptionalFold, ident_tag!("Optional/fold")) | - value!(Bool, ident_tag!("Bool")) -)); - -named!(token<&str, Tok>, alt!( - value!(Tok::Pi, ident_tag!("forall")) | - value!(Tok::Pi, tag!("∀")) | - value!(Tok::Lambda, tag!("\\")) | - value!(Tok::Lambda, tag!("λ")) | - value!(Tok::Combine, tag!("/\\")) | - value!(Tok::Combine, tag!("∧")) | - value!(Tok::Arrow, tag!("->")) | - value!(Tok::Arrow, tag!("→")) | - - map!(type_const, Tok::Const) | - map!(boolean, Tok::Bool) | - map!(keyword, Tok::Keyword) | - map!(builtin, Tok::Builtin) | - map!(list_like, Tok::ListLike) | - map!(natural, Tok::Natural) | - map!(integer, Tok::Integer) | - map!(identifier, Tok::Identifier) | - map!(string_lit, Tok::Text) | - - value!(Tok::BraceL, tag!("{")) | - value!(Tok::BraceR, tag!("}")) | - value!(Tok::BracketL, tag!("[")) | - value!(Tok::BracketR, tag!("]")) | - value!(Tok::ParenL, tag!("(")) | - value!(Tok::ParenR, tag!(")")) | - value!(Tok::BoolAnd, tag!("&&")) | - value!(Tok::BoolOr, tag!("||")) | - value!(Tok::CompareEQ, tag!("==")) | - value!(Tok::CompareNE, tag!("!=")) | - value!(Tok::Append, tag!("++")) | - value!(Tok::Times, tag!("*")) | - value!(Tok::Plus, tag!("+")) | - value!(Tok::Comma, tag!(",")) | - value!(Tok::Dot, tag!(".")) | - value!(Tok::Ascription, tag!(":")) | - value!(Tok::Equals, tag!("=")) -)); - -fn find_end(input: &str, ending: &str) -> Option<usize> { - input.find(ending).map(|i| i + ending.len()) -} - -pub struct Lexer<'input> { - input: &'input str, - offset: usize, -} - -impl<'input> Lexer<'input> { - pub fn new(input: &'input str) -> Self { - Lexer { - input: input, - offset: 0, - } - } - - fn current_input(&mut self) -> &'input str { - &self.input[self.offset..] - } - - fn skip_whitespace(&mut self) -> bool { - let input = self.current_input(); - let trimmed = input.trim_start(); - let whitespace_len = input.len() - trimmed.len(); - let skipped = whitespace_len > 0; - if skipped { - // println!("skipped {} whitespace bytes in {}..{}", whitespace_len, self.offset, self.offset + whitespace_len); - self.offset += whitespace_len; - } - skipped - } - - fn skip_comments(&mut self) -> bool { - let input = self.current_input(); - if !input.is_char_boundary(0) || !input.is_char_boundary(2) { - return false; - } - let skip = match &input[0..2] { - "{-" => find_end(input, "-}"), - "--" => find_end(input, "\n"), // Also skips past \r\n (CRLF) - _ => None, - } - .unwrap_or(0); - // println!("skipped {} bytes of comment", skip); - self.offset += skip; - skip != 0 - } - - fn skip_comments_and_whitespace(&mut self) { - while self.skip_whitespace() || self.skip_comments() {} - } -} - -impl<'input> Iterator for Lexer<'input> { - type Item = Spanned<Tok<'input>, usize, LexicalError>; - - fn next(&mut self) -> Option<Self::Item> { - use nom::IResult::*; - self.skip_comments_and_whitespace(); - let input = self.current_input(); - if input.is_empty() { - return None; - } - match token(input) { - Done(rest, t) => { - let parsed_len = input.len() - rest.len(); - //println!("parsed {} bytes => {:?}", parsed_len, t); - let start = self.offset; - self.offset += parsed_len; - Some(Ok((start, t, self.offset))) - } - Error(e) => { - let offset = self.offset; - self.offset = self.input.len(); - Some(Err(LexicalError::Error(offset, e))) - } - Incomplete(needed) => Some(Err(LexicalError::Incomplete(needed))), - } - } -} - -#[test] -fn test_lex() { - use self::Tok::*; - let s = "λ(b : Bool) → b == False"; - let expected = [ - Lambda, - ParenL, - Identifier("b"), - Ascription, - Builtin(crate::core::Builtin::Bool), - ParenR, - Arrow, - Identifier("b"), - CompareEQ, - Bool(false), - ]; - let lexer = Lexer::new(s); - let tokens = lexer.map(|r| r.unwrap().1).collect::<Vec<_>>(); - assert_eq!(&tokens, &expected); - - assert_eq!(string_lit(r#""a\&b""#).to_result(), Ok("ab".to_owned())); - assert_eq!( - string_lit(r#""a\ \b""#).to_result(), - Ok("ab".to_owned()) - ); - assert!(string_lit(r#""a\ b""#).is_err()); - assert_eq!(string_lit(r#""a\nb""#).to_result(), Ok("a\nb".to_owned())); - assert_eq!( - string_lit(r#""\o141\x62\99""#).to_result(), - Ok("abc".to_owned()) - ); -} diff --git a/dhall_core/src/lib.rs b/dhall_core/src/lib.rs index 76f436d..bd53603 100644 --- a/dhall_core/src/lib.rs +++ b/dhall_core/src/lib.rs @@ -3,9 +3,6 @@ pub mod core; pub use crate::core::*; -use lalrpop_util::lalrpop_mod; -lalrpop_mod!(pub grammar); pub mod context; mod grammar_util; -pub mod lexer; pub mod parser; diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs index 09863a2..34327b1 100644 --- a/dhall_core/src/parser.rs +++ b/dhall_core/src/parser.rs @@ -1,24 +1,13 @@ -use std::collections::BTreeMap; -// use itertools::*; -use lalrpop_util; use pest::iterators::Pair; use pest::Parser; +use std::collections::BTreeMap; use std::path::PathBuf; use dhall_parser::{DhallParser, Rule}; use crate::core; use crate::core::*; -use crate::grammar; use crate::grammar_util::{BoxExpr, ParsedExpr}; -use crate::lexer::{Lexer, LexicalError, Tok}; - -pub fn parse_expr_lalrpop( - s: &str, -) -> Result<BoxExpr, lalrpop_util::ParseError<usize, Tok, LexicalError>> { - grammar::ExprParser::new().parse(Lexer::new(s)) - // Ok(bx(Expr::BoolLit(false))) -} pub type ParseError = pest::error::Error<Rule>; @@ -818,7 +807,7 @@ rule!(final_expression<BoxExpr<'a>>; children!(e: expression, _eoi: EOI) => e ); -pub fn parse_expr_pest(s: &str) -> ParseResult<BoxExpr> { +pub fn parse_expr(s: &str) -> ParseResult<BoxExpr> { let pairs = DhallParser::parse(Rule::final_expression, s)?; // Match the only item in the pairs iterator match_iter!(@panic; pairs; (p) => expression(p)) @@ -832,12 +821,12 @@ fn test_parse() { // let expr = r#"{ x = "foo", y = 4 }.x"#; // let expr = r#"(1 + 2) * 3"#; let expr = r#"if True then 1 + 3 * 5 else 2"#; - println!("{:?}", parse_expr_lalrpop(expr)); + println!("{:?}", parse_expr(expr)); use std::thread; // I don't understand why it stack overflows even on tiny expressions... thread::Builder::new() .stack_size(3 * 1024 * 1024) - .spawn(move || match parse_expr_pest(expr) { + .spawn(move || match parse_expr(expr) { Err(e) => { println!("{:?}", e); println!("{}", e); @@ -847,14 +836,14 @@ fn test_parse() { .unwrap() .join() .unwrap(); - // assert_eq!(parse_expr_pest(expr).unwrap(), parse_expr_lalrpop(expr).unwrap()); + // assert_eq!(parse_expr(expr).unwrap(), parse_expr(expr).unwrap()); // assert!(false); - println!("test {:?}", parse_expr_lalrpop("3 + 5 * 10")); - assert!(parse_expr_lalrpop("22").is_ok()); - assert!(parse_expr_lalrpop("(22)").is_ok()); + println!("test {:?}", parse_expr("3 + 5 * 10")); + assert!(parse_expr("22").is_ok()); + assert!(parse_expr("(22)").is_ok()); assert_eq!( - parse_expr_lalrpop("3 + 5 * 10").ok(), + parse_expr("3 + 5 * 10").ok(), Some(Box::new(BinOp( NaturalPlus, Box::new(NaturalLit(3)), @@ -867,7 +856,7 @@ fn test_parse() { ); // The original parser is apparently right-associative assert_eq!( - parse_expr_lalrpop("2 * 3 * 4").ok(), + parse_expr("2 * 3 * 4").ok(), Some(Box::new(BinOp( NaturalTimes, Box::new(NaturalLit(2)), @@ -878,15 +867,15 @@ fn test_parse() { )) ))) ); - assert!(parse_expr_lalrpop("((((22))))").is_ok()); - assert!(parse_expr_lalrpop("((22)").is_err()); - println!("{:?}", parse_expr_lalrpop("\\(b : Bool) -> b == False")); - assert!(parse_expr_lalrpop("\\(b : Bool) -> b == False").is_ok()); - println!("{:?}", parse_expr_lalrpop("foo.bar")); - assert!(parse_expr_lalrpop("foo.bar").is_ok()); - assert!(parse_expr_lalrpop("[] : List Bool").is_ok()); - - // println!("{:?}", parse_expr_lalrpop("< Left = True | Right : Natural >")); - // println!("{:?}", parse_expr_lalrpop(r#""bl${42}ah""#)); - // assert!(parse_expr_lalrpop("< Left = True | Right : Natural >").is_ok()); + assert!(parse_expr("((((22))))").is_ok()); + assert!(parse_expr("((22)").is_err()); + println!("{:?}", parse_expr("\\(b : Bool) -> b == False")); + assert!(parse_expr("\\(b : Bool) -> b == False").is_ok()); + println!("{:?}", parse_expr("foo.bar")); + assert!(parse_expr("foo.bar").is_ok()); + assert!(parse_expr("[] : List Bool").is_ok()); + + // println!("{:?}", parse_expr("< Left = True | Right : Natural >")); + // println!("{:?}", parse_expr(r#""bl${42}ah""#)); + // assert!(parse_expr("< Left = True | Right : Natural >").is_ok()); } |