Skip to content

Commit

Permalink
chore: move literals into a separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Feb 24, 2024
1 parent fde3782 commit 2b5df09
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 130 deletions.
4 changes: 3 additions & 1 deletion compiler/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
//! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should
//! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the
//! current parser to try alternative parsers in a `choice` expression.
use self::primitives::{keyword, literal, mutable_reference, variable};
use self::primitives::{keyword, mutable_reference, variable};

use super::{
foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery,
Expand Down Expand Up @@ -51,6 +51,7 @@ use noirc_errors::{Span, Spanned};

mod assertion;
mod function;
mod literals;
mod path;
mod primitives;
mod structs;
Expand All @@ -59,6 +60,7 @@ mod traits;
#[cfg(test)]
mod test_helpers;

use literals::literal;
use path::{maybe_empty_path, path};
use primitives::{dereference, ident, negation, not, nothing, right_shift_operator, token_kind};

Expand Down
157 changes: 157 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/literals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use chumsky::Parser;

use crate::{
parser::NoirParser,
token::{Token, TokenKind},
ExpressionKind,
};

use super::primitives::token_kind;

pub(super) fn literal() -> impl NoirParser<ExpressionKind> {
token_kind(TokenKind::Literal).map(|token| match token {
Token::Int(x) => ExpressionKind::integer(x),
Token::Bool(b) => ExpressionKind::boolean(b),
Token::Str(s) => ExpressionKind::string(s),
Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes),
Token::FmtStr(s) => ExpressionKind::format_string(s),
unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected),
})
}

#[cfg(test)]
mod test {
use super::*;
use crate::parser::parser::{
expression, expression_no_constructors, fresh_statement, term, test_helpers::*,
};
use crate::Literal;

fn expr_to_lit(expr: ExpressionKind) -> Literal {
match expr {
ExpressionKind::Literal(literal) => literal,
_ => unreachable!("expected a literal"),
}
}

#[test]
fn parse_int() {
let int = parse_with(literal(), "5").unwrap();
let hex = parse_with(literal(), "0x05").unwrap();

match (expr_to_lit(int), expr_to_lit(hex)) {
(Literal::Integer(int, false), Literal::Integer(hex, false)) => assert_eq!(int, hex),
_ => unreachable!(),
}
}

#[test]
fn parse_string() {
let expr = parse_with(literal(), r#""hello""#).unwrap();
match expr_to_lit(expr) {
Literal::Str(s) => assert_eq!(s, "hello"),
_ => unreachable!(),
};
}

#[test]
fn parse_bool() {
let expr_true = parse_with(literal(), "true").unwrap();
let expr_false = parse_with(literal(), "false").unwrap();

match (expr_to_lit(expr_true), expr_to_lit(expr_false)) {
(Literal::Bool(t), Literal::Bool(f)) => {
assert!(t);
assert!(!f);
}
_ => unreachable!(),
};
}

#[test]
fn parse_unary() {
parse_all(
term(expression(), expression_no_constructors(expression()), fresh_statement(), true),
vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"],
);
parse_all_failing(
term(expression(), expression_no_constructors(expression()), fresh_statement(), true),
vec!["+hello", "/hello"],
);
}

#[test]
fn parse_raw_string_expr() {
let cases = vec![
Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 },
Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 },
// backslash
Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 },
Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 },
Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 },
Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 },
// escape sequence
Case {
source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##,
expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##,
errors: 0,
},
Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 },
// mismatch - errors:
Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 },
Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 },
// mismatch: short:
Case { source: r##" r"foo"# "##, expect: r#"r"foo""#, errors: 1 },
Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 },
// empty string
Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 },
Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 },
// miscellaneous
Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 },
Case { source: r#" r\"foo\" "#, expect: "plain::r", errors: 1 },
Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 },
// missing 'r' letter
Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 },
Case { source: r#" #"foo" "#, expect: "plain::foo", errors: 2 },
// whitespace
Case { source: r##" r #"foo"# "##, expect: "plain::r", errors: 2 },
Case { source: r##" r# "foo"# "##, expect: "plain::r", errors: 3 },
Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 },
// after identifier
Case { source: r##" bar#"foo"# "##, expect: "plain::bar", errors: 2 },
// nested
Case {
source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###,
expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###,
errors: 0,
},
];

check_cases_with_errors(&cases[..], expression());
}

#[test]
fn parse_raw_string_lit() {
let lit_cases = vec![
Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 },
Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 },
// backslash
Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 },
Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 },
Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 },
Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 },
// escape sequence
Case {
source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##,
expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##,
errors: 0,
},
Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 },
// mismatch - errors:
Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 },
Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 },
];

check_cases_with_errors(&lit_cases[..], literal());
}
}
129 changes: 0 additions & 129 deletions compiler/noirc_frontend/src/parser/parser/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,65 +81,11 @@ pub(super) fn variable() -> impl NoirParser<ExpressionKind> {
path().map(ExpressionKind::Variable)
}

pub(super) fn literal() -> impl NoirParser<ExpressionKind> {
token_kind(TokenKind::Literal).map(|token| match token {
Token::Int(x) => ExpressionKind::integer(x),
Token::Bool(b) => ExpressionKind::boolean(b),
Token::Str(s) => ExpressionKind::string(s),
Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes),
Token::FmtStr(s) => ExpressionKind::format_string(s),
unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected),
})
}

#[cfg(test)]
mod test {
use super::*;
use crate::parser::parser::{
expression, expression_no_constructors, fresh_statement, term, test_helpers::*,
};
use crate::Literal;

fn expr_to_lit(expr: ExpressionKind) -> Literal {
match expr {
ExpressionKind::Literal(literal) => literal,
_ => unreachable!("expected a literal"),
}
}

#[test]
fn parse_int() {
let int = parse_with(literal(), "5").unwrap();
let hex = parse_with(literal(), "0x05").unwrap();

match (expr_to_lit(int), expr_to_lit(hex)) {
(Literal::Integer(int, false), Literal::Integer(hex, false)) => assert_eq!(int, hex),
_ => unreachable!(),
}
}

#[test]
fn parse_string() {
let expr = parse_with(literal(), r#""hello""#).unwrap();
match expr_to_lit(expr) {
Literal::Str(s) => assert_eq!(s, "hello"),
_ => unreachable!(),
};
}

#[test]
fn parse_bool() {
let expr_true = parse_with(literal(), "true").unwrap();
let expr_false = parse_with(literal(), "false").unwrap();

match (expr_to_lit(expr_true), expr_to_lit(expr_false)) {
(Literal::Bool(t), Literal::Bool(f)) => {
assert!(t);
assert!(!f);
}
_ => unreachable!(),
};
}

#[test]
fn parse_unary() {
Expand All @@ -152,79 +98,4 @@ mod test {
vec!["+hello", "/hello"],
);
}

#[test]
fn parse_raw_string_expr() {
let cases = vec![
Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 },
Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 },
// backslash
Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 },
Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 },
Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 },
Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 },
// escape sequence
Case {
source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##,
expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##,
errors: 0,
},
Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 },
// mismatch - errors:
Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 },
Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 },
// mismatch: short:
Case { source: r##" r"foo"# "##, expect: r#"r"foo""#, errors: 1 },
Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 },
// empty string
Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 },
Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 },
// miscellaneous
Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 },
Case { source: r#" r\"foo\" "#, expect: "plain::r", errors: 1 },
Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 },
// missing 'r' letter
Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 },
Case { source: r#" #"foo" "#, expect: "plain::foo", errors: 2 },
// whitespace
Case { source: r##" r #"foo"# "##, expect: "plain::r", errors: 2 },
Case { source: r##" r# "foo"# "##, expect: "plain::r", errors: 3 },
Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 },
// after identifier
Case { source: r##" bar#"foo"# "##, expect: "plain::bar", errors: 2 },
// nested
Case {
source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###,
expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###,
errors: 0,
},
];

check_cases_with_errors(&cases[..], expression());
}

#[test]
fn parse_raw_string_lit() {
let lit_cases = vec![
Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 },
Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 },
// backslash
Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 },
Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 },
Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 },
Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 },
// escape sequence
Case {
source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##,
expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##,
errors: 0,
},
Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 },
// mismatch - errors:
Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 },
Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 },
];

check_cases_with_errors(&lit_cases[..], literal());
}
}

0 comments on commit 2b5df09

Please sign in to comment.