mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Made IdentProblem a thing
This commit is contained in:
parent
9bedae3ba6
commit
2a67b3fcd3
3 changed files with 36 additions and 22 deletions
|
@ -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)]
|
||||||
|
|
52
src/parse.rs
52
src/parse.rs
|
@ -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>
|
||||||
|
|
|
@ -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))
|
||||||
),
|
),
|
||||||
"")
|
"")
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue