mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Incorporate Located, reorganize operator
This commit is contained in:
parent
38a4ac5c5c
commit
3651151f12
7 changed files with 501 additions and 339 deletions
27
src/expr.rs
27
src/expr.rs
|
@ -1,3 +1,7 @@
|
||||||
|
|
||||||
|
use operator::Operator;
|
||||||
|
use region::{Located, Region};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
// Literals
|
// Literals
|
||||||
|
@ -5,32 +9,31 @@ pub enum Expr {
|
||||||
Frac(i64, i64),
|
Frac(i64, i64),
|
||||||
EmptyStr,
|
EmptyStr,
|
||||||
Str(String),
|
Str(String),
|
||||||
InterpolatedStr(Vec<(String, Ident)>, String),
|
InterpolatedStr(Vec<(String, Located<Ident>)>, String),
|
||||||
Char(char),
|
Char(char),
|
||||||
|
|
||||||
Var(Ident),
|
Var(Ident),
|
||||||
Assign(Pattern, Box<Expr>, Box<Expr>),
|
Assign(Located<Pattern>, Box<Located<Expr>>, Box<Located<Expr>>),
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
CallByName(Ident, Vec<Expr>),
|
CallByName(Ident, Vec<Located<Expr>>),
|
||||||
Apply(Box<Expr>, Vec<Expr>),
|
Apply(Box<Located<Expr>>, Vec<Located<Expr>>),
|
||||||
Operator(Box<Expr>, Operator, Box<Expr>),
|
Operator(Box<Located<Expr>>, Located<Operator>, Box<Located<Expr>>),
|
||||||
Closure(Vec<Pattern>, Box<Expr>),
|
Closure(Vec<Located<Pattern>>, Box<Located<Expr>>),
|
||||||
|
|
||||||
// Sum Types
|
// Sum Types
|
||||||
ApplyVariant(String, Option<Vec<Expr>>),
|
ApplyVariant(String, Option<Vec<Located<Expr>>>),
|
||||||
|
|
||||||
// Product Types
|
// Product Types
|
||||||
EmptyRecord,
|
EmptyRecord,
|
||||||
|
|
||||||
// Conditionals
|
// Conditionals
|
||||||
If(Box<Expr>, Box<Expr>, Box<Expr>),
|
If(Box<Located<Expr>>, Box<Located<Expr>>, Box<Located<Expr>>),
|
||||||
Case(Box<Expr>, Vec<(Pattern, Box<Expr>)>),
|
Case(Box<Located<Expr>>, Vec<(Located<Pattern>, Box<Located<Expr>>)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Ident = String;
|
pub type Ident = String;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Pattern {
|
pub enum Pattern {
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
|
@ -42,8 +45,4 @@ pub enum Pattern {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub enum Operator {
|
|
||||||
Plus, Minus, Star, Slash, DoubleSlash,
|
|
||||||
Equals, LessThan, GreaterThan, LessThanOrEq, GreaterThanOrEq
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ pub mod expr;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod parse_state;
|
pub mod parse_state;
|
||||||
// pub mod eval;
|
// pub mod eval;
|
||||||
|
pub mod operator;
|
||||||
|
pub mod region;
|
||||||
pub mod fast_fraction;
|
pub mod fast_fraction;
|
||||||
// mod ena;
|
// mod ena;
|
||||||
|
|
||||||
|
|
72
src/operator.rs
Normal file
72
src/operator.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use self::Operator::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Operator {
|
||||||
|
// highest precedence
|
||||||
|
Caret,
|
||||||
|
Star, Slash, DoubleSlash, Percent,
|
||||||
|
Plus, Minus,
|
||||||
|
Equals, LessThan, GreaterThan, LessThanOrEq, GreaterThanOrEq,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
LeftPizza, RightPizza
|
||||||
|
// lowest precedence
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum Associativity {
|
||||||
|
/// left-associative operators:
|
||||||
|
///
|
||||||
|
/// arithmetic: * / // % + -
|
||||||
|
/// application: |>
|
||||||
|
LeftAssociative,
|
||||||
|
|
||||||
|
/// right-associative operators:
|
||||||
|
///
|
||||||
|
/// exponentiation: ^
|
||||||
|
/// boolean: && ||
|
||||||
|
/// application: <|
|
||||||
|
RightAssociative,
|
||||||
|
|
||||||
|
/// non-associative operators:
|
||||||
|
///
|
||||||
|
/// comparison: == > >= < <=
|
||||||
|
NonAssociative
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operator {
|
||||||
|
pub fn associativity(&self) -> Associativity {
|
||||||
|
use self::Associativity::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
RightPizza | Star | Slash | DoubleSlash | Percent | Plus | Minus => LeftAssociative,
|
||||||
|
LeftPizza | And | Or | Caret => RightAssociative,
|
||||||
|
Equals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => NonAssociative
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn precedence(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Caret => 7,
|
||||||
|
Star | Slash | DoubleSlash | Percent => 6,
|
||||||
|
Plus | Minus => 5,
|
||||||
|
Equals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
|
||||||
|
And => 3,
|
||||||
|
Or => 2,
|
||||||
|
LeftPizza | RightPizza => 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Operator {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Operator {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.precedence().cmp(&other.precedence())
|
||||||
|
}
|
||||||
|
}
|
139
src/parse.rs
139
src/parse.rs
|
@ -1,5 +1,6 @@
|
||||||
use operator::Operator;
|
use operator::Operator;
|
||||||
use expr::{Expr, Pattern, Ident};
|
use expr::{Expr, Pattern, Ident};
|
||||||
|
use region::{Located, Region};
|
||||||
|
|
||||||
use std::char;
|
use std::char;
|
||||||
use parse_state::{IndentablePosition};
|
use parse_state::{IndentablePosition};
|
||||||
|
@ -28,7 +29,37 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
spaces().with(expr_body(0)).skip(whitespace_or_eof())
|
spaces().with(expr_body(0)).skip(whitespace_or_eof())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indentation<I>() -> impl Parser<Input = I, Output = i32>
|
fn located<I, O, P>(parser: P) -> impl Parser<Input = I, Output = Located<O>>
|
||||||
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
|
I::Error: ParseError<I::Item, I::Range, I::Position>,
|
||||||
|
I: Positioned,
|
||||||
|
P: Parser<Input = I, Output = O>
|
||||||
|
{
|
||||||
|
attempt(position().and(parser))
|
||||||
|
// TODO uncommenting this and trying to use its value triggers what appears to be a rustc bug.
|
||||||
|
//
|
||||||
|
// rustc gives this error:
|
||||||
|
// error: reached the recursion limit while instantiating `<combine::combinator::Many<combine::combinator::Sink, combine::combinator::Ignore<combine::combinator::Token<combine::easy::Stream<combine::stream::state::State<&str, parse_state::IndentablePosition>>>>> as combine::Parser>::parse_mode::<combine::parser::FirstMode>`
|
||||||
|
//
|
||||||
|
// I haven't been able to figure out how to implement this another way
|
||||||
|
// that actually compiles. So for now, we set the end equal to the start
|
||||||
|
// and will have to make do without that info.
|
||||||
|
//
|
||||||
|
// .and(position())
|
||||||
|
.map(|( start, val )| {
|
||||||
|
let end = start;
|
||||||
|
|
||||||
|
Located::new(val, Region {
|
||||||
|
start_line: start.line,
|
||||||
|
start_col: start.column,
|
||||||
|
|
||||||
|
end_line: end.line,
|
||||||
|
end_col: end.column
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn indentation<I>() -> impl Parser<Input = I, Output = u32>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position> ,
|
I::Error: ParseError<I::Item, I::Range, I::Position> ,
|
||||||
I: Positioned
|
I: Positioned
|
||||||
|
@ -93,19 +124,19 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
.with(value(()))
|
.with(value(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indented_whitespaces<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()>
|
fn indented_whitespaces<I>(min_indent: u32) -> impl Parser<Input = I, Output = ()>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position> {
|
I::Error: ParseError<I::Item, I::Range, I::Position> {
|
||||||
skip_many(skipped_indented_whitespace_char(min_indent))
|
skip_many(skipped_indented_whitespace_char(min_indent))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indented_whitespaces1<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()>
|
fn indented_whitespaces1<I>(min_indent: u32) -> impl Parser<Input = I, Output = ()>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position> {
|
I::Error: ParseError<I::Item, I::Range, I::Position> {
|
||||||
skip_many1(skipped_indented_whitespace_char(min_indent))
|
skip_many1(skipped_indented_whitespace_char(min_indent))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skipped_indented_whitespace_char<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()>
|
fn skipped_indented_whitespace_char<I>(min_indent: u32) -> impl Parser<Input = I, Output = ()>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position> {
|
I::Error: ParseError<I::Item, I::Range, I::Position> {
|
||||||
choice((
|
choice((
|
||||||
|
@ -148,7 +179,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
/// This is separate from expr_body for the sake of function application,
|
/// This is separate from expr_body for the sake of function application,
|
||||||
/// so it can stop parsing when it reaches an operator (since they have
|
/// so it can stop parsing when it reaches an operator (since they have
|
||||||
/// higher precedence.)
|
/// higher precedence.)
|
||||||
fn expr_body_without_operators<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
fn expr_body_without_operators<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
|
@ -157,10 +188,10 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
|
|
||||||
parser! {
|
parser! {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn expr_body_without_operators_[I](min_indent_ref: i32)(I) -> Expr
|
fn expr_body_without_operators_[I](min_indent_ref: u32)(I) -> Expr
|
||||||
where [ I: Stream<Item = char, Position = IndentablePosition> ]
|
where [ I: Stream<Item = char, Position = IndentablePosition> ]
|
||||||
{
|
{
|
||||||
// TODO figure out why min_indent_ref has the type &mut i32
|
// TODO figure out why min_indent_ref has the type &mut u32
|
||||||
let min_indent = *min_indent_ref;
|
let min_indent = *min_indent_ref;
|
||||||
|
|
||||||
choice((
|
choice((
|
||||||
|
@ -180,7 +211,7 @@ parser! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_body<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
fn expr_body<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
|
@ -190,13 +221,13 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
// This macro allows recursive parsers
|
// This macro allows recursive parsers
|
||||||
parser! {
|
parser! {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn expr_body_[I](min_indent_ref: i32)(I) -> Expr
|
fn expr_body_[I](min_indent_ref: u32)(I) -> Expr
|
||||||
where [ I: Stream<Item = char, Position = IndentablePosition> ]
|
where [ I: Stream<Item = char, Position = IndentablePosition> ]
|
||||||
{
|
{
|
||||||
// TODO figure out why min_indent_ref has the type &mut i32
|
// TODO figure out why min_indent_ref has the type &mut u32
|
||||||
let min_indent = *min_indent_ref;
|
let min_indent = *min_indent_ref;
|
||||||
|
|
||||||
expr_body_without_operators(min_indent)
|
located(expr_body_without_operators(min_indent))
|
||||||
.and(
|
.and(
|
||||||
// Optionally follow the expression with an operator,
|
// Optionally follow the expression with an operator,
|
||||||
//
|
//
|
||||||
|
@ -205,15 +236,15 @@ parser! {
|
||||||
optional(
|
optional(
|
||||||
attempt(
|
attempt(
|
||||||
indented_whitespaces(min_indent)
|
indented_whitespaces(min_indent)
|
||||||
.with(operator())
|
.with(located(operator()))
|
||||||
.skip(whitespace())
|
.skip(whitespace())
|
||||||
.skip(indented_whitespaces(min_indent))
|
.skip(indented_whitespaces(min_indent))
|
||||||
.and(expr_body(min_indent))
|
.and(located(expr_body(min_indent)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).map(|(expr1, opt_op)| {
|
).map(|(expr1, opt_op)| {
|
||||||
match opt_op {
|
match opt_op {
|
||||||
None => expr1,
|
None => expr1.value,
|
||||||
Some((op, expr2)) => {
|
Some((op, expr2)) => {
|
||||||
Expr::Operator(Box::new(expr1), op, Box::new(expr2))
|
Expr::Operator(Box::new(expr1), op, Box::new(expr2))
|
||||||
},
|
},
|
||||||
|
@ -222,16 +253,16 @@ parser! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn if_expr<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn if_expr<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
attempt(string("if").skip(indented_whitespaces1(min_indent)))
|
attempt(string("if").skip(indented_whitespaces1(min_indent)))
|
||||||
.with(expr_body(min_indent)).skip(indented_whitespaces1(min_indent))
|
.with(located(expr_body(min_indent))).skip(indented_whitespaces1(min_indent))
|
||||||
.skip(string("then")).skip(indented_whitespaces1(min_indent))
|
.skip(string("then")).skip(indented_whitespaces1(min_indent))
|
||||||
.and(expr_body(min_indent)).skip(indented_whitespaces1(min_indent))
|
.and(located(expr_body(min_indent))).skip(indented_whitespaces1(min_indent))
|
||||||
.skip(string("else")).skip(indented_whitespaces1(min_indent))
|
.skip(string("else")).skip(indented_whitespaces1(min_indent))
|
||||||
.and(expr_body(min_indent))
|
.and(located(expr_body(min_indent)))
|
||||||
.map(|((conditional, then_branch), else_branch)|
|
.map(|((conditional, then_branch), else_branch)|
|
||||||
Expr::If(
|
Expr::If(
|
||||||
Box::new(conditional),
|
Box::new(conditional),
|
||||||
|
@ -241,21 +272,21 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn case_expr<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn case_expr<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
attempt(string("case").skip(indented_whitespaces1(min_indent)))
|
attempt(string("case").skip(indented_whitespaces1(min_indent)))
|
||||||
.with(expr_body(min_indent))
|
.with(located(expr_body(min_indent)))
|
||||||
.and(
|
.and(
|
||||||
many::<Vec<_>, _>(
|
many::<Vec<_>, _>(
|
||||||
attempt(
|
attempt(
|
||||||
skip_many(indented_whitespaces1(min_indent))
|
skip_many(indented_whitespaces1(min_indent))
|
||||||
.with(string("when").skip(indented_whitespaces1(min_indent)))
|
.with(string("when").skip(indented_whitespaces1(min_indent)))
|
||||||
)
|
)
|
||||||
.with(pattern(min_indent)).skip(indented_whitespaces1(min_indent))
|
.with(located(pattern(min_indent))).skip(indented_whitespaces1(min_indent))
|
||||||
.skip(string("then")).skip(indented_whitespaces1(min_indent))
|
.skip(string("then")).skip(indented_whitespaces1(min_indent))
|
||||||
.and(expr_body(min_indent).map(|expr| Box::new(expr)))
|
.and(located(expr_body(min_indent)).map(|expr| Box::new(expr)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.map(|(conditional, branches)|
|
.map(|(conditional, branches)|
|
||||||
|
@ -268,13 +299,13 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parenthetical_expr<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn parenthetical_expr<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
between(char('('), char(')'),
|
between(char('('), char(')'),
|
||||||
indented_whitespaces(min_indent)
|
indented_whitespaces(min_indent)
|
||||||
.with(expr_body(min_indent))
|
.with(located(expr_body(min_indent)))
|
||||||
.skip(indented_whitespaces(min_indent))
|
.skip(indented_whitespaces(min_indent))
|
||||||
).and(
|
).and(
|
||||||
// Parenthetical expressions can optionally be followed by
|
// Parenthetical expressions can optionally be followed by
|
||||||
|
@ -283,26 +314,26 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
optional(
|
optional(
|
||||||
attempt(apply_args(min_indent))
|
attempt(apply_args(min_indent))
|
||||||
)
|
)
|
||||||
).map(|(expr, opt_args): (Expr, Option<Vec<Expr>>)|
|
).map(|(located_expr, opt_args): (Located<Expr>, Option<Vec<Located<Expr>>>)|
|
||||||
match opt_args {
|
match opt_args {
|
||||||
None => expr,
|
None => located_expr.value,
|
||||||
Some(args) => Expr::Apply(Box::new(expr), args)
|
Some(args) => Expr::Apply(Box::new(located_expr), args)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn function_arg<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
fn function_arg<I>(min_indent: u32) -> impl Parser<Input = I, Output = Located<Expr>>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
// Don't parse operators, because they have a higher
|
// Don't parse operators, because they have a higher
|
||||||
// precedence than function application. If we see one,
|
// precedence than function application. If we see one,
|
||||||
// we're done!
|
// we're done!
|
||||||
expr_body_without_operators(min_indent)
|
located(expr_body_without_operators(min_indent))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_args<I>(min_indent: i32) -> impl Parser<Input = I, Output = Vec<Expr>>
|
pub fn apply_args<I>(min_indent: u32) -> impl Parser<Input = I, Output = Vec<Located<Expr>>>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
|
@ -346,17 +377,19 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
char('-').map(|_| Operator::Minus),
|
char('-').map(|_| Operator::Minus),
|
||||||
char('*').map(|_| Operator::Star),
|
char('*').map(|_| Operator::Star),
|
||||||
char('/').map(|_| Operator::Slash),
|
char('/').map(|_| Operator::Slash),
|
||||||
|
char('^').map(|_| Operator::Caret),
|
||||||
|
char('%').map(|_| Operator::Percent),
|
||||||
char('<').map(|_| Operator::LessThan),
|
char('<').map(|_| Operator::LessThan),
|
||||||
char('>').map(|_| Operator::GreaterThan),
|
char('>').map(|_| Operator::GreaterThan),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn let_expr<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn let_expr<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
attempt(
|
attempt(
|
||||||
pattern(min_indent).and(indentation())
|
located(pattern(min_indent)).and(indentation())
|
||||||
.skip(whitespace())
|
.skip(whitespace())
|
||||||
.and(
|
.and(
|
||||||
char('=').with(indentation())
|
char('=').with(indentation())
|
||||||
|
@ -372,9 +405,9 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
} else if equals_sign_indent < original_indent /* `<` because '=' should be same indent or greater */ {
|
} else if equals_sign_indent < original_indent /* `<` because '=' should be same indent or greater */ {
|
||||||
unexpected_any("the = in this declaration seems outdented").left()
|
unexpected_any("the = in this declaration seems outdented").left()
|
||||||
} else {
|
} else {
|
||||||
expr_body(original_indent + 1 /* declaration body must be indented relative to original decl */)
|
located(expr_body(original_indent + 1 /* declaration body must be indented relative to original decl */))
|
||||||
.skip(whitespace1())
|
.skip(whitespace1())
|
||||||
.and(expr_body(original_indent).and(indentation()))
|
.and(located(expr_body(original_indent)).and(indentation()))
|
||||||
.then(move |(var_expr, (in_expr, in_expr_indent))| {
|
.then(move |(var_expr, (in_expr, in_expr_indent))| {
|
||||||
if in_expr_indent != original_indent {
|
if in_expr_indent != original_indent {
|
||||||
unexpected_any("the return expression was indented differently from the original declaration").left()
|
unexpected_any("the return expression was indented differently from the original declaration").left()
|
||||||
|
@ -386,12 +419,12 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn func_or_var<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn func_or_var<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
ident().and(optional(attempt(apply_args(min_indent))))
|
ident().and(optional(attempt(apply_args(min_indent))))
|
||||||
.map(|(name, opt_args): (String, Option<Vec<Expr>>)| {
|
.map(|(name, opt_args): (String, Option<Vec<Located<Expr>>>)| {
|
||||||
// Use optional(sep_by1()) over sep_by() to avoid
|
// Use optional(sep_by1()) over sep_by() to avoid
|
||||||
// allocating a Vec in the common case where this is a var
|
// allocating a Vec in the common case where this is a var
|
||||||
match opt_args {
|
match opt_args {
|
||||||
|
@ -404,7 +437,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
/// Closure *without* parens around the args
|
/// Closure *without* parens around the args
|
||||||
///
|
///
|
||||||
/// e.g. (x, y -> stuff) as opposed to (x, y) -> stuff
|
/// e.g. (x, y -> stuff) as opposed to (x, y) -> stuff
|
||||||
pub fn enclosed_closure<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn enclosed_closure<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
|
@ -412,14 +445,14 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
char('(')
|
char('(')
|
||||||
.with(
|
.with(
|
||||||
sep_by1(
|
sep_by1(
|
||||||
pattern(min_indent),
|
located(pattern(min_indent)),
|
||||||
char(',').skip(indented_whitespaces(min_indent))
|
char(',').skip(indented_whitespaces(min_indent))
|
||||||
))
|
))
|
||||||
.skip(indented_whitespaces1(min_indent))
|
.skip(indented_whitespaces1(min_indent))
|
||||||
.skip(string("->"))
|
.skip(string("->"))
|
||||||
.skip(indented_whitespaces1(min_indent))
|
.skip(indented_whitespaces1(min_indent))
|
||||||
)
|
)
|
||||||
.and(expr_body(min_indent))
|
.and(located(expr_body(min_indent)))
|
||||||
.skip(char(')'))
|
.skip(char(')'))
|
||||||
.map(|(patterns, closure_body)| {
|
.map(|(patterns, closure_body)| {
|
||||||
Expr::Closure(patterns, Box::new(closure_body))
|
Expr::Closure(patterns, Box::new(closure_body))
|
||||||
|
@ -429,21 +462,21 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
/// Closure with parens around the args
|
/// Closure with parens around the args
|
||||||
///
|
///
|
||||||
/// e.g. (x, y) -> stuff as opposde to (x, y -> stuff)
|
/// e.g. (x, y) -> stuff as opposde to (x, y -> stuff)
|
||||||
pub fn closure<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn closure<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
attempt(
|
attempt(
|
||||||
between(char('('), char(')'),
|
between(char('('), char(')'),
|
||||||
sep_by1(
|
sep_by1(
|
||||||
pattern(min_indent),
|
located(pattern(min_indent)),
|
||||||
char(',').skip(indented_whitespaces(min_indent))
|
char(',').skip(indented_whitespaces(min_indent))
|
||||||
))
|
))
|
||||||
.skip(indented_whitespaces1(min_indent))
|
.skip(indented_whitespaces1(min_indent))
|
||||||
.skip(string("->"))
|
.skip(string("->"))
|
||||||
.skip(indented_whitespaces1(min_indent))
|
.skip(indented_whitespaces1(min_indent))
|
||||||
)
|
)
|
||||||
.and(expr_body(min_indent))
|
.and(located(expr_body(min_indent)))
|
||||||
.map(|(patterns, closure_body)| {
|
.map(|(patterns, closure_body)| {
|
||||||
Expr::Closure(patterns, Box::new(closure_body))
|
Expr::Closure(patterns, Box::new(closure_body))
|
||||||
})
|
})
|
||||||
|
@ -451,7 +484,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
|
|
||||||
parser! {
|
parser! {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn pattern[I](min_indent_ref: i32)(I) -> Pattern
|
fn pattern[I](min_indent_ref: u32)(I) -> Pattern
|
||||||
where [ I: Stream<Item = char, Position = IndentablePosition> ]
|
where [ I: Stream<Item = char, Position = IndentablePosition> ]
|
||||||
{
|
{
|
||||||
let min_indent = *min_indent_ref;
|
let min_indent = *min_indent_ref;
|
||||||
|
@ -466,18 +499,18 @@ parser! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_variant<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
pub fn apply_variant<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
attempt(variant_name())
|
attempt(variant_name())
|
||||||
.and(optional(attempt(apply_args(min_indent))))
|
.and(optional(attempt(apply_args(min_indent))))
|
||||||
.map(|(name, opt_args): (String, Option<Vec<Expr>>)|
|
.map(|(name, opt_args): (String, Option<Vec<Located<Expr>>>)|
|
||||||
Expr::ApplyVariant(name, opt_args)
|
Expr::ApplyVariant(name, opt_args)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn match_variant<I>(min_indent: i32) -> impl Parser<Input = I, Output = Pattern>
|
pub fn match_variant<I>(min_indent: u32) -> impl Parser<Input = I, Output = Pattern>
|
||||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
|
@ -542,32 +575,32 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
{
|
{
|
||||||
between(char('"'), char('"'),
|
between(char('"'), char('"'),
|
||||||
many::<Vec<(Ident, String)>, _>(
|
many::<Vec<(String, Located<Ident>)>, _>(
|
||||||
choice((
|
choice((
|
||||||
// Handle the edge cases where the interpolation happens
|
// Handle the edge cases where the interpolation happens
|
||||||
// to be at the very beginning of the string literal,
|
// to be at the very beginning of the string literal,
|
||||||
// or immediately following the previous interpolation.
|
// or immediately following the previous interpolation.
|
||||||
attempt(string("\\("))
|
attempt(string("\\("))
|
||||||
.with(value("".to_string()))
|
.with(value("".to_string()))
|
||||||
.and(ident().skip(char(')'))),
|
.and(located(ident()).skip(char(')'))),
|
||||||
|
|
||||||
// Parse a bunch of non-interpolated characters until we hit \(
|
// Parse a bunch of non-interpolated characters until we hit \(
|
||||||
many1::<Vec<char>, _>(string_body())
|
many1::<Vec<char>, _>(string_body())
|
||||||
.map(|chars: Vec<char>| chars.into_iter().collect::<String>())
|
.map(|chars: Vec<char>| chars.into_iter().collect::<String>())
|
||||||
.and(choice((
|
.and(choice((
|
||||||
attempt(string("\\(").with(ident().skip(char(')')))),
|
attempt(string("\\(").with(located(ident()).skip(char(')')))),
|
||||||
// If we never encountered \( then we hit the end of
|
// If we never encountered \( then we hit the end of
|
||||||
// the string literal. Use empty Ident here because
|
// the string literal. Use empty Ident here because
|
||||||
// we're going to pop this Ident off the array anyhow.
|
// we're going to pop this Ident off the array anyhow.
|
||||||
value("".to_string())
|
located(value("".to_string()))
|
||||||
))),
|
))),
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
.map(|mut pairs| {
|
.map(|mut pairs| {
|
||||||
match pairs.pop() {
|
match pairs.pop() {
|
||||||
None => Expr::EmptyStr,
|
None => Expr::EmptyStr,
|
||||||
Some(( trailing_str, name )) => {
|
Some(( trailing_str, located_name )) => {
|
||||||
if name.is_empty() {
|
if located_name.value.is_empty() {
|
||||||
if pairs.is_empty() {
|
if pairs.is_empty() {
|
||||||
// We didn't find any interpolation at all. This is a string literal!
|
// We didn't find any interpolation at all. This is a string literal!
|
||||||
Expr::Str(trailing_str.to_string())
|
Expr::Str(trailing_str.to_string())
|
||||||
|
@ -579,7 +612,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
// happened to occur at the very end of the literal.
|
// happened to occur at the very end of the literal.
|
||||||
|
|
||||||
// Put the tuple back.
|
// Put the tuple back.
|
||||||
pairs.push(( trailing_str, name ));
|
pairs.push(( trailing_str, located_name ));
|
||||||
|
|
||||||
Expr::InterpolatedStr(pairs, "".to_string())
|
Expr::InterpolatedStr(pairs, "".to_string())
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,12 @@ use combine::stream::state::{Positioner, RangePositioner};
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub struct IndentablePosition {
|
pub struct IndentablePosition {
|
||||||
/// Current line of the input
|
/// Current line of the input
|
||||||
pub line: i32,
|
pub line: u32,
|
||||||
/// Current column of the input
|
/// Current column of the input
|
||||||
pub column: i32,
|
pub column: u32,
|
||||||
|
|
||||||
/// Current indentation level, in columns (so no indent is col 1 - this saves an arithmetic operation.)
|
/// Current indentation level, in columns (so no indent is col 1 - this saves an arithmetic operation.)
|
||||||
pub indent_col : i32,
|
pub indent_col : u32,
|
||||||
|
|
||||||
// true at the beginning of each line, then false after encountering the first nonspace char.
|
// true at the beginning of each line, then false after encountering the first nonspace char.
|
||||||
pub is_indenting: bool,
|
pub is_indenting: bool,
|
||||||
|
|
20
src/region.rs
Normal file
20
src/region.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Region {
|
||||||
|
pub start_line: u32,
|
||||||
|
pub start_col: u32,
|
||||||
|
|
||||||
|
pub end_line: u32,
|
||||||
|
pub end_col: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct Located<T> {
|
||||||
|
pub region: Region,
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Located<T> {
|
||||||
|
pub fn new(value: T, region: Region) -> Located<T> {
|
||||||
|
Located { value, region }
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue