mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Use private/global tags and Access over Field
This commit is contained in:
parent
468e285654
commit
1d18f4cc85
17 changed files with 333 additions and 314 deletions
|
@ -1,21 +1,17 @@
|
||||||
use crate::can::problem::Problem;
|
use crate::can::problem::Problem;
|
||||||
use crate::can::procedure::References;
|
use crate::can::procedure::References;
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::{ImMap, MutMap};
|
use crate::collections::MutMap;
|
||||||
use crate::region::Located;
|
|
||||||
|
|
||||||
/// The canonicalization environment for a particular module.
|
/// The canonicalization environment for a particular module.
|
||||||
pub struct Env {
|
pub struct Env {
|
||||||
/// The module's path. Unqualified references to identifiers and variant names are assumed
|
/// The module's path. Private tags and unqualified references to identifiers
|
||||||
/// to be relative to this path.
|
/// are assumed to be relative to this path.
|
||||||
pub home: Box<str>,
|
pub home: Box<str>,
|
||||||
|
|
||||||
/// Problems we've encountered along the way, which will be reported to the user at the end.
|
/// Problems we've encountered along the way, which will be reported to the user at the end.
|
||||||
pub problems: Vec<Problem>,
|
pub problems: Vec<Problem>,
|
||||||
|
|
||||||
/// Variants either declared in this module, or imported.
|
|
||||||
pub variants: ImMap<Symbol, Located<Box<str>>>,
|
|
||||||
|
|
||||||
/// Closures
|
/// Closures
|
||||||
pub closures: MutMap<Symbol, References>,
|
pub closures: MutMap<Symbol, References>,
|
||||||
|
|
||||||
|
@ -24,10 +20,9 @@ pub struct Env {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Env {
|
impl Env {
|
||||||
pub fn new(home: Box<str>, declared_variants: ImMap<Symbol, Located<Box<str>>>) -> Env {
|
pub fn new(home: Box<str>) -> Env {
|
||||||
Env {
|
Env {
|
||||||
home,
|
home,
|
||||||
variants: declared_variants,
|
|
||||||
problems: Vec::new(),
|
problems: Vec::new(),
|
||||||
closures: MutMap::default(),
|
closures: MutMap::default(),
|
||||||
tailcallable_symbol: None,
|
tailcallable_symbol: None,
|
||||||
|
|
|
@ -22,8 +22,9 @@ pub enum Expr {
|
||||||
FunctionPointer(Variable, Symbol),
|
FunctionPointer(Variable, Symbol),
|
||||||
|
|
||||||
/// Look up exactly one field on a record, e.g. (expr).foo.
|
/// Look up exactly one field on a record, e.g. (expr).foo.
|
||||||
/// Canonicalization will convert chains to single-access, e.g. foo.bar.baz to (foo.bar).baz.
|
Access(Box<Located<Expr>>, Box<str>),
|
||||||
Field(Box<Located<Expr>>, Box<str>),
|
|
||||||
|
Tag(Box<str>, Vec<Expr>),
|
||||||
|
|
||||||
// Pattern Matching
|
// Pattern Matching
|
||||||
/// Case is guaranteed to be exhaustive at this point. (If it wasn't, then
|
/// Case is guaranteed to be exhaustive at this point. (If it wasn't, then
|
||||||
|
@ -40,6 +41,8 @@ pub enum Expr {
|
||||||
Box<Located<Expr>>,
|
Box<Located<Expr>>,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
/// This is *only* for calling functions, not for tag application.
|
||||||
|
/// The Tag variant contains any applied values inside it.
|
||||||
Call(Box<Expr>, Vec<Located<Expr>>, CalledVia),
|
Call(Box<Expr>, Vec<Located<Expr>>, CalledVia),
|
||||||
|
|
||||||
Closure(Symbol, Recursive, Vec<Located<Pattern>>, Box<Located<Expr>>),
|
Closure(Symbol, Recursive, Vec<Located<Pattern>>, Box<Located<Expr>>),
|
||||||
|
|
|
@ -86,7 +86,6 @@ fn canonicalize_def<'a>(
|
||||||
let variable = var_store.fresh();
|
let variable = var_store.fresh();
|
||||||
let expected = Expected::NoExpectation(Type::Variable(variable));
|
let expected = Expected::NoExpectation(Type::Variable(variable));
|
||||||
let declared_idents = ImMap::default(); // TODO FIXME infer this from scope arg
|
let declared_idents = ImMap::default(); // TODO FIXME infer this from scope arg
|
||||||
let declared_variants = ImMap::default(); // TODO get rid of this
|
|
||||||
let name: Box<str> = "TODOfixme".into();
|
let name: Box<str> = "TODOfixme".into();
|
||||||
|
|
||||||
// Desugar operators (convert them to Apply calls, taking into account
|
// Desugar operators (convert them to Apply calls, taking into account
|
||||||
|
@ -102,7 +101,7 @@ fn canonicalize_def<'a>(
|
||||||
// scope_prefix will be "Main.foo$" and its first closure will be named "Main.foo$0"
|
// scope_prefix will be "Main.foo$" and its first closure will be named "Main.foo$0"
|
||||||
let scope_prefix = format!("{}.{}$", home, name).into();
|
let scope_prefix = format!("{}.{}$", home, name).into();
|
||||||
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
||||||
let mut env = Env::new(home, declared_variants.clone());
|
let mut env = Env::new(home);
|
||||||
let (loc_expr, _) = canonicalize_expr(
|
let (loc_expr, _) = canonicalize_expr(
|
||||||
&ImMap::default(),
|
&ImMap::default(),
|
||||||
&mut env,
|
&mut env,
|
||||||
|
@ -165,7 +164,6 @@ pub fn canonicalize_declaration<'a>(
|
||||||
region: Region,
|
region: Region,
|
||||||
loc_expr: Located<ast::Expr<'a>>,
|
loc_expr: Located<ast::Expr<'a>>,
|
||||||
declared_idents: &ImMap<Ident, (Symbol, Region)>,
|
declared_idents: &ImMap<Ident, (Symbol, Region)>,
|
||||||
declared_variants: &ImMap<Symbol, Located<Box<str>>>,
|
|
||||||
expected: Expected<Type>,
|
expected: Expected<Type>,
|
||||||
) -> (Located<Expr>, Output, Vec<Problem>) {
|
) -> (Located<Expr>, Output, Vec<Problem>) {
|
||||||
// Desugar operators (convert them to Apply calls, taking into account
|
// Desugar operators (convert them to Apply calls, taking into account
|
||||||
|
@ -181,7 +179,7 @@ pub fn canonicalize_declaration<'a>(
|
||||||
// scope_prefix will be "Main.foo$" and its first closure will be named "Main.foo$0"
|
// scope_prefix will be "Main.foo$" and its first closure will be named "Main.foo$0"
|
||||||
let scope_prefix = format!("{}.{}$", home, name).into();
|
let scope_prefix = format!("{}.{}$", home, name).into();
|
||||||
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
||||||
let mut env = Env::new(home, declared_variants.clone());
|
let mut env = Env::new(home);
|
||||||
let (loc_expr, output) = canonicalize_expr(
|
let (loc_expr, output) = canonicalize_expr(
|
||||||
&ImMap::default(),
|
&ImMap::default(),
|
||||||
&mut env,
|
&mut env,
|
||||||
|
@ -846,11 +844,11 @@ fn canonicalize_expr(
|
||||||
|
|
||||||
(expr, output)
|
(expr, output)
|
||||||
}
|
}
|
||||||
ast::Expr::Field(_, _)
|
ast::Expr::Access(_, _)
|
||||||
| ast::Expr::QualifiedField(_, _)
|
|
||||||
| ast::Expr::AccessorFunction(_)
|
| ast::Expr::AccessorFunction(_)
|
||||||
| ast::Expr::If(_)
|
| ast::Expr::If(_)
|
||||||
| ast::Expr::Variant(_, _)
|
| ast::Expr::GlobalTag(_)
|
||||||
|
| ast::Expr::PrivateTag(_)
|
||||||
| ast::Expr::MalformedIdent(_)
|
| ast::Expr::MalformedIdent(_)
|
||||||
| ast::Expr::MalformedClosure
|
| ast::Expr::MalformedClosure
|
||||||
| ast::Expr::PrecedenceConflict(_, _, _) => {
|
| ast::Expr::PrecedenceConflict(_, _, _) => {
|
||||||
|
@ -1267,17 +1265,9 @@ fn add_idents_from_pattern<'a>(
|
||||||
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
||||||
add_idents_from_pattern(region, pattern, scope, answer)
|
add_idents_from_pattern(region, pattern, scope, answer)
|
||||||
}
|
}
|
||||||
Variant(_, _)
|
GlobalTag(_) | PrivateTag(_) | IntLiteral(_) | HexIntLiteral(_) | OctalIntLiteral(_)
|
||||||
| IntLiteral(_)
|
| BinaryIntLiteral(_) | FloatLiteral(_) | StrLiteral(_) | BlockStrLiteral(_)
|
||||||
| HexIntLiteral(_)
|
| EmptyRecordLiteral | Malformed(_) | Underscore => (),
|
||||||
| OctalIntLiteral(_)
|
|
||||||
| BinaryIntLiteral(_)
|
|
||||||
| FloatLiteral(_)
|
|
||||||
| StrLiteral(_)
|
|
||||||
| BlockStrLiteral(_)
|
|
||||||
| EmptyRecordLiteral
|
|
||||||
| Malformed(_)
|
|
||||||
| Underscore => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1309,17 +1299,9 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
|
||||||
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
// Ignore the newline/comment info; it doesn't matter in canonicalization.
|
||||||
remove_idents(pattern, idents)
|
remove_idents(pattern, idents)
|
||||||
}
|
}
|
||||||
Variant(_, _)
|
GlobalTag(_) | PrivateTag(_) | IntLiteral(_) | HexIntLiteral(_) | BinaryIntLiteral(_)
|
||||||
| IntLiteral(_)
|
| OctalIntLiteral(_) | FloatLiteral(_) | StrLiteral(_) | BlockStrLiteral(_)
|
||||||
| HexIntLiteral(_)
|
| EmptyRecordLiteral | Malformed(_) | Underscore => {}
|
||||||
| BinaryIntLiteral(_)
|
|
||||||
| OctalIntLiteral(_)
|
|
||||||
| FloatLiteral(_)
|
|
||||||
| StrLiteral(_)
|
|
||||||
| BlockStrLiteral(_)
|
|
||||||
| EmptyRecordLiteral
|
|
||||||
| Malformed(_)
|
|
||||||
| Underscore => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1638,7 +1620,7 @@ fn can_defs<'a>(
|
||||||
// see below: a closure needs a fresh References!
|
// see below: a closure needs a fresh References!
|
||||||
let mut is_closure = false;
|
let mut is_closure = false;
|
||||||
|
|
||||||
// First, make sure we are actually assigning an identifier instead of (for example) a variant.
|
// First, make sure we are actually assigning an identifier instead of (for example) a tag.
|
||||||
//
|
//
|
||||||
// If we're assigning (UserId userId) = ... then this is certainly not a closure declaration,
|
// If we're assigning (UserId userId) = ... then this is certainly not a closure declaration,
|
||||||
// which also implies it's not a self tail call!
|
// which also implies it's not a self tail call!
|
||||||
|
|
|
@ -49,8 +49,6 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
| Nested(Str(_))
|
| Nested(Str(_))
|
||||||
| BlockStr(_)
|
| BlockStr(_)
|
||||||
| Nested(BlockStr(_))
|
| Nested(BlockStr(_))
|
||||||
| QualifiedField(_, _)
|
|
||||||
| Nested(QualifiedField(_, _))
|
|
||||||
| AccessorFunction(_)
|
| AccessorFunction(_)
|
||||||
| Nested(AccessorFunction(_))
|
| Nested(AccessorFunction(_))
|
||||||
| Var(_, _)
|
| Var(_, _)
|
||||||
|
@ -61,13 +59,24 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
| Nested(MalformedClosure)
|
| Nested(MalformedClosure)
|
||||||
| PrecedenceConflict(_, _, _)
|
| PrecedenceConflict(_, _, _)
|
||||||
| Nested(PrecedenceConflict(_, _, _))
|
| Nested(PrecedenceConflict(_, _, _))
|
||||||
| Variant(_, _)
|
| GlobalTag(_)
|
||||||
| Nested(Variant(_, _)) => loc_expr,
|
| Nested(GlobalTag(_))
|
||||||
|
| PrivateTag(_)
|
||||||
|
| Nested(PrivateTag(_)) => loc_expr,
|
||||||
|
|
||||||
Field(sub_expr, paths) | Nested(Field(sub_expr, paths)) => arena.alloc(Located {
|
Access(sub_expr, paths) | Nested(Access(sub_expr, paths)) => {
|
||||||
region: loc_expr.region,
|
let region = loc_expr.region;
|
||||||
value: Field(desugar(arena, sub_expr), paths.clone()),
|
let loc_sub_expr = Located {
|
||||||
}),
|
region,
|
||||||
|
value: Nested(sub_expr),
|
||||||
|
};
|
||||||
|
let value = Access(
|
||||||
|
&desugar(arena, arena.alloc(loc_sub_expr)).value,
|
||||||
|
paths.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.alloc(Located { region, value })
|
||||||
|
}
|
||||||
List(elems) | Nested(List(elems)) => {
|
List(elems) | Nested(List(elems)) => {
|
||||||
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
|
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
|
||||||
|
|
||||||
|
@ -221,7 +230,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
|
|
||||||
branches.push(&*arena.alloc((
|
branches.push(&*arena.alloc((
|
||||||
Located {
|
Located {
|
||||||
value: Pattern::Variant(&[], "False"),
|
value: Pattern::GlobalTag("False"),
|
||||||
region: pattern_region,
|
region: pattern_region,
|
||||||
},
|
},
|
||||||
Located {
|
Located {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::can::problem::Problem;
|
||||||
use crate::can::scope::Scope;
|
use crate::can::scope::Scope;
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::ImMap;
|
use crate::collections::ImMap;
|
||||||
use crate::ident::{Ident, VariantName};
|
use crate::ident::Ident;
|
||||||
use crate::parse::ast;
|
use crate::parse::ast;
|
||||||
use crate::region::{Located, Region};
|
use crate::region::{Located, Region};
|
||||||
use crate::subs::VarStore;
|
use crate::subs::VarStore;
|
||||||
|
@ -19,8 +19,9 @@ use crate::types::{Constraint, PExpected, PatternCategory, Type};
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Pattern {
|
pub enum Pattern {
|
||||||
Identifier(Variable, Symbol),
|
Identifier(Variable, Symbol),
|
||||||
Variant(Variable, Symbol),
|
Tag(Variable, Symbol),
|
||||||
AppliedVariant(Variable, Symbol, Vec<Located<Pattern>>),
|
/// TODO replace regular Tag with this
|
||||||
|
AppliedTag(Variable, Symbol, Vec<Located<Pattern>>),
|
||||||
IntLiteral(i64),
|
IntLiteral(i64),
|
||||||
FloatLiteral(f64),
|
FloatLiteral(f64),
|
||||||
ExactString(Box<str>),
|
ExactString(Box<str>),
|
||||||
|
@ -29,7 +30,6 @@ pub enum Pattern {
|
||||||
|
|
||||||
// Runtime Exceptions
|
// Runtime Exceptions
|
||||||
Shadowed(Located<Ident>),
|
Shadowed(Located<Ident>),
|
||||||
UnrecognizedVariant(Located<VariantName>),
|
|
||||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
UnsupportedPattern(Region),
|
UnsupportedPattern(Region),
|
||||||
}
|
}
|
||||||
|
@ -118,9 +118,10 @@ pub fn canonicalize_pattern<'a>(
|
||||||
let symbol_and_region = (symbol.clone(), region);
|
let symbol_and_region = (symbol.clone(), region);
|
||||||
|
|
||||||
// Add this to both scope.idents *and* shadowable_idents.
|
// Add this to both scope.idents *and* shadowable_idents.
|
||||||
// The latter is relevant when recursively canonicalizing Variant patterns,
|
// The latter is relevant when recursively canonicalizing
|
||||||
// which can bring multiple new idents into scope. For example, it's important
|
// tag application patterns, which can bring multiple
|
||||||
// that we catch (Blah foo foo) as being an example of shadowing.
|
// new idents into scope. For example, it's important that
|
||||||
|
// we catch (Blah foo foo) as being an example of shadowing.
|
||||||
scope
|
scope
|
||||||
.idents
|
.idents
|
||||||
.insert(new_ident.clone(), symbol_and_region.clone());
|
.insert(new_ident.clone(), symbol_and_region.clone());
|
||||||
|
@ -132,56 +133,18 @@ pub fn canonicalize_pattern<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&GlobalTag(name) => {
|
||||||
|
// Canonicalize the tag's name.
|
||||||
|
let symbol = Symbol::from_global_tag(name);
|
||||||
|
|
||||||
// &AppliedVariant((ref loc_name, ref loc_args)) => {
|
Pattern::Tag(var_store.fresh(), symbol)
|
||||||
// // Canonicalize the variant's arguments.
|
|
||||||
// let mut can_args: Vec<Located<Pattern>> = Vec::new();
|
|
||||||
|
|
||||||
// for loc_arg in loc_args {
|
|
||||||
// let loc_can_arg =
|
|
||||||
// canonicalize_pattern(env, subs, constraints, scope, pattern_type, &loc_arg, shadowable_idents);
|
|
||||||
|
|
||||||
// can_args.push(loc_can_arg);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Canonicalize the variant's name.
|
|
||||||
// let symbol = Symbol::from_variant(&loc_name.value, &env.home);
|
|
||||||
|
|
||||||
// if env.variants.contains_key(&symbol) {
|
|
||||||
// // No problems; the qualified variant name was in scope!
|
|
||||||
// Pattern::AppliedVariant(symbol, can_args)
|
|
||||||
// } else {
|
|
||||||
// // We couldn't find the variant name in scope. NAMING PROBLEM!
|
|
||||||
// env.problem(Problem::UnrecognizedVariant(loc_name.clone()));
|
|
||||||
|
|
||||||
// Pattern::UnrecognizedVariant(loc_name.clone())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
&Variant(module_parts, name) => {
|
|
||||||
// Canonicalize the variant's name.
|
|
||||||
let variant = if module_parts.is_empty() {
|
|
||||||
VariantName::Unqualified(name.to_string())
|
|
||||||
} else {
|
|
||||||
VariantName::Qualified(module_parts.to_vec().join("."), name.to_string())
|
|
||||||
};
|
|
||||||
|
|
||||||
let symbol = Symbol::from_variant(&variant, &env.home);
|
|
||||||
|
|
||||||
if env.variants.contains_key(&symbol) {
|
|
||||||
// No problems; the qualified variant name was in scope!
|
|
||||||
Pattern::Variant(var_store.fresh(), symbol)
|
|
||||||
} else {
|
|
||||||
let loc_name = Located {
|
|
||||||
region,
|
|
||||||
value: variant,
|
|
||||||
};
|
|
||||||
// We couldn't find the variant name in scope. NAMING PROBLEM!
|
|
||||||
env.problem(Problem::UnrecognizedVariant(loc_name.clone()));
|
|
||||||
|
|
||||||
Pattern::UnrecognizedVariant(loc_name.clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
&PrivateTag(name) => {
|
||||||
|
// Canonicalize the tag's name.
|
||||||
|
let symbol = Symbol::from_private_tag(&env.home, name);
|
||||||
|
|
||||||
|
Pattern::Tag(var_store.fresh(), symbol)
|
||||||
|
}
|
||||||
&FloatLiteral(ref string) => match pattern_type {
|
&FloatLiteral(ref string) => match pattern_type {
|
||||||
CaseBranch => {
|
CaseBranch => {
|
||||||
let float = finish_parsing_float(string)
|
let float = finish_parsing_float(string)
|
||||||
|
@ -361,7 +324,8 @@ fn add_constraints<'a>(
|
||||||
add_constraints(pattern, scope, region, expected, state)
|
add_constraints(pattern, scope, region, expected, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant(_, _)
|
GlobalTag(_)
|
||||||
|
| PrivateTag(_)
|
||||||
| Apply(_, _)
|
| Apply(_, _)
|
||||||
| RecordDestructure(_)
|
| RecordDestructure(_)
|
||||||
| RecordField(_, _)
|
| RecordField(_, _)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::can::pattern::PatternType;
|
use crate::can::pattern::PatternType;
|
||||||
use crate::ident::{Ident, VariantName};
|
use crate::ident::Ident;
|
||||||
use crate::operator::BinOp;
|
use crate::operator::BinOp;
|
||||||
use crate::region::{Located, Region};
|
use crate::region::{Located, Region};
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ pub enum Problem {
|
||||||
Shadowing(Located<Ident>),
|
Shadowing(Located<Ident>),
|
||||||
UnrecognizedFunctionName(Located<Ident>),
|
UnrecognizedFunctionName(Located<Ident>),
|
||||||
UnrecognizedConstant(Located<Ident>),
|
UnrecognizedConstant(Located<Ident>),
|
||||||
UnrecognizedVariant(Located<VariantName>),
|
|
||||||
UnusedAssignment(Located<Ident>),
|
UnusedAssignment(Located<Ident>),
|
||||||
UnusedArgument(Located<Ident>),
|
UnusedArgument(Located<Ident>),
|
||||||
PrecedenceProblem(PrecedenceProblem),
|
PrecedenceProblem(PrecedenceProblem),
|
||||||
|
@ -29,7 +28,6 @@ pub enum RuntimeError {
|
||||||
InvalidPrecedence(PrecedenceProblem, Region),
|
InvalidPrecedence(PrecedenceProblem, Region),
|
||||||
UnrecognizedFunctionName(Located<Ident>),
|
UnrecognizedFunctionName(Located<Ident>),
|
||||||
UnrecognizedConstant(Located<Ident>),
|
UnrecognizedConstant(Located<Ident>),
|
||||||
UnrecognizedVariant(Located<VariantName>),
|
|
||||||
FloatOutsideRange(Box<str>),
|
FloatOutsideRange(Box<str>),
|
||||||
IntOutsideRange(Box<str>),
|
IntOutsideRange(Box<str>),
|
||||||
InvalidHex(std::num::ParseIntError, Box<str>),
|
InvalidHex(std::num::ParseIntError, Box<str>),
|
||||||
|
|
|
@ -46,7 +46,6 @@ impl Procedure {
|
||||||
pub struct References {
|
pub struct References {
|
||||||
pub locals: ImSet<Symbol>,
|
pub locals: ImSet<Symbol>,
|
||||||
pub globals: ImSet<Symbol>,
|
pub globals: ImSet<Symbol>,
|
||||||
pub variants: ImSet<Symbol>,
|
|
||||||
pub calls: ImSet<Symbol>,
|
pub calls: ImSet<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ impl References {
|
||||||
References {
|
References {
|
||||||
locals: ImSet::default(),
|
locals: ImSet::default(),
|
||||||
globals: ImSet::default(),
|
globals: ImSet::default(),
|
||||||
variants: ImSet::default(),
|
|
||||||
calls: ImSet::default(),
|
calls: ImSet::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +61,6 @@ impl References {
|
||||||
pub fn union(mut self, other: References) -> Self {
|
pub fn union(mut self, other: References) -> Self {
|
||||||
self.locals = self.locals.union(other.locals);
|
self.locals = self.locals.union(other.locals);
|
||||||
self.globals = self.globals.union(other.globals);
|
self.globals = self.globals.union(other.globals);
|
||||||
self.variants = self.variants.union(other.variants);
|
|
||||||
self.calls = self.calls.union(other.calls);
|
self.calls = self.calls.union(other.calls);
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -72,8 +69,4 @@ impl References {
|
||||||
pub fn has_local(&self, symbol: &Symbol) -> bool {
|
pub fn has_local(&self, symbol: &Symbol) -> bool {
|
||||||
self.locals.contains(symbol)
|
self.locals.contains(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_variant(&self, symbol: &Symbol) -> bool {
|
|
||||||
self.variants.contains(symbol)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::ident::{UnqualifiedIdent, VariantName};
|
use crate::ident::UnqualifiedIdent;
|
||||||
use crate::module::ModuleName;
|
use crate::module::ModuleName;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// A globally unique identifier, used for both vars and variants.
|
/// A globally unique identifier, used for both vars and tags.
|
||||||
/// It will be used directly in code gen.
|
/// It will be used directly in code gen.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Symbol(Box<str>);
|
pub struct Symbol(Box<str>);
|
||||||
|
@ -33,12 +33,12 @@ impl Symbol {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_variant(variant_name: &VariantName, home: &str) -> Symbol {
|
pub fn from_global_tag(tag_name: &str) -> Symbol {
|
||||||
match variant_name {
|
Symbol(tag_name.into())
|
||||||
VariantName::Unqualified(name) => Symbol::new(home, name),
|
}
|
||||||
|
|
||||||
VariantName::Qualified(path, name) => Symbol::new(path, name),
|
pub fn from_private_tag(home: &str, tag_name: &str) -> Symbol {
|
||||||
}
|
Symbol(format!("{}.{}", home, tag_name).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_module<'a>(
|
pub fn from_module<'a>(
|
||||||
|
|
|
@ -251,13 +251,13 @@ pub fn is_multiline_expr<'a>(expr: &'a Expr<'a>) -> bool {
|
||||||
| OctalInt(_)
|
| OctalInt(_)
|
||||||
| BinaryInt(_)
|
| BinaryInt(_)
|
||||||
| Str(_)
|
| Str(_)
|
||||||
| Field(_, _)
|
| Access(_, _)
|
||||||
| QualifiedField(_, _)
|
|
||||||
| AccessorFunction(_)
|
| AccessorFunction(_)
|
||||||
| Var(_, _)
|
| Var(_, _)
|
||||||
| MalformedIdent(_)
|
| MalformedIdent(_)
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
| Variant(_, _) => false,
|
| GlobalTag(_)
|
||||||
|
| PrivateTag(_) => false,
|
||||||
|
|
||||||
// These expressions always have newlines
|
// These expressions always have newlines
|
||||||
Defs(_, _) | Case(_, _) => true,
|
Defs(_, _) | Case(_, _) => true,
|
||||||
|
|
|
@ -12,12 +12,7 @@ pub fn fmt_pattern<'a>(
|
||||||
|
|
||||||
match pattern {
|
match pattern {
|
||||||
Identifier(string) => buf.push_str(string),
|
Identifier(string) => buf.push_str(string),
|
||||||
Variant(module_parts, name) => {
|
GlobalTag(name) | PrivateTag(name) => {
|
||||||
for part in module_parts.iter() {
|
|
||||||
buf.push_str(part);
|
|
||||||
buf.push('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(name);
|
buf.push_str(name);
|
||||||
}
|
}
|
||||||
Apply(loc_pattern, loc_arg_patterns) => {
|
Apply(loc_pattern, loc_arg_patterns) => {
|
||||||
|
|
18
src/ident.rs
18
src/ident.rs
|
@ -40,15 +40,6 @@ impl<'a> UnqualifiedIdent<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A variant name, possibly fully-qualified with a module name
|
|
||||||
/// e.g. (Result.Ok)
|
|
||||||
/// Parameterized on a phantom marker for whether it has been canonicalized
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum VariantName {
|
|
||||||
Unqualified(String),
|
|
||||||
Qualified(String, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An identifier, possibly fully-qualified with a module name
|
/// An identifier, possibly fully-qualified with a module name
|
||||||
/// e.g. (Http.Request from http)
|
/// e.g. (Http.Request from http)
|
||||||
/// Parameterized on a phantom marker for whether it has been canonicalized
|
/// Parameterized on a phantom marker for whether it has been canonicalized
|
||||||
|
@ -89,12 +80,3 @@ impl Display for Ident {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for VariantName {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
VariantName::Unqualified(name) => write!(f, "{}", name),
|
|
||||||
VariantName::Qualified(path, name) => write!(f, "{}.{}", path, name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -127,13 +127,10 @@ pub enum Expr<'a> {
|
||||||
// String Literals
|
// String Literals
|
||||||
Str(&'a str),
|
Str(&'a str),
|
||||||
BlockStr(&'a [&'a str]),
|
BlockStr(&'a [&'a str]),
|
||||||
/// e.g. `(expr).foo.bar` - we rule out nested lookups in canonicalization,
|
/// Look up exactly one field on a record, e.g. (expr).foo.
|
||||||
/// but we want to keep the nesting here to give a nicer error message.
|
Access(&'a Expr<'a>, UnqualifiedIdent<'a>),
|
||||||
Field(&'a Loc<Expr<'a>>, Vec<'a, &'a str>),
|
|
||||||
/// e.g. `Foo.Bar.baz.qux`
|
|
||||||
QualifiedField(&'a [&'a str], &'a [&'a str]),
|
|
||||||
/// e.g. `.foo`
|
/// e.g. `.foo`
|
||||||
AccessorFunction(&'a str),
|
AccessorFunction(UnqualifiedIdent<'a>),
|
||||||
|
|
||||||
// Collection Literals
|
// Collection Literals
|
||||||
List(Vec<'a, &'a Loc<Expr<'a>>>),
|
List(Vec<'a, &'a Loc<Expr<'a>>>),
|
||||||
|
@ -141,7 +138,10 @@ pub enum Expr<'a> {
|
||||||
|
|
||||||
// Lookups
|
// Lookups
|
||||||
Var(&'a [&'a str], &'a str),
|
Var(&'a [&'a str], &'a str),
|
||||||
Variant(&'a [&'a str], &'a str),
|
|
||||||
|
// Tags
|
||||||
|
GlobalTag(&'a str),
|
||||||
|
PrivateTag(&'a str),
|
||||||
|
|
||||||
// Pattern Matching
|
// Pattern Matching
|
||||||
Closure(&'a Vec<'a, Loc<Pattern<'a>>>, &'a Loc<Expr<'a>>),
|
Closure(&'a Vec<'a, Loc<Pattern<'a>>>, &'a Loc<Expr<'a>>),
|
||||||
|
@ -150,7 +150,7 @@ pub enum Expr<'a> {
|
||||||
|
|
||||||
// Application
|
// Application
|
||||||
/// To apply by name, do Apply(Var(...), ...)
|
/// To apply by name, do Apply(Var(...), ...)
|
||||||
/// To apply a variant by name, do Apply(Variant(...), ...)
|
/// To apply a tag by name, do Apply(Tag(...), ...)
|
||||||
Apply(&'a Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>, CalledVia),
|
Apply(&'a Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>, CalledVia),
|
||||||
BinOp(&'a (Loc<Expr<'a>>, Loc<BinOp>, Loc<Expr<'a>>)),
|
BinOp(&'a (Loc<Expr<'a>>, Loc<BinOp>, Loc<Expr<'a>>)),
|
||||||
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
|
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
|
||||||
|
@ -272,8 +272,8 @@ pub enum Pattern<'a> {
|
||||||
// Identifier
|
// Identifier
|
||||||
Identifier(&'a str),
|
Identifier(&'a str),
|
||||||
|
|
||||||
// Variant, optionally qualified
|
GlobalTag(&'a str),
|
||||||
Variant(&'a [&'a str], &'a str),
|
PrivateTag(&'a str),
|
||||||
Apply(&'a Loc<Pattern<'a>>, &'a [Loc<Pattern<'a>>]),
|
Apply(&'a Loc<Pattern<'a>>, &'a [Loc<Pattern<'a>>]),
|
||||||
/// This is Loc<Pattern> rather than Loc<str> so we can record comments
|
/// This is Loc<Pattern> rather than Loc<str> so we can record comments
|
||||||
/// around the destructured names, e.g. { x ### x does stuff ###, y }
|
/// around the destructured names, e.g. { x ### x does stuff ###, y }
|
||||||
|
@ -310,39 +310,35 @@ pub enum Pattern<'a> {
|
||||||
impl<'a> Pattern<'a> {
|
impl<'a> Pattern<'a> {
|
||||||
pub fn from_ident(arena: &'a Bump, ident: Ident<'a>) -> Pattern<'a> {
|
pub fn from_ident(arena: &'a Bump, ident: Ident<'a>) -> Pattern<'a> {
|
||||||
match ident {
|
match ident {
|
||||||
Ident::Var(maybe_qualified) => {
|
Ident::GlobalTag(string) => Pattern::GlobalTag(string),
|
||||||
if maybe_qualified.module_parts.is_empty() {
|
Ident::PrivateTag(string) => Pattern::PrivateTag(string),
|
||||||
Pattern::Identifier(maybe_qualified.value)
|
Ident::Access(maybe_qualified) => {
|
||||||
|
if maybe_qualified.value.len() == 1 {
|
||||||
|
Pattern::Identifier(maybe_qualified.value.iter().next().unwrap())
|
||||||
} else {
|
} else {
|
||||||
Pattern::Variant(maybe_qualified.module_parts, maybe_qualified.value)
|
let mut buf = String::with_capacity_in(
|
||||||
}
|
maybe_qualified.module_parts.len() + maybe_qualified.value.len(),
|
||||||
}
|
arena,
|
||||||
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() {
|
for part in maybe_qualified.module_parts.iter() {
|
||||||
buf.push_str(part);
|
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('.');
|
buf.push('.');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Pattern::Malformed(buf.into_bump_str())
|
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::AccessorFunction(string) => Pattern::Malformed(string),
|
||||||
Ident::Malformed(string) => Pattern::Malformed(string),
|
Ident::Malformed(string) => Pattern::Malformed(string),
|
||||||
|
|
|
@ -8,16 +8,16 @@ use bumpalo::Bump;
|
||||||
|
|
||||||
/// The parser accepts all of these in any position where any one of them could
|
/// The parser accepts all of these in any position where any one of them could
|
||||||
/// 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 tag!" 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, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Ident<'a> {
|
pub enum Ident<'a> {
|
||||||
/// foo or Bar.Baz.foo
|
/// Foo or Bar
|
||||||
Var(MaybeQualified<'a, &'a str>),
|
GlobalTag(&'a str),
|
||||||
/// Foo or Bar.Baz.Foo
|
/// @Foo or @Bar
|
||||||
Variant(MaybeQualified<'a, &'a str>),
|
PrivateTag(&'a str),
|
||||||
/// foo.bar or Foo.Bar.baz.qux
|
/// foo or foo.bar or Foo.Bar.baz.qux
|
||||||
Field(MaybeQualified<'a, &'a [&'a str]>),
|
Access(MaybeQualified<'a, &'a [&'a str]>),
|
||||||
/// .foo
|
/// .foo
|
||||||
AccessorFunction(&'a str),
|
AccessorFunction(&'a str),
|
||||||
/// .Foo or foo. or something like foo.Bar
|
/// .Foo or foo. or something like foo.Bar
|
||||||
|
@ -29,9 +29,8 @@ impl<'a> Ident<'a> {
|
||||||
use self::Ident::*;
|
use self::Ident::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Var(string) => string.len(),
|
GlobalTag(string) | PrivateTag(string) => string.len(),
|
||||||
Variant(string) => string.len(),
|
Access(string) => string.len(),
|
||||||
Field(string) => string.len(),
|
|
||||||
AccessorFunction(string) => string.len(),
|
AccessorFunction(string) => string.len(),
|
||||||
Malformed(string) => string.len(),
|
Malformed(string) => string.len(),
|
||||||
}
|
}
|
||||||
|
@ -64,12 +63,31 @@ where
|
||||||
let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena);
|
let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena);
|
||||||
let mut is_capitalized;
|
let mut is_capitalized;
|
||||||
let is_accessor_fn;
|
let is_accessor_fn;
|
||||||
|
let mut is_private_tag = false;
|
||||||
|
|
||||||
// Identifiers and accessor functions must start with either a letter or a dot.
|
// Identifiers and accessor functions must start with either a letter or a dot.
|
||||||
// If this starts with neither, it must be something else!
|
// If this starts with neither, it must be something else!
|
||||||
match chars.next() {
|
match chars.next() {
|
||||||
Some(ch) => {
|
Some(ch) => {
|
||||||
if ch.is_alphabetic() {
|
if ch == '@' {
|
||||||
|
// '@' must always be followed by a capital letter!
|
||||||
|
match chars.next() {
|
||||||
|
Some(ch) if ch.is_uppercase() => {
|
||||||
|
part_buf.push('@');
|
||||||
|
part_buf.push(ch);
|
||||||
|
|
||||||
|
is_private_tag = true;
|
||||||
|
is_capitalized = true;
|
||||||
|
is_accessor_fn = false;
|
||||||
|
}
|
||||||
|
Some(ch) => {
|
||||||
|
return Err(unexpected(ch, 0, state, Attempting::Identifier));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(unexpected_eof(0, Attempting::Identifier, state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ch.is_alphabetic() {
|
||||||
part_buf.push(ch);
|
part_buf.push(ch);
|
||||||
|
|
||||||
is_capitalized = ch.is_uppercase();
|
is_capitalized = ch.is_uppercase();
|
||||||
|
@ -178,7 +196,7 @@ where
|
||||||
let answer = if is_accessor_fn {
|
let answer = if is_accessor_fn {
|
||||||
// Handle accessor functions first because they have the strictest requirements.
|
// Handle accessor functions first because they have the strictest requirements.
|
||||||
// Accessor functions may have exactly 1 noncapitalized part, and no capitalzed parts.
|
// Accessor functions may have exactly 1 noncapitalized part, and no capitalzed parts.
|
||||||
if capitalized_parts.is_empty() && noncapitalized_parts.len() == 1 {
|
if capitalized_parts.is_empty() && noncapitalized_parts.len() == 1 && !is_private_tag {
|
||||||
let value = noncapitalized_parts.iter().next().unwrap();
|
let value = noncapitalized_parts.iter().next().unwrap();
|
||||||
|
|
||||||
Ident::AccessorFunction(value)
|
Ident::AccessorFunction(value)
|
||||||
|
@ -193,38 +211,51 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match noncapitalized_parts.len() {
|
if noncapitalized_parts.is_empty() {
|
||||||
0 => {
|
// We have capitalized parts only, so this must be a tag.
|
||||||
// We have capitalized parts only, so this must be a variant.
|
match capitalized_parts.first() {
|
||||||
match capitalized_parts.pop() {
|
Some(value) => {
|
||||||
Some(value) => Ident::Variant(MaybeQualified {
|
if capitalized_parts.len() == 1 {
|
||||||
module_parts: capitalized_parts.into_bump_slice(),
|
if is_private_tag {
|
||||||
value,
|
Ident::PrivateTag(value)
|
||||||
}),
|
} else {
|
||||||
None => {
|
Ident::GlobalTag(value)
|
||||||
// We had neither capitalized nor noncapitalized parts,
|
}
|
||||||
// yet we made it this far. The only explanation is that this was
|
} else {
|
||||||
// a stray '.' drifting through the cosmos.
|
// This is a qualified tag, which is not allowed!
|
||||||
return Err(unexpected('.', 1, state, Attempting::Identifier));
|
return malformed(
|
||||||
|
None,
|
||||||
|
arena,
|
||||||
|
state,
|
||||||
|
chars,
|
||||||
|
capitalized_parts,
|
||||||
|
noncapitalized_parts,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
// We had neither capitalized nor noncapitalized parts,
|
||||||
|
// yet we made it this far. The only explanation is that this was
|
||||||
|
// a stray '.' drifting through the cosmos.
|
||||||
|
return Err(unexpected('.', 1, state, Attempting::Identifier));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
1 => {
|
} else if is_private_tag {
|
||||||
// We have exactly one noncapitalized part, so this must be a var.
|
// This is qualified field access with an '@' in front, which does not make sense!
|
||||||
let value = noncapitalized_parts.iter().next().unwrap();
|
return malformed(
|
||||||
|
None,
|
||||||
Ident::Var(MaybeQualified {
|
arena,
|
||||||
module_parts: capitalized_parts.into_bump_slice(),
|
state,
|
||||||
value,
|
chars,
|
||||||
})
|
capitalized_parts,
|
||||||
}
|
noncapitalized_parts,
|
||||||
_ => {
|
);
|
||||||
// We have multiple noncapitalized parts, so this must be a field.
|
} else {
|
||||||
Ident::Field(MaybeQualified {
|
// We have multiple noncapitalized parts, so this must be field access.
|
||||||
module_parts: capitalized_parts.into_bump_slice(),
|
Ident::Access(MaybeQualified {
|
||||||
value: noncapitalized_parts.into_bump_slice(),
|
module_parts: capitalized_parts.into_bump_slice(),
|
||||||
})
|
value: noncapitalized_parts.into_bump_slice(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -288,14 +319,14 @@ pub fn ident<'a>() -> impl Parser<'a, Ident<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variant_or_ident<'a, F>(pred: F) -> impl Parser<'a, &'a str>
|
pub fn global_tag_or_ident<'a, F>(pred: F) -> impl Parser<'a, &'a str>
|
||||||
where
|
where
|
||||||
F: Fn(char) -> bool,
|
F: Fn(char) -> bool,
|
||||||
{
|
{
|
||||||
move |arena, state: State<'a>| {
|
move |arena, state: State<'a>| {
|
||||||
let mut chars = state.input.chars();
|
let mut chars = state.input.chars();
|
||||||
|
|
||||||
// pred will determine if this is a variant or ident (based on capitalization)
|
// pred will determine if this is a tag or ident (based on capitalization)
|
||||||
let first_letter = match chars.next() {
|
let first_letter = match chars.next() {
|
||||||
Some(first_char) => {
|
Some(first_char) => {
|
||||||
if pred(first_char) {
|
if pred(first_char) {
|
||||||
|
@ -346,12 +377,12 @@ where
|
||||||
/// * A record field, e.g. "email" in `.email` or in `email:`
|
/// * A record field, e.g. "email" in `.email` or in `email:`
|
||||||
/// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->`
|
/// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->`
|
||||||
pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||||
variant_or_ident(|first_char| first_char.is_lowercase())
|
global_tag_or_ident(|first_char| first_char.is_lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unqualified_ident<'a>() -> impl Parser<'a, UnqualifiedIdent<'a>> {
|
pub fn unqualified_ident<'a>() -> impl Parser<'a, UnqualifiedIdent<'a>> {
|
||||||
map!(
|
map!(
|
||||||
variant_or_ident(|first_char| first_char.is_alphabetic()),
|
global_tag_or_ident(|first_char| first_char.is_alphabetic()),
|
||||||
UnqualifiedIdent::new
|
UnqualifiedIdent::new
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
108
src/parse/mod.rs
108
src/parse/mod.rs
|
@ -10,13 +10,14 @@ pub mod problems;
|
||||||
pub mod string_literal;
|
pub mod string_literal;
|
||||||
pub mod type_annotation;
|
pub mod type_annotation;
|
||||||
|
|
||||||
|
use crate::ident::UnqualifiedIdent;
|
||||||
use crate::operator::{BinOp, CalledVia, UnaryOp};
|
use crate::operator::{BinOp, CalledVia, UnaryOp};
|
||||||
use crate::parse::ast::{AssignedField, Attempting, Def, Expr, MaybeQualified, Pattern, Spaceable};
|
use crate::parse::ast::{AssignedField, Attempting, Def, Expr, MaybeQualified, Pattern, Spaceable};
|
||||||
use crate::parse::blankspace::{
|
use crate::parse::blankspace::{
|
||||||
space0, space0_after, space0_around, space0_before, space1, space1_after, space1_around,
|
space0, space0_after, space0_around, space0_before, space1, space1_after, space1_around,
|
||||||
space1_before,
|
space1_before,
|
||||||
};
|
};
|
||||||
use crate::parse::ident::{ident, lowercase_ident, variant_or_ident, Ident};
|
use crate::parse::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
|
||||||
use crate::parse::number_literal::number_literal;
|
use crate::parse::number_literal::number_literal;
|
||||||
use crate::parse::parser::{
|
use crate::parse::parser::{
|
||||||
allocated, char, not, not_followed_by, optional, string, then, unexpected, unexpected_eof,
|
allocated, char, not, not_followed_by, optional, string, then, unexpected, unexpected_eof,
|
||||||
|
@ -216,13 +217,24 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
|
||||||
Ok((Located { value, region }, state))
|
Ok((Located { value, region }, state))
|
||||||
}
|
}
|
||||||
// '.' and a record field immediately after ')', no optional spaces
|
// '.' and a record field immediately after ')', no optional spaces
|
||||||
Some(Either::Second(Either::First(fields))) => Ok((
|
Some(Either::Second(Either::First(fields))) => {
|
||||||
Located {
|
let mut value = loc_expr.value;
|
||||||
region: loc_expr_with_extras.region,
|
|
||||||
value: Expr::Field(arena.alloc(loc_expr), fields),
|
for field in fields {
|
||||||
},
|
// Wrap the previous answer in the new one, so we end up
|
||||||
state,
|
// with a nested Expr. That way, `foo.bar.baz` gets represented
|
||||||
)),
|
// in the AST as if it had been written (foo.bar).baz all along.
|
||||||
|
value = Expr::Access(arena.alloc(value), UnqualifiedIdent::new(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Located {
|
||||||
|
region: loc_expr.region,
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
None => Ok((loc_expr, state)),
|
None => Ok((loc_expr, state)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -243,7 +255,8 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Variant(module_parts, value) => Ok(Pattern::Variant(module_parts, value)),
|
Expr::GlobalTag(value) => Ok(Pattern::GlobalTag(value)),
|
||||||
|
Expr::PrivateTag(value) => Ok(Pattern::PrivateTag(value)),
|
||||||
Expr::Apply(loc_val, loc_args, _) => {
|
Expr::Apply(loc_val, loc_args, _) => {
|
||||||
let region = loc_val.region;
|
let region = loc_val.region;
|
||||||
let value = expr_to_pattern(arena, &loc_val.value)?;
|
let value = expr_to_pattern(arena, &loc_val.value)?;
|
||||||
|
@ -298,7 +311,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
||||||
// These would not have parsed as patterns
|
// These would not have parsed as patterns
|
||||||
Expr::BlockStr(_)
|
Expr::BlockStr(_)
|
||||||
| Expr::AccessorFunction(_)
|
| Expr::AccessorFunction(_)
|
||||||
| Expr::Field(_, _)
|
| Expr::Access(_, _)
|
||||||
| Expr::List(_)
|
| Expr::List(_)
|
||||||
| Expr::Closure(_, _)
|
| Expr::Closure(_, _)
|
||||||
| Expr::BinOp(_)
|
| Expr::BinOp(_)
|
||||||
|
@ -307,8 +320,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
||||||
| Expr::Case(_, _)
|
| Expr::Case(_, _)
|
||||||
| Expr::MalformedClosure
|
| Expr::MalformedClosure
|
||||||
| Expr::PrecedenceConflict(_, _, _)
|
| Expr::PrecedenceConflict(_, _, _)
|
||||||
| Expr::UnaryOp(_, _)
|
| Expr::UnaryOp(_, _) => Err(Fail {
|
||||||
| Expr::QualifiedField(_, _) => Err(Fail {
|
|
||||||
attempting: Attempting::Def,
|
attempting: Attempting::Def,
|
||||||
reason: FailReason::InvalidPattern,
|
reason: FailReason::InvalidPattern,
|
||||||
}),
|
}),
|
||||||
|
@ -655,9 +667,10 @@ fn parse_closure_param<'a>(
|
||||||
char(')')
|
char(')')
|
||||||
),
|
),
|
||||||
// The least common, but still allowed, e.g. \Foo -> ...
|
// The least common, but still allowed, e.g. \Foo -> ...
|
||||||
loc!(map!(unqualified_variant(), |name| {
|
loc!(one_of!(
|
||||||
Pattern::Variant(&[], name)
|
map!(private_tag(), Pattern::PrivateTag),
|
||||||
}))
|
map!(global_tag(), Pattern::GlobalTag)
|
||||||
|
))
|
||||||
)
|
)
|
||||||
.parse(arena, state)
|
.parse(arena, state)
|
||||||
}
|
}
|
||||||
|
@ -665,7 +678,7 @@ fn parse_closure_param<'a>(
|
||||||
fn pattern<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
fn pattern<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||||
one_of!(
|
one_of!(
|
||||||
underscore_pattern(),
|
underscore_pattern(),
|
||||||
variant_pattern(),
|
tag_pattern(),
|
||||||
ident_pattern(),
|
ident_pattern(),
|
||||||
record_destructure(min_indent),
|
record_destructure(min_indent),
|
||||||
string_pattern(),
|
string_pattern(),
|
||||||
|
@ -708,8 +721,11 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variant_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
fn tag_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||||
map!(unqualified_variant(), |name| Pattern::Variant(&[], name))
|
one_of!(
|
||||||
|
map!(private_tag(), Pattern::PrivateTag),
|
||||||
|
map!(global_tag(), Pattern::GlobalTag)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ident_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
fn ident_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||||
|
@ -958,7 +974,7 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
Some(Either::First(loc_args)) => {
|
Some(Either::First(loc_args)) => {
|
||||||
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(arena, loc_ident.value),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut allocated_args = Vec::with_capacity_in(loc_args.len(), arena);
|
let mut allocated_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||||
|
@ -999,7 +1015,7 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
None => {
|
None => {
|
||||||
let ident = loc_ident.value.clone();
|
let ident = loc_ident.value.clone();
|
||||||
|
|
||||||
Ok((ident_to_expr(ident), state))
|
Ok((ident_to_expr(arena, ident), state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1007,8 +1023,8 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ident_without_apply<'a>() -> impl Parser<'a, Expr<'a>> {
|
pub fn ident_without_apply<'a>() -> impl Parser<'a, Expr<'a>> {
|
||||||
then(loc!(ident()), move |_arena, state, loc_ident| {
|
then(loc!(ident()), move |arena, state, loc_ident| {
|
||||||
Ok((ident_to_expr(loc_ident.value), state))
|
Ok((ident_to_expr(arena, loc_ident.value), state))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1047,12 +1063,34 @@ pub fn case_with_indent<'a>() -> impl Parser<'a, u16> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ident_to_expr(src: Ident<'_>) -> Expr<'_> {
|
fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
||||||
match src {
|
match src {
|
||||||
Ident::Var(info) => Expr::Var(info.module_parts, info.value),
|
Ident::GlobalTag(string) => Expr::GlobalTag(string),
|
||||||
Ident::Variant(info) => Expr::Variant(info.module_parts, info.value),
|
Ident::PrivateTag(string) => Expr::PrivateTag(string),
|
||||||
Ident::Field(info) => Expr::QualifiedField(info.module_parts, info.value),
|
Ident::Access(info) => {
|
||||||
Ident::AccessorFunction(string) => Expr::AccessorFunction(string),
|
let mut iter = info.value.into_iter();
|
||||||
|
|
||||||
|
// The first value in the iterator is the variable name,
|
||||||
|
// e.g. `foo` in `foo.bar.baz`
|
||||||
|
let mut answer = match iter.next() {
|
||||||
|
Some(var) => Expr::Var(info.module_parts, var),
|
||||||
|
None => {
|
||||||
|
panic!("Parsed an Ident::Access with no parts");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The remaining items in the iterator are record field accesses,
|
||||||
|
// e.g. `bar` in `foo.bar.baz`, followed by `baz`
|
||||||
|
for field in iter {
|
||||||
|
// Wrap the previous answer in the new one, so we end up
|
||||||
|
// with a nested Expr. That way, `foo.bar.baz` gets represented
|
||||||
|
// in the AST as if it had been written (foo.bar).baz all along.
|
||||||
|
answer = Expr::Access(arena.alloc(answer), UnqualifiedIdent::new(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
Ident::AccessorFunction(string) => Expr::AccessorFunction(UnqualifiedIdent::new(string)),
|
||||||
Ident::Malformed(string) => Expr::MalformedIdent(string),
|
Ident::Malformed(string) => Expr::MalformedIdent(string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1162,11 +1200,17 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is mainly for matching variants in closure params, e.g. \Foo -> ...
|
/// This is mainly for matching tags in closure params, e.g. \@Foo -> ...
|
||||||
///
|
fn private_tag<'a>() -> impl Parser<'a, &'a str> {
|
||||||
/// TODO: this should absolutely support qualified variants. Need to change it and rename it!
|
skip_first!(char('@'), global_tag())
|
||||||
fn unqualified_variant<'a>() -> impl Parser<'a, &'a str> {
|
}
|
||||||
variant_or_ident(|first_char| first_char.is_uppercase())
|
|
||||||
|
/// This is mainly for matching tags in closure params, e.g. \Foo -> ...
|
||||||
|
fn global_tag<'a>() -> impl Parser<'a, &'a str> {
|
||||||
|
skip_first!(
|
||||||
|
char('@'),
|
||||||
|
global_tag_or_ident(|first_char| first_char.is_uppercase())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string_literal<'a>() -> impl Parser<'a, Expr<'a>> {
|
pub fn string_literal<'a>() -> impl Parser<'a, Expr<'a>> {
|
||||||
|
|
|
@ -36,13 +36,7 @@ pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn can_expr(expr_str: &str) -> (Expr, Output, Vec<Problem>, VarStore, Variable) {
|
pub fn can_expr(expr_str: &str) -> (Expr, Output, Vec<Problem>, VarStore, Variable) {
|
||||||
can_expr_with(
|
can_expr_with(&Bump::new(), "blah", expr_str, &ImMap::default())
|
||||||
&Bump::new(),
|
|
||||||
"blah",
|
|
||||||
expr_str,
|
|
||||||
&ImMap::default(),
|
|
||||||
&ImMap::default(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -51,7 +45,6 @@ pub fn can_expr_with(
|
||||||
name: &str,
|
name: &str,
|
||||||
expr_str: &str,
|
expr_str: &str,
|
||||||
declared_idents: &ImMap<Ident, (Symbol, Region)>,
|
declared_idents: &ImMap<Ident, (Symbol, Region)>,
|
||||||
declared_variants: &ImMap<Symbol, Located<Box<str>>>,
|
|
||||||
) -> (Expr, Output, Vec<Problem>, VarStore, Variable) {
|
) -> (Expr, Output, Vec<Problem>, VarStore, Variable) {
|
||||||
let loc_expr = parse_loc_with(&arena, expr_str).unwrap_or_else(|_| {
|
let loc_expr = parse_loc_with(&arena, expr_str).unwrap_or_else(|_| {
|
||||||
panic!(
|
panic!(
|
||||||
|
@ -72,7 +65,6 @@ pub fn can_expr_with(
|
||||||
Region::zero(),
|
Region::zero(),
|
||||||
loc_expr,
|
loc_expr,
|
||||||
declared_idents,
|
declared_idents,
|
||||||
declared_variants,
|
|
||||||
expected,
|
expected,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ mod test_canonicalize {
|
||||||
struct Out<'a> {
|
struct Out<'a> {
|
||||||
locals: Vec<&'a str>,
|
locals: Vec<&'a str>,
|
||||||
globals: Vec<&'a str>,
|
globals: Vec<&'a str>,
|
||||||
variants: Vec<&'a str>,
|
|
||||||
calls: Vec<&'a str>,
|
calls: Vec<&'a str>,
|
||||||
tail_call: Option<&'a str>,
|
tail_call: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
@ -58,7 +57,6 @@ mod test_canonicalize {
|
||||||
let references = References {
|
let references = References {
|
||||||
locals: vec_to_set(self.locals),
|
locals: vec_to_set(self.locals),
|
||||||
globals: vec_to_set(self.globals),
|
globals: vec_to_set(self.globals),
|
||||||
variants: vec_to_set(self.variants),
|
|
||||||
calls: vec_to_set(self.calls),
|
calls: vec_to_set(self.calls),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,8 +76,7 @@ mod test_canonicalize {
|
||||||
|
|
||||||
fn assert_can(input: &str, expected: Expr) {
|
fn assert_can(input: &str, expected: Expr) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (actual, _, _, _, _) =
|
let (actual, _, _, _, _) = can_expr_with(&arena, "Blah", input, &ImMap::default());
|
||||||
can_expr_with(&arena, "Blah", input, &ImMap::default(), &ImMap::default());
|
|
||||||
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
@ -141,7 +138,7 @@ mod test_canonicalize {
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
let (_actual, mut output, problems, _var_store, _vars) =
|
let (_actual, mut output, problems, _var_store, _vars) =
|
||||||
can_expr_with(&arena, "Blah", src, &ImMap::default(), &ImMap::default());
|
can_expr_with(&arena, "Blah", src, &ImMap::default());
|
||||||
|
|
||||||
assert_eq!(problems, vec![]);
|
assert_eq!(problems, vec![]);
|
||||||
|
|
||||||
|
@ -153,7 +150,6 @@ mod test_canonicalize {
|
||||||
Out {
|
Out {
|
||||||
locals: vec!["func"],
|
locals: vec!["func"],
|
||||||
globals: vec![],
|
globals: vec![],
|
||||||
variants: vec![],
|
|
||||||
calls: vec!["func"],
|
calls: vec!["func"],
|
||||||
tail_call: None
|
tail_call: None
|
||||||
}
|
}
|
||||||
|
@ -177,7 +173,6 @@ mod test_canonicalize {
|
||||||
// references: References {
|
// references: References {
|
||||||
// locals: vec_to_set(vec![]),
|
// locals: vec_to_set(vec![]),
|
||||||
// globals: vec_to_set(vec![]),
|
// globals: vec_to_set(vec![]),
|
||||||
// variants: vec_to_set(vec![]),
|
|
||||||
// calls: vec_to_set(vec![]),
|
// calls: vec_to_set(vec![]),
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
@ -199,7 +194,7 @@ mod test_canonicalize {
|
||||||
);
|
);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (_actual, mut output, problems, _var_store, _vars) =
|
let (_actual, mut output, problems, _var_store, _vars) =
|
||||||
can_expr_with(&arena, "Blah", src, &ImMap::default(), &ImMap::default());
|
can_expr_with(&arena, "Blah", src, &ImMap::default());
|
||||||
|
|
||||||
assert_eq!(problems, vec![]);
|
assert_eq!(problems, vec![]);
|
||||||
|
|
||||||
|
@ -211,7 +206,6 @@ mod test_canonicalize {
|
||||||
Out {
|
Out {
|
||||||
locals: vec!["identity", "apply"],
|
locals: vec!["identity", "apply"],
|
||||||
globals: vec![],
|
globals: vec![],
|
||||||
variants: vec![],
|
|
||||||
calls: vec!["f", "apply"],
|
calls: vec!["f", "apply"],
|
||||||
tail_call: None
|
tail_call: None
|
||||||
}
|
}
|
||||||
|
@ -257,7 +251,7 @@ mod test_canonicalize {
|
||||||
);
|
);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (actual, _output, _problems, _var_store, _vars) =
|
let (actual, _output, _problems, _var_store, _vars) =
|
||||||
can_expr_with(&arena, "Blah", src, &ImMap::default(), &ImMap::default());
|
can_expr_with(&arena, "Blah", src, &ImMap::default());
|
||||||
|
|
||||||
let detected = get_closure(&actual, 0);
|
let detected = get_closure(&actual, 0);
|
||||||
assert_eq!(detected, Recursive::TailRecursive);
|
assert_eq!(detected, Recursive::TailRecursive);
|
||||||
|
@ -283,7 +277,7 @@ mod test_canonicalize {
|
||||||
);
|
);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (actual, _output, _problems, _var_store, _vars) =
|
let (actual, _output, _problems, _var_store, _vars) =
|
||||||
can_expr_with(&arena, "Blah", src, &ImMap::default(), &ImMap::default());
|
can_expr_with(&arena, "Blah", src, &ImMap::default());
|
||||||
|
|
||||||
let detected = get_closure(&actual, 0);
|
let detected = get_closure(&actual, 0);
|
||||||
assert_eq!(detected, Recursive::TailRecursive);
|
assert_eq!(detected, Recursive::TailRecursive);
|
||||||
|
@ -300,7 +294,7 @@ mod test_canonicalize {
|
||||||
);
|
);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (actual, _output, _problems, _var_store, _vars) =
|
let (actual, _output, _problems, _var_store, _vars) =
|
||||||
can_expr_with(&arena, "Blah", src, &ImMap::default(), &ImMap::default());
|
can_expr_with(&arena, "Blah", src, &ImMap::default());
|
||||||
|
|
||||||
let detected = get_closure(&actual, 0);
|
let detected = get_closure(&actual, 0);
|
||||||
assert_eq!(detected, Recursive::TailRecursive);
|
assert_eq!(detected, Recursive::TailRecursive);
|
||||||
|
@ -320,7 +314,7 @@ mod test_canonicalize {
|
||||||
);
|
);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (actual, _output, _problems, _var_store, _vars) =
|
let (actual, _output, _problems, _var_store, _vars) =
|
||||||
can_expr_with(&arena, "Blah", src, &ImMap::default(), &ImMap::default());
|
can_expr_with(&arena, "Blah", src, &ImMap::default());
|
||||||
|
|
||||||
let detected = get_closure(&actual, 0);
|
let detected = get_closure(&actual, 0);
|
||||||
assert_eq!(detected, Recursive::Recursive);
|
assert_eq!(detected, Recursive::Recursive);
|
||||||
|
@ -346,7 +340,7 @@ mod test_canonicalize {
|
||||||
);
|
);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let (actual, _output, _problems, _var_store, _vars) =
|
let (actual, _output, _problems, _var_store, _vars) =
|
||||||
can_expr_with(&arena, "Blah", src, &ImMap::default(), &ImMap::default());
|
can_expr_with(&arena, "Blah", src, &ImMap::default());
|
||||||
|
|
||||||
let detected = get_closure(&actual, 0);
|
let detected = get_closure(&actual, 0);
|
||||||
assert_eq!(detected, Recursive::Recursive);
|
assert_eq!(detected, Recursive::Recursive);
|
||||||
|
@ -381,7 +375,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["func", "local"],
|
// locals: vec!["func", "local"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec!["func"],
|
// calls: vec!["func"],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -415,7 +408,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["local"],
|
// locals: vec!["local"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec![],
|
// calls: vec![],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -450,7 +442,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec![],
|
// locals: vec![],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec![],
|
// calls: vec![],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -481,7 +472,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["a", "b"],
|
// locals: vec!["a", "b"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec![],
|
// calls: vec![],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -512,7 +502,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["c"],
|
// locals: vec!["c"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec![],
|
// calls: vec![],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -541,7 +530,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["fibonacci"],
|
// locals: vec!["fibonacci"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec!["fibonacci"],
|
// calls: vec!["fibonacci"],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -576,7 +564,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["factorial", "factorialHelp"],
|
// locals: vec!["factorial", "factorialHelp"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec!["factorial", "factorialHelp"],
|
// calls: vec!["factorial", "factorialHelp"],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -604,7 +591,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["a", "b"],
|
// locals: vec!["a", "b"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec![],
|
// calls: vec![],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -634,7 +620,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["increment", "x", "y", "z"],
|
// locals: vec!["increment", "x", "y", "z"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec!["increment"],
|
// calls: vec!["increment"],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
@ -675,7 +660,6 @@ mod test_canonicalize {
|
||||||
// Out {
|
// Out {
|
||||||
// locals: vec!["func1", "func2", "x", "y", "z"],
|
// locals: vec!["func1", "func2", "x", "y", "z"],
|
||||||
// globals: vec![],
|
// globals: vec![],
|
||||||
// variants: vec![],
|
|
||||||
// calls: vec!["func1", "func2"],
|
// calls: vec!["func1", "func2"],
|
||||||
// tail_call: None
|
// tail_call: None
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -17,6 +17,7 @@ mod test_parse {
|
||||||
use crate::helpers::parse_with;
|
use crate::helpers::parse_with;
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
use bumpalo::{self, Bump};
|
use bumpalo::{self, Bump};
|
||||||
|
use roc::ident::UnqualifiedIdent;
|
||||||
use roc::module::ModuleName;
|
use roc::module::ModuleName;
|
||||||
use roc::operator::BinOp::*;
|
use roc::operator::BinOp::*;
|
||||||
use roc::operator::CalledVia;
|
use roc::operator::CalledVia;
|
||||||
|
@ -467,25 +468,51 @@ mod test_parse {
|
||||||
// VARIANT
|
// VARIANT
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_variant() {
|
fn basic_global_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
let expected = Expr::GlobalTag("Whee");
|
||||||
let expected = Expr::Variant(module_parts, "Whee");
|
|
||||||
let actual = parse_with(&arena, "Whee");
|
let actual = parse_with(&arena, "Whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn qualified_variant() {
|
fn basic_private_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let module_parts = bumpalo::vec![in &arena; "One", "Two"].into_bump_slice();
|
let expected = Expr::PrivateTag("@Whee");
|
||||||
let expected = Expr::Variant(module_parts, "Whee");
|
let actual = parse_with(&arena, "@Whee");
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn qualified_global_tag() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let expected = Expr::MalformedIdent("One.Two.Whee");
|
||||||
let actual = parse_with(&arena, "One.Two.Whee");
|
let actual = parse_with(&arena, "One.Two.Whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO restore this test - it fails, but is not worth fixing right now.
|
||||||
|
// #[test]
|
||||||
|
// fn qualified_private_tag() {
|
||||||
|
// let arena = Bump::new();
|
||||||
|
// let expected = Expr::MalformedIdent("One.Two.@Whee");
|
||||||
|
// let actual = parse_with(&arena, "One.Two.@Whee");
|
||||||
|
|
||||||
|
// assert_eq!(Ok(expected), actual);
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn private_qualified_tag() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let expected = Expr::MalformedIdent("@One.Two.Whee");
|
||||||
|
let actual = parse_with(&arena, "@One.Two.Whee");
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
// LISTS
|
// LISTS
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -520,13 +547,25 @@ mod test_parse {
|
||||||
|
|
||||||
// FIELD ACCESS
|
// FIELD ACCESS
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_field() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
||||||
|
let field = UnqualifiedIdent::new("field");
|
||||||
|
let var = Var(module_parts, "rec");
|
||||||
|
let expected = Access(arena.alloc(var), field);
|
||||||
|
let actual = parse_with(&arena, "rec.field");
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parenthetical_basic_field() {
|
fn parenthetical_basic_field() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
||||||
let fields = bumpalo::vec![in &arena; "field"];
|
let field = UnqualifiedIdent::new("field");
|
||||||
let paren_var = ParensAround(arena.alloc(Var(module_parts, "rec")));
|
let paren_var = ParensAround(arena.alloc(Var(module_parts, "rec")));
|
||||||
let expected = Field(arena.alloc(Located::new(0, 0, 1, 4, paren_var)), fields);
|
let expected = Access(arena.alloc(paren_var), field);
|
||||||
let actual = parse_with(&arena, "(rec).field");
|
let actual = parse_with(&arena, "(rec).field");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -536,21 +575,27 @@ mod test_parse {
|
||||||
fn parenthetical_field_qualified_var() {
|
fn parenthetical_field_qualified_var() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let module_parts = bumpalo::vec![in &arena; "One", "Two"].into_bump_slice();
|
let module_parts = bumpalo::vec![in &arena; "One", "Two"].into_bump_slice();
|
||||||
let fields = bumpalo::vec![in &arena; "field"];
|
let field = UnqualifiedIdent::new("field");
|
||||||
let paren_var = ParensAround(arena.alloc(Var(module_parts, "rec")));
|
let paren_var = ParensAround(arena.alloc(Var(module_parts, "rec")));
|
||||||
let expected = Field(arena.alloc(Located::new(0, 0, 1, 12, paren_var)), fields);
|
let expected = Access(arena.alloc(paren_var), field);
|
||||||
let actual = parse_with(&arena, "(One.Two.rec).field");
|
let actual = parse_with(&arena, "(One.Two.rec).field");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_field() {
|
fn multiple_fields() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
||||||
let fields = bumpalo::vec![in &arena; "rec", "field"].into_bump_slice();
|
let abc = UnqualifiedIdent::new("abc");
|
||||||
let expected = QualifiedField(module_parts, fields);
|
let def = UnqualifiedIdent::new("def");
|
||||||
let actual = parse_with(&arena, "rec.field");
|
let ghi = UnqualifiedIdent::new("ghi");
|
||||||
|
let var = Var(module_parts, "rec");
|
||||||
|
let expected = Access(
|
||||||
|
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), abc)), def)),
|
||||||
|
ghi,
|
||||||
|
);
|
||||||
|
let actual = parse_with(&arena, "rec.abc.def.ghi");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -559,9 +604,15 @@ mod test_parse {
|
||||||
fn qualified_field() {
|
fn qualified_field() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let module_parts = bumpalo::vec![in &arena; "One", "Two"].into_bump_slice();
|
let module_parts = bumpalo::vec![in &arena; "One", "Two"].into_bump_slice();
|
||||||
let fields = bumpalo::vec![in &arena; "rec", "field"].into_bump_slice();
|
let abc = UnqualifiedIdent::new("abc");
|
||||||
let expected = QualifiedField(module_parts, fields);
|
let def = UnqualifiedIdent::new("def");
|
||||||
let actual = parse_with(&arena, "One.Two.rec.field");
|
let ghi = UnqualifiedIdent::new("ghi");
|
||||||
|
let var = Var(module_parts, "rec");
|
||||||
|
let expected = Access(
|
||||||
|
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), abc)), def)),
|
||||||
|
ghi,
|
||||||
|
);
|
||||||
|
let actual = parse_with(&arena, "One.Two.rec.abc.def.ghi");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue