Get basic def working

This commit is contained in:
Richard Feldman 2019-09-29 22:32:02 +03:00
parent f6ecc88d9c
commit fda3d3056a
8 changed files with 323 additions and 152 deletions

View file

@ -948,6 +948,9 @@ fn add_idents_from_pattern<'a>(
(symbol, region.clone()), (symbol, region.clone()),
)); ));
} }
&QualifiedIdentifier(name) => {
panic!("TODO implement QualifiedIdentifier pattern.");
}
&Apply(_) => { &Apply(_) => {
panic!("TODO implement Apply pattern."); panic!("TODO implement Apply pattern.");
// &AppliedVariant(_, ref opt_loc_args) => match opt_loc_args { // &AppliedVariant(_, ref opt_loc_args) => match opt_loc_args {
@ -972,6 +975,7 @@ fn add_idents_from_pattern<'a>(
| &FloatLiteral(_) | &FloatLiteral(_)
| &StrLiteral(_) | &StrLiteral(_)
| &EmptyRecordLiteral | &EmptyRecordLiteral
| &Malformed(_)
| &Underscore => (), | &Underscore => (),
} }
} }
@ -983,6 +987,9 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
Identifier(name) => { Identifier(name) => {
idents.remove(&(Ident::Unqualified(name.to_string()))); idents.remove(&(Ident::Unqualified(name.to_string())));
} }
QualifiedIdentifier(name) => {
panic!("TODO implement QualifiedIdentifier pattern in remove_idents.");
}
Apply(_) => { Apply(_) => {
panic!("TODO implement Apply pattern in remove_idents."); panic!("TODO implement Apply pattern in remove_idents.");
// AppliedVariant(_, Some(loc_args)) => { // AppliedVariant(_, Some(loc_args)) => {
@ -1003,6 +1010,7 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
| FloatLiteral(_) | FloatLiteral(_)
| StrLiteral(_) | StrLiteral(_)
| EmptyRecordLiteral | EmptyRecordLiteral
| Malformed(_)
| Underscore => {} | Underscore => {}
} }
} }

View file

