Made IdentProblem a thing

This commit is contained in:
Richard Feldman 2019-05-04 00:05:55 -04:00
parent 9bedae3ba6
commit 2a67b3fcd3
3 changed files with 36 additions and 22 deletions

View file

@ -13,6 +13,8 @@ pub enum Expr {
Func(String, Box<Expr>), Func(String, Box<Expr>),
Apply(Box<Expr>, Box<Expr>), Apply(Box<Expr>, Box<Expr>),
Operator(Box<Expr>, Operator, Box<Expr>), Operator(Box<Expr>, Operator, Box<Expr>),
SyntaxProblem(String),
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]

View file

@ -5,8 +5,8 @@ use std::char;
use combine::parser::char::{char, space, spaces, digit, hex_digit, HexDigit, alpha_num}; use combine::parser::char::{char, space, spaces, digit, hex_digit, HexDigit, alpha_num};
use combine::parser::repeat::{many, count_min_max}; use combine::parser::repeat::{many, count_min_max};
use combine::parser::item::{any, satisfy, satisfy_map, value}; use combine::parser::item::{any, satisfy_map, value};
use combine::{choice, many1, parser, Parser, optional, between, unexpected_any, look_ahead}; use combine::{choice, many1, parser, Parser, optional, between, unexpected_any};
use combine::error::{Consumed, ParseError}; use combine::error::{Consumed, ParseError};
use combine::stream::{Stream}; use combine::stream::{Stream};
@ -40,7 +40,7 @@ parser! {
optional( optional(
operator() operator()
.skip(spaces()) .skip(spaces())
.and(expr_body()) .and(expr())
) )
).skip(spaces()).map(|(v1, opt_op)| { ).skip(spaces()).map(|(v1, opt_op)| {
match opt_op { match opt_op {
@ -58,17 +58,13 @@ where I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position> I::Error: ParseError<I::Item, I::Range, I::Position>
{ {
between(char('('), char(')'), between(char('('), char(')'),
// NOTE: It's critical to use expr_body() instead of expr() here! spaces().with(expr()).skip(spaces())
//
// expr() must check for eof() at the end, and it's impossible to have
// an eof() followed by a ')' character!
spaces().with(expr_body()).skip(spaces())
).and( ).and(
// Parenthetical expressions can optionally be followed by // Parenthetical expressions can optionally be followed by
// whitespace and an expr, meaning this is function application! // whitespace and an expr, meaning this is function application!
optional( optional(
many1::<Vec<_>, _>(space()) many1::<Vec<_>, _>(space())
.with(expr_body()) .with(expr())
) )
).map(|(expr1, opt_expr2)| ).map(|(expr1, opt_expr2)|
match opt_expr2 { match opt_expr2 {
@ -99,27 +95,43 @@ where I: Stream<Item = char>,
ident() ident()
.and(optional( .and(optional(
many1::<Vec<_>, _>(space()) many1::<Vec<_>, _>(space())
.with(expr_body()) .with(expr())
)).map(|(str, opt_arg)| )).map(|pair|
match opt_arg { match pair {
Some(arg) => Expr::Func(str, Box::new(arg)), ( Ok(str), Some(arg) ) => Expr::Func(str, Box::new(arg)),
None => Expr::Var(str), ( Ok(str), None ) => Expr::Var(str),
( Err(_ident_problem), _ ) => Expr::SyntaxProblem("TODO put _ident_problem here".to_owned())
} }
) )
} }
pub fn ident<I>() -> impl Parser<Input = I, Output = String> pub enum IdentProblem {
InvalidFirstChar(String),
ReservedKeyword(String),
}
pub fn ident<I>() -> impl Parser<Input = I, Output = Result<String, IdentProblem>>
where I: Stream<Item = char>, where I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position> I::Error: ParseError<I::Item, I::Range, I::Position>
{ {
// TODO fail if this is a reserved keyword like "if"
// Identifiers must begin with a lowercase letter, but can have any // Identifiers must begin with a lowercase letter, but can have any
// combination of letters or numbers afterwards. // combination of letters or numbers afterwards.
// No underscores, dashes, or apostrophes. // No underscores, dashes, or apostrophes.
look_ahead(satisfy(|first_char:char| first_char.is_lowercase())) many::<Vec<_>, _>(alpha_num())
.with(many::<Vec<_>, _>(alpha_num())) .map(|chars: Vec<char>| {
.map(|chars| chars.into_iter().collect()) let valid_start_char = chars[0].is_lowercase();
let ident_str:String = chars.into_iter().collect();
if valid_start_char {
if ident_str == "if" {
Err(IdentProblem::ReservedKeyword(ident_str.to_owned()))
} else {
Ok(ident_str)
}
} else {
Err(IdentProblem::ReservedKeyword(ident_str.to_owned()))
}
})
} }
pub fn string_literal<I>() -> impl Parser<Input = I, Output = Expr> pub fn string_literal<I>() -> impl Parser<Input = I, Output = Expr>

View file

@ -428,12 +428,12 @@ mod tests {
assert_eq!( assert_eq!(
// TODO why does this fail?! // TODO why does this fail?!
parse::expr().parse("(5) + 123"), parse::expr().parse("(5) + 1998"),
Ok(( Ok((
Operator( Operator(
Box::new(Int(5)), Box::new(Int(5)),
Plus, Plus,
Box::new(Int(123)) Box::new(Int(1998))
), ),
"") "")
) )