Remove UnqualifiedIdent

This commit is contained in:
Richard Feldman 2019-12-16 17:33:00 -05:00
parent db6f80f358
commit d73b8ca1fc
13 changed files with 35 additions and 119 deletions

View file

@ -668,7 +668,7 @@ pub fn canonicalize_expr(
let mut rec_field_types = SendMap::default(); let mut rec_field_types = SendMap::default();
rec_field_types.insert(Lowercase::from_unqualified_ident(field), field_type.clone()); rec_field_types.insert(Lowercase::from(*field), field_type.clone());
let record_type = Type::Record(rec_field_types, Box::new(ext_type)); let record_type = Type::Record(rec_field_types, Box::new(ext_type));
let record_expected = Expected::NoExpectation(record_type); let record_expected = Expected::NoExpectation(record_type);

View file

@ -1,4 +1,3 @@
use crate::ident::UnqualifiedIdent;
use std::fmt; use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@ -12,18 +11,18 @@ pub struct Lowercase(Box<str>);
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Uppercase(Box<str>); pub struct Uppercase(Box<str>);
impl Lowercase {
pub fn from_unqualified_ident(ident: &UnqualifiedIdent<'_>) -> Self {
Self(ident.as_str().into())
}
}
impl Into<Box<str>> for Lowercase { impl Into<Box<str>> for Lowercase {
fn into(self) -> Box<str> { fn into(self) -> Box<str> {
self.0 self.0
} }
} }
impl<'a> From<&'a str> for Lowercase {
fn from(string: &'a str) -> Self {
Self(string.into())
}
}
/// Rather than displaying as this: /// Rather than displaying as this:
/// ///
/// Lowercase("foo") /// Lowercase("foo")

View file

@ -295,7 +295,7 @@ fn desugar_field<'a>(
LabelOnly(loc_str) => { LabelOnly(loc_str) => {
// Desugar { x } into { x: x } // Desugar { x } into { x: x }
let loc_expr = Located { let loc_expr = Located {
value: Var(&[], loc_str.value.as_str()), value: Var(&[], loc_str.value),
region: loc_str.region, region: loc_str.region,
}; };

View file

@ -1,4 +1,3 @@
use crate::ident::UnqualifiedIdent;
use crate::module::ModuleName; use crate::module::ModuleName;
use std::fmt; use std::fmt;
@ -41,11 +40,8 @@ impl Symbol {
Symbol(format!("{}.{}", home, tag_name).into()) Symbol(format!("{}.{}", home, tag_name).into())
} }
pub fn from_module<'a>( pub fn from_module<'a>(module_name: &'a ModuleName<'a>, ident: &'a &'a str) -> Symbol {
module_name: &'a ModuleName<'a>, Symbol(format!("{}.{}", module_name.as_str(), ident).into())
ident: &'a UnqualifiedIdent<'a>,
) -> Symbol {
Symbol(format!("{}.{}", module_name.as_str(), ident.as_str()).into())
} }
pub fn from_qualified_ident(module_name: Box<str>, ident: Box<str>) -> Symbol { pub fn from_qualified_ident(module_name: Box<str>, ident: Box<str>) -> Symbol {

View file

@ -130,7 +130,7 @@ fn fmt_exposes_entry<'a>(buf: &mut String<'a>, entry: &'a ExposesEntry<'a>, inde
use crate::parse::ast::ExposesEntry::*; use crate::parse::ast::ExposesEntry::*;
match entry { match entry {
Ident(ident) => buf.push_str(ident.as_str()), Ident(ident) => buf.push_str(ident),
SpaceBefore(sub_entry, spaces) => { SpaceBefore(sub_entry, spaces) => {
fmt_spaces(buf, spaces.iter(), indent); fmt_spaces(buf, spaces.iter(), indent);

View file

@ -1,45 +1,5 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
/// An unqualified identifier, possibly capitalized.
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UnqualifiedIdent<'a>(&'a str);
impl<'a> Into<&'a str> for UnqualifiedIdent<'a> {
fn into(self) -> &'a str {
self.0
}
}
/// Rather than displaying as this:
///
/// UnqualifiedIdent("foo")
///
/// ...instead display as this:
///
/// 'foo'
impl<'a> fmt::Debug for UnqualifiedIdent<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "'{}'", self.0)
}
}
impl<'a> UnqualifiedIdent<'a> {
pub fn new(name: &'a str) -> Self {
// Unqualified idents must always start with a lowercase character.
debug_assert!(name
.chars()
.next()
.expect("UnqualifiedIdent was empty")
.is_alphabetic());
UnqualifiedIdent(name)
}
pub fn as_str(&'a self) -> &'a str {
self.0
}
}
/// 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

View file

@ -338,9 +338,9 @@ fn expose(
match entry { match entry {
Ident(ident) => { Ident(ident) => {
// Since this value is exposed, add it to our module's default scope. // Since this value is exposed, add it to our module's default scope.
let symbol = Symbol::from_module(&module_name, &ident); let symbol = Symbol::from_module(&module_name, ident);
(ident.as_str().into(), (symbol, region)) ((*ident).into(), (symbol, region))
} }
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => { SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
// Ignore spaces. // Ignore spaces.

View file

@ -1,4 +1,3 @@
use crate::ident::UnqualifiedIdent;
use crate::parse::ast::CommentOrNewline; use crate::parse::ast::CommentOrNewline;
use crate::region::Loc; use crate::region::Loc;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
@ -48,7 +47,7 @@ pub struct AppHeader<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Exposes<'a> { pub enum Exposes<'a> {
/// e.g. `Task` /// e.g. `Task`
Ident(UnqualifiedIdent<'a>), Ident(&'a str),
// Spaces // Spaces
SpaceBefore(&'a Exposes<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a Exposes<'a>, &'a [CommentOrNewline<'a>]),
@ -58,7 +57,7 @@ pub enum Exposes<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Imports<'a> { pub enum Imports<'a> {
/// e.g. `Task` or `Task.{ Task, after }` /// e.g. `Task` or `Task.{ Task, after }`
Ident(UnqualifiedIdent<'a>, Vec<'a, UnqualifiedIdent<'a>>), Ident(&'a str, Vec<'a, &'a str>),
// Spaces // Spaces
SpaceBefore(&'a Imports<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a Imports<'a>, &'a [CommentOrNewline<'a>]),

View file

@ -1,4 +1,3 @@
use crate::ident::UnqualifiedIdent;
use crate::module::ModuleName; use crate::module::ModuleName;
use crate::operator::CalledVia; use crate::operator::CalledVia;
use crate::operator::{BinOp, UnaryOp}; use crate::operator::{BinOp, UnaryOp};
@ -40,7 +39,7 @@ pub struct AppHeader<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ExposesEntry<'a> { pub enum ExposesEntry<'a> {
/// e.g. `Task` /// e.g. `Task`
Ident(UnqualifiedIdent<'a>), Ident(&'a str),
// Spaces // Spaces
SpaceBefore(&'a ExposesEntry<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a ExposesEntry<'a>, &'a [CommentOrNewline<'a>]),
@ -125,9 +124,9 @@ pub enum Expr<'a> {
Str(&'a str), Str(&'a str),
BlockStr(&'a [&'a str]), BlockStr(&'a [&'a str]),
/// Look up exactly one field on a record, e.g. (expr).foo. /// Look up exactly one field on a record, e.g. (expr).foo.
Access(&'a Expr<'a>, UnqualifiedIdent<'a>), Access(&'a Expr<'a>, &'a str),
/// e.g. `.foo` /// e.g. `.foo`
AccessorFunction(UnqualifiedIdent<'a>), AccessorFunction(&'a str),
// Collection Literals // Collection Literals
List(Vec<'a, &'a Loc<Expr<'a>>>), List(Vec<'a, &'a Loc<Expr<'a>>>),
@ -233,14 +232,10 @@ pub enum TypeAnnotation<'a> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum AssignedField<'a, Val> { pub enum AssignedField<'a, Val> {
// Both a label and a value, e.g. `{ name: "blah" }` // Both a label and a value, e.g. `{ name: "blah" }`
LabeledValue( LabeledValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Val>),
Loc<UnqualifiedIdent<'a>>,
&'a [CommentOrNewline<'a>],
&'a Loc<Val>,
),
// A label with no value, e.g. `{ name }` (this is sugar for { name: name }) // A label with no value, e.g. `{ name }` (this is sugar for { name: name })
LabelOnly(Loc<UnqualifiedIdent<'a>>), LabelOnly(Loc<&'a str>),
// We preserve this for the formatter; canonicalization ignores it. // We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]),

View file

@ -1,5 +1,4 @@
use crate::collections::arena_join; use crate::collections::arena_join;
use crate::ident::UnqualifiedIdent;
use crate::parse::ast::{Attempting, MaybeQualified}; use crate::parse::ast::{Attempting, MaybeQualified};
use crate::parse::parser::{unexpected, unexpected_eof, ParseResult, Parser, State}; use crate::parse::parser::{unexpected, unexpected_eof, ParseResult, Parser, State};
use bumpalo::collections::string::String; use bumpalo::collections::string::String;
@ -378,9 +377,6 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
global_tag_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, &'a str> {
map!( global_tag_or_ident(|first_char| first_char.is_alphabetic())
global_tag_or_ident(|first_char| first_char.is_alphabetic()),
UnqualifiedIdent::new
)
} }

View file

@ -10,7 +10,6 @@ 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::{
@ -224,7 +223,7 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
// Wrap the previous answer in the new one, so we end up // Wrap the previous answer in the new one, so we end up
// with a nested Expr. That way, `foo.bar.baz` gets represented // 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. // in the AST as if it had been written (foo.bar).baz all along.
value = Expr::Access(arena.alloc(value), UnqualifiedIdent::new(field)); value = Expr::Access(arena.alloc(value), field);
} }
Ok(( Ok((
@ -1171,12 +1170,12 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
// Wrap the previous answer in the new one, so we end up // Wrap the previous answer in the new one, so we end up
// with a nested Expr. That way, `foo.bar.baz` gets represented // 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. // in the AST as if it had been written (foo.bar).baz all along.
answer = Expr::Access(arena.alloc(answer), UnqualifiedIdent::new(field)); answer = Expr::Access(arena.alloc(answer), field);
} }
answer answer
} }
Ident::AccessorFunction(string) => Expr::AccessorFunction(UnqualifiedIdent::new(string)), Ident::AccessorFunction(string) => Expr::AccessorFunction(string),
Ident::Malformed(string) => Expr::MalformedIdent(string), Ident::Malformed(string) => Expr::MalformedIdent(string),
} }
} }

View file

@ -827,11 +827,9 @@ macro_rules! record_field {
'a, 'a,
$crate::parse::ast::AssignedField<'a, _>, $crate::parse::ast::AssignedField<'a, _>,
> { > {
use $crate::ident::UnqualifiedIdent;
use $crate::parse::ast::AssignedField::*; use $crate::parse::ast::AssignedField::*;
use $crate::parse::blankspace::{space0, space0_before}; use $crate::parse::blankspace::{space0, space0_before};
use $crate::parse::ident::lowercase_ident; use $crate::parse::ident::lowercase_ident;
use $crate::region::Located;
// You must have a field name, e.g. "email" // You must have a field name, e.g. "email"
let (loc_label, state) = loc!(lowercase_ident()).parse(arena, state)?; let (loc_label, state) = loc!(lowercase_ident()).parse(arena, state)?;
@ -845,30 +843,14 @@ macro_rules! record_field {
.parse(arena, state)?; .parse(arena, state)?;
let answer = match opt_loc_val { let answer = match opt_loc_val {
Some(loc_val) => LabeledValue( Some(loc_val) => LabeledValue(loc_label, spaces, arena.alloc(loc_val)),
Located {
value: UnqualifiedIdent::new(loc_label.value),
region: loc_label.region,
},
spaces,
arena.alloc(loc_val),
),
// If no value was provided, record it as a Var. // If no value was provided, record it as a Var.
// Canonicalize will know what to do with a Var later. // Canonicalize will know what to do with a Var later.
None => { None => {
if !spaces.is_empty() { if !spaces.is_empty() {
SpaceAfter( SpaceAfter(arena.alloc(LabelOnly(loc_label)), spaces)
arena.alloc(LabelOnly(Located {
value: UnqualifiedIdent::new(loc_label.value),
region: loc_label.region,
})),
spaces,
)
} else { } else {
LabelOnly(Located { LabelOnly(loc_label)
value: UnqualifiedIdent::new(loc_label.value),
region: loc_label.region,
})
} }
} }
}; };

View file

@ -17,7 +17,6 @@ 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;
@ -696,9 +695,8 @@ mod test_parse {
fn basic_field() { fn 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 field = UnqualifiedIdent::new("field");
let var = Var(module_parts, "rec"); let var = Var(module_parts, "rec");
let expected = Access(arena.alloc(var), field); let expected = Access(arena.alloc(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);
@ -708,9 +706,8 @@ mod test_parse {
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 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 = Access(arena.alloc(paren_var), field); 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);
@ -720,9 +717,8 @@ 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 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 = Access(arena.alloc(paren_var), field); 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);
@ -732,13 +728,10 @@ mod test_parse {
fn multiple_fields() { 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 abc = UnqualifiedIdent::new("abc");
let def = UnqualifiedIdent::new("def");
let ghi = UnqualifiedIdent::new("ghi");
let var = Var(module_parts, "rec"); let var = Var(module_parts, "rec");
let expected = Access( let expected = Access(
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), abc)), def)), arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
ghi, "ghi",
); );
let actual = parse_with(&arena, "rec.abc.def.ghi"); let actual = parse_with(&arena, "rec.abc.def.ghi");
@ -749,13 +742,10 @@ 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 abc = UnqualifiedIdent::new("abc");
let def = UnqualifiedIdent::new("def");
let ghi = UnqualifiedIdent::new("ghi");
let var = Var(module_parts, "rec"); let var = Var(module_parts, "rec");
let expected = Access( let expected = Access(
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), abc)), def)), arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
ghi, "ghi",
); );
let actual = parse_with(&arena, "One.Two.rec.abc.def.ghi"); let actual = parse_with(&arena, "One.Two.rec.abc.def.ghi");