@ -2,6 +2,7 @@ use bumpalo::collections::vec::Vec;
use bumpalo::collections::String; use bumpalo::collections::String;
use bumpalo::Bump; use bumpalo::Bump;
use operator::Operator; use operator::Operator;
use parse::ident::{Ident, MaybeQualified};
use region::{Loc, Region}; use region::{Loc, Region};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -55,7 +56,12 @@ pub enum Expr<'a> {
When(&'a [(Loc<Pattern<'a>>, Loc<Expr<'a>>)]), When(&'a [(Loc<Pattern<'a>>, Loc<Expr<'a>>)]),
Closure(&'a (Vec<'a, Loc<Pattern<'a>>>, Loc<Expr<'a>>)), Closure(&'a (Vec<'a, Loc<Pattern<'a>>>, Loc<Expr<'a>>)),
/// Multiple defs in a row /// Multiple defs in a row
Defs(&'a (Vec<'a, Def<'a>>, Loc<Expr<'a>>)), Defs(
&'a (
Vec<'a, (&'a [CommentOrNewline<'a>], Def<'a>)>,
Loc<Expr<'a>>,
),
),
// Application // Application
/// To apply by name, do Apply(Var(...), ...) /// To apply by name, do Apply(Var(...), ...)
@ -115,6 +121,53 @@ pub enum Pattern<'a> {
// Space // Space
SpaceBefore(&'a Pattern<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a Pattern<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a Pattern<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a Pattern<'a>, &'a [CommentOrNewline<'a>]),
// Malformed
Malformed(&'a str),
QualifiedIdentifier(MaybeQualified<'a, &'a str>),
}
impl<'a> Pattern<'a> {
pub fn from_ident(arena: &'a Bump, ident: Ident<'a>) -> Pattern<'a> {
match ident {
Ident::Var(maybe_qualified) => {
if maybe_qualified.module_parts.is_empty() {
Pattern::Identifier(maybe_qualified.value)
} else {
Pattern::Variant(maybe_qualified.module_parts, maybe_qualified.value)
}
}
Ident::Variant(maybe_qualified) => {
Pattern::Variant(maybe_qualified.module_parts, maybe_qualified.value)
}
Ident::Field(maybe_qualified) => {
let mut buf = String::with_capacity_in(
maybe_qualified.module_parts.len() + maybe_qualified.value.len(),
arena,
);
for part in maybe_qualified.module_parts.iter() {
buf.push_str(part);
buf.push('.');
}
let mut iter = maybe_qualified.value.iter().peekable();
while let Some(part) = iter.next() {
buf.push_str(part);
// If there are more fields to come, add a "."
if iter.peek().is_some() {
buf.push('.');
}
}
Pattern::Malformed(buf.into_bump_str())
}
Ident::AccessorFunction(string) => Pattern::Malformed(string),
Ident::Malformed(string) => Pattern::Malformed(string),
}
}
} }
pub trait Spaceable<'a> { pub trait Spaceable<'a> {
@ -235,6 +288,7 @@ pub enum Attempting {
InterpolatedString, InterpolatedString,
NumberLiteral, NumberLiteral,
UnicodeEscape, UnicodeEscape,
Def,
Expression, Expression,
Module, Module,
Identifier, Identifier,

View file

@ -9,7 +9,7 @@ use parse::parser::{unexpected, unexpected_eof, ParseResult, Parser, State};
/// appear. This way, canonicalization can give more helpful error messages like /// appear. This way, canonicalization can give more helpful error messages like
/// "you can't redefine this variant!" if you wrote `Foo = ...` or /// "you can't redefine this variant!" if you wrote `Foo = ...` or
/// "you can only define unqualified constants" if you wrote `Foo.bar = ...` /// "you can only define unqualified constants" if you wrote `Foo.bar = ...`
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum Ident<'a> { pub enum Ident<'a> {
/// foo or Bar.Baz.foo /// foo or Bar.Baz.foo
Var(MaybeQualified<'a, &'a str>), Var(MaybeQualified<'a, &'a str>),
@ -23,14 +23,56 @@ pub enum Ident<'a> {
Malformed(&'a str), Malformed(&'a str),
} }
impl<'a> Ident<'a> {
pub fn len(&self) -> usize {
use self::Ident::*;
match self {
Var(string) => string.len(),
Variant(string) => string.len(),
Field(string) => string.len(),
AccessorFunction(string) => string.len(),
Malformed(string) => string.len(),
}
}
}
/// An optional qualifier (the `Foo.Bar` in `Foo.Bar.baz`). /// An optional qualifier (the `Foo.Bar` in `Foo.Bar.baz`).
/// If module_parts is empty, this is unqualified. /// If module_parts is empty, this is unqualified.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaybeQualified<'a, Val> { pub struct MaybeQualified<'a, Val> {
pub module_parts: &'a [&'a str], pub module_parts: &'a [&'a str],
pub value: Val, pub value: Val,
} }
impl<'a> MaybeQualified<'a, &'a str> {
pub fn len(&self) -> usize {
let mut answer = self.value.len();
for part in self.module_parts {
answer += part.len();
}
answer
}
}
impl<'a> MaybeQualified<'a, &'a [&'a str]> {
pub fn len(&self) -> usize {
let mut answer = 0;
for module_part in self.module_parts {
answer += module_part.len();
}
for value_part in self.module_parts {
answer += value_part.len();
}
answer
}
}
/// Parse an identifier into a string. /// Parse an identifier into a string.
/// ///
/// This is separate from the `ident` Parser because string interpolation /// This is separate from the `ident` Parser because string interpolation

View file

@ -24,8 +24,10 @@ use bumpalo::collections::String;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use operator::Operator; use operator::Operator;
use parse::ast::{Attempting, Def, Expr, Pattern, Spaceable}; use parse::ast::{Attempting, CommentOrNewline, Def, Expr, Pattern, Spaceable};
use parse::blankspace::{space0, space0_around, space0_before, space1_before}; use parse::blankspace::{
space0, space0_after, space0_around, space0_before, space1, space1_before,
};
use parse::ident::{ident, Ident}; use parse::ident::{ident, Ident};
use parse::number_literal::number_literal; use parse::number_literal::number_literal;
use parse::parser::{ use parse::parser::{
@ -185,19 +187,38 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
/// * A type annotation /// * A type annotation
/// * Both /// * Both
pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> { pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
move |arena, state| panic!("TODO parse a single def") // TODO support type annotations
map_with_arena(
and(
skip_second(
space0_after(loc_closure_param(min_indent), min_indent),
char('='),
),
space0_before(
loc(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent,
),
),
|arena, (loc_pattern, loc_expr)| {
// BodyOnly(Loc<Pattern<'a>>, &'a Loc<Expr<'a>>),
Def::BodyOnly(loc_pattern, arena.alloc(loc_expr))
},
)
} }
/// Same as def() but with space_before1 before each def, because each nested def must /// Same as def() but with space_before1 before each def, because each nested def must
/// have space separating it from the previous def. /// have space separating it from the previous def.
pub fn nested_def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> { pub fn nested_def<'a>(min_indent: u16) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], Def<'a>)> {
then(def(min_indent), move |arena: &'a Bump, state, def_val| { then(
panic!("TODO actually parse the def with space_before1"); and(space1(min_indent), def(min_indent)),
Ok((def_val, state)) move |arena: &'a Bump, state, tuple| {
}) // TODO verify spacing (I think?)
Ok((tuple, state))
},
)
} }
fn parse_def_expr<'a, S>( fn parse_def_expr<'a>(
min_indent: u16, min_indent: u16,
equals_sign_indent: u16, equals_sign_indent: u16,
arena: &'a Bump, arena: &'a Bump,
@ -221,8 +242,11 @@ fn parse_def_expr<'a, S>(
and( and(
// Optionally parse additional defs. // Optionally parse additional defs.
zero_or_more(nested_def(original_indent)), zero_or_more(nested_def(original_indent)),
// Parse the final // Parse the final expression that will be returned
space1_before(
loc(move |arena, state| parse_expr(original_indent + 1, arena, state)), loc(move |arena, state| parse_expr(original_indent + 1, arena, state)),
original_indent + 1,
),
), ),
), ),
move |arena, state, (loc_first_body, (mut defs, loc_ret))| { move |arena, state, (loc_first_body, (mut defs, loc_ret))| {
@ -237,7 +261,7 @@ fn parse_def_expr<'a, S>(
// reorder the first one to the end, because canonicalize will // reorder the first one to the end, because canonicalize will
// re-sort all of these based on dependencies anyway. Only // re-sort all of these based on dependencies anyway. Only
// their regions will ever be visible to the user.) // their regions will ever be visible to the user.)
defs.push(first_def); defs.push((&[], first_def));
Ok((Expr::Defs(arena.alloc((defs, loc_ret))), state)) Ok((Expr::Defs(arena.alloc((defs, loc_ret))), state))
} }
@ -426,7 +450,7 @@ pub fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located
/// 4. The beginning of a type annotation (e.g. `foo :`) /// 4. The beginning of a type annotation (e.g. `foo :`)
/// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else. /// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else.
pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
map_with_arena( then(
and( and(
loc(ident()), loc(ident()),
optional(either( optional(either(
@ -438,32 +462,67 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
// If there aren't any args, there may be a '=' or ':' after it. // If there aren't any args, there may be a '=' or ':' after it.
// (It's a syntax error to write e.g. `foo bar =` - so if there // (It's a syntax error to write e.g. `foo bar =` - so if there
// were any args, there is definitely no need to parse '=' or ':'!) // were any args, there is definitely no need to parse '=' or ':'!)
and(space0(min_indent), either(char('='), char(':'))), and(space0(min_indent), either(equals_with_indent(), char(':'))),
)), )),
), ),
|arena, (loc_ident, opt_extras)| { move |arena, state, (loc_ident, opt_extras)| {
// This appears to be a var, keyword, or function application. // This appears to be a var, keyword, or function application.
match opt_extras { match opt_extras {
Some(Either::First(loc_args)) => { Some(Either::First(loc_args)) => {
let len = loc_ident.value.len();
let loc_expr = Located { let loc_expr = Located {
region: loc_ident.region, region: loc_ident.region,
value: ident_to_expr(loc_ident.value), value: ident_to_expr(loc_ident.value),
}; };
Expr::Apply(arena.alloc((loc_expr, loc_args))) Ok((Expr::Apply(arena.alloc((loc_expr, loc_args))), state))
} }
Some(Either::Second((_space_list, Either::First(())))) => { Some(Either::Second((_space_list, Either::First(indent)))) => {
panic!("TODO handle def, making sure not to drop comments!"); let value: Pattern<'a> = Pattern::from_ident(arena, loc_ident.value);
let region = loc_ident.region;
let loc_pattern = Located { region, value };
parse_def_expr(min_indent, indent, arena, state, loc_pattern)
} }
Some(Either::Second((_space_list, Either::Second(())))) => { Some(Either::Second((_space_list, Either::Second(())))) => {
panic!("TODO handle annotation, making sure not to drop comments!"); panic!("TODO handle annotation, making sure not to drop comments!");
} }
None => ident_to_expr(loc_ident.value), None => {
let ident = loc_ident.value.clone();
let len = ident.len();
Ok((ident_to_expr(ident), state))
}
} }
}, },
) )
} }
pub fn equals_with_indent<'a>() -> impl Parser<'a, u16> {
move |_arena, state: State<'a>| {
let mut iter = state.input.chars();
match iter.next() {
Some(ch) if ch == '=' => {
match iter.peekable().peek() {
// The '=' must not be followed by another `=` or `>`
Some(next_ch) if next_ch != &'=' && next_ch != &'>' => {
Ok((state.indent_col, state.advance_without_indenting(1)?))
}
Some(next_ch) => Err(unexpected(*next_ch, 0, state, Attempting::Def)),
None => Err(unexpected_eof(
1,
Attempting::Def,
state.advance_without_indenting(1)?,
)),
}
}
Some(ch) => Err(unexpected(ch, 0, state, Attempting::Def)),
None => Err(unexpected_eof(0, Attempting::Def, state)),
}
}
}
fn ident_to_expr<'a>(src: Ident<'a>) -> Expr<'a> { fn ident_to_expr<'a>(src: Ident<'a>) -> Expr<'a> {
match src { match src {
Ident::Var(info) => Expr::Var(info.module_parts, info.value), Ident::Var(info) => Expr::Var(info.module_parts, info.value),

View file

@ -209,31 +209,6 @@ where
} }
} }
#[cfg(not(debug_assertions))]
pub fn map_with_arena<'a, P, F, Before, After>(parser: P, transform: F) -> impl Parser<'a, After>
where
P: Parser<'a, Before>,
F: Fn(&'a Bump, Before) -> After,
{
map_with_arena_impl(parser, transform)
}
#[inline(always)]
pub fn map_with_arena_impl<'a, P, F, Before, After>(
parser: P,
transform: F,
) -> impl Parser<'a, After>
where
P: Parser<'a, Before>,
F: Fn(&'a Bump, Before) -> After,
{
move |arena, state| {
parser
.parse(arena, state)
.map(|(output, next_state)| (transform(arena, output), next_state))
}
}
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
pub fn attempt<'a, P, Val>(attempting: Attempting, parser: P) -> impl Parser<'a, Val> pub fn attempt<'a, P, Val>(attempting: Attempting, parser: P) -> impl Parser<'a, Val>
where where
@ -1114,6 +1089,28 @@ where
BoxedParser::new(and_impl(p1, p2)) BoxedParser::new(and_impl(p1, p2))
} }
#[cfg(not(debug_assertions))]
pub fn map_with_arena<'a, P, F, Before, After>(parser: P, transform: F) -> impl Parser<'a, After>
where
P: Parser<'a, Before>,
F: Fn(&'a Bump, Before) -> After,
{
map_with_arena_impl(parser, transform)
}
#[inline(always)]
fn map_with_arena_impl<'a, P, F, Before, After>(parser: P, transform: F) -> impl Parser<'a, After>
where
P: Parser<'a, Before>,
F: Fn(&'a Bump, Before) -> After,
{
move |arena, state| {
parser
.parse(arena, state)
.map(|(output, next_state)| (transform(arena, output), next_state))
}
}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub fn map_with_arena<'a, P, F, Before, After>(parser: P, transform: F) -> BoxedParser<'a, After> pub fn map_with_arena<'a, P, F, Before, After>(parser: P, transform: F) -> BoxedParser<'a, After>
where where

View file

@ -38,8 +38,8 @@ impl fmt::Debug for Region {
} else { } else {
write!( write!(
f, f,
"|L {}, C {} - L {}, C {}|", "|L {}-{}, C {}-{}|",
self.start_line, self.start_col, self.end_line, self.end_col, self.start_line, self.end_line, self.start_col, self.end_col,
) )
} }
} }

View file

@ -312,125 +312,125 @@ mod test_infer {
// DEF // DEF
#[test] // #[test]
fn def_empty_record() { // fn def_empty_record() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
foo = {} // foo = {}
foo // foo
"# // "#
), // ),
"{}", // "{}",
); // );
} // }
#[test] // #[test]
fn def_string() { // fn def_string() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
str = "thing" // str = "thing"
str // str
"# // "#
), // ),
"Str", // "Str",
); // );
} // }
#[test] // #[test]
fn def_1_arg_closure() { // fn def_1_arg_closure() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
fn = \_ -> {} // fn = \_ -> {}
fn // fn
"# // "#
), // ),
"* -> {}", // "* -> {}",
); // );
} // }
#[test] // #[test]
fn def_2_arg_closure() { // fn def_2_arg_closure() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
func = \_ _ -> 42 // func = \_ _ -> 42
func // func
"# // "#
), // ),
"*, * -> Int", // "*, * -> Int",
); // );
} // }
#[test] // #[test]
fn def_3_arg_closure() { // fn def_3_arg_closure() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
f = \_ _ _ -> "test!" // f = \_ _ _ -> "test!"
f // f
"# // "#
), // ),
"*, *, * -> String", // "*, *, * -> String",
); // );
} // }
#[test] // #[test]
fn def_multiple_functions() { // fn def_multiple_functions() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
a = \_ _ _ -> "test!" // a = \_ _ _ -> "test!"
b = a // b = a
b // b
"# // "#
), // ),
"*, *, * -> String", // "*, *, * -> String",
); // );
} // }
#[test] // #[test]
fn def_multiple_strings() { // fn def_multiple_strings() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
a = "test!" // a = "test!"
b = a // b = a
b // b
"# // "#
), // ),
"Str", // "Str",
); // );
} // }
#[test] // #[test]
fn def_multiple_nums() { // fn def_multiple_nums() {
infer_eq( // infer_eq(
indoc!( // indoc!(
r#" // r#"
c = b // c = b
b = a // b = a
a = 42 // a = 42
c // c
"# // "#
), // ),
"Int", // "Int",
); // );
} // }
// // CALLING FUNCTIONS // // CALLING FUNCTIONS

View file

@ -21,7 +21,7 @@ mod test_parse {
use roc::parse::ast::CommentOrNewline::*; use roc::parse::ast::CommentOrNewline::*;
use roc::parse::ast::Expr::{self, *}; use roc::parse::ast::Expr::{self, *};
use roc::parse::ast::Pattern::*; use roc::parse::ast::Pattern::*;
use roc::parse::ast::{Attempting, Spaceable}; use roc::parse::ast::{Attempting, Def, Spaceable};
use roc::parse::parser::{Fail, FailReason}; use roc::parse::parser::{Fail, FailReason};
use roc::region::{Located, Region}; use roc::region::{Located, Region};
use std::{f64, i64}; use std::{f64, i64};
@ -635,15 +635,26 @@ mod test_parse {
#[test] #[test]
fn basic_def() { fn basic_def() {
let arena = Bump::new();
let newlines = bumpalo::vec![in &arena; Newline, Newline];
let def = Def::BodyOnly(
Located::new(0, 0, 0, 1, Identifier("x")),
arena.alloc(Located::new(0, 0, 2, 3, Int("5"))),
);
let defs = bumpalo::vec![in &arena; (Vec::new_in(&arena).into_bump_slice(), def)];
let ret = Expr::SpaceBefore(arena.alloc(Int("42")), newlines.into_bump_slice());
let loc_ret = Located::new(2, 2, 0, 2, ret);
let expected = Defs(arena.alloc((defs, loc_ret)));
assert_parses_to( assert_parses_to(
indoc!( indoc!(
r#" r#"
x = 5 x=5
42 42
"# "#
), ),
Str(""), expected,
); );
} }