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();
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_expected = Expected::NoExpectation(record_type);

View file

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

View file

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

View file

@ -1,4 +1,3 @@
use crate::ident::UnqualifiedIdent;
use crate::module::ModuleName;
use std::fmt;
@ -41,11 +40,8 @@ impl Symbol {
Symbol(format!("{}.{}", home, tag_name).into())
}
pub fn from_module<'a>(
module_name: &'a ModuleName<'a>,
ident: &'a UnqualifiedIdent<'a>,
) -> Symbol {
Symbol(format!("{}.{}", module_name.as_str(), ident.as_str()).into())
pub fn from_module<'a>(module_name: &'a ModuleName<'a>, ident: &'a &'a str) -> Symbol {
Symbol(format!("{}.{}", module_name.as_str(), ident).into())
}
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::*;
match entry {
Ident(ident) => buf.push_str(ident.as_str()),
Ident(ident) => buf.push_str(ident),
SpaceBefore(sub_entry, spaces) => {
fmt_spaces(buf, spaces.iter(), indent);

View file

@ -1,45 +1,5 @@
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
/// e.g. (Http.Request from http)
/// Parameterized on a phantom marker for whether it has been canonicalized

View file

@ -338,9 +338,9 @@ fn expose(
match entry {
Ident(ident) => {
// 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, _) => {
// Ignore spaces.

View file

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

View file

@ -1,4 +1,3 @@
use crate::ident::UnqualifiedIdent;
use crate::module::ModuleName;
use crate::operator::CalledVia;
use crate::operator::{BinOp, UnaryOp};
@ -40,7 +39,7 @@ pub struct AppHeader<'a> {
#[derive(Clone, Debug, PartialEq)]
pub enum ExposesEntry<'a> {
/// e.g. `Task`
Ident(UnqualifiedIdent<'a>),
Ident(&'a str),
// Spaces
SpaceBefore(&'a ExposesEntry<'a>, &'a [CommentOrNewline<'a>]),
@ -125,9 +124,9 @@ pub enum Expr<'a> {
Str(&'a str),
BlockStr(&'a [&'a str]),
/// 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`
AccessorFunction(UnqualifiedIdent<'a>),
AccessorFunction(&'a str),
// Collection Literals
List(Vec<'a, &'a Loc<Expr<'a>>>),
@ -233,14 +232,10 @@ pub enum TypeAnnotation<'a> {
#[derive(Debug, Clone, PartialEq)]
pub enum AssignedField<'a, Val> {
// Both a label and a value, e.g. `{ name: "blah" }`
LabeledValue(
Loc<UnqualifiedIdent<'a>>,
&'a [CommentOrNewline<'a>],
&'a Loc<Val>,
),
LabeledValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc<Val>),
// 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.
SpaceBefore(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]),

View file

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

View file

@ -10,7 +10,6 @@ pub mod problems;
pub mod string_literal;
pub mod type_annotation;
use crate::ident::UnqualifiedIdent;
use crate::operator::{BinOp, CalledVia, UnaryOp};
use crate::parse::ast::{AssignedField, Attempting, Def, Expr, MaybeQualified, Pattern, Spaceable};
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
// 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));
value = Expr::Access(arena.alloc(value), field);
}
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
// 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 = Expr::Access(arena.alloc(answer), field);
}
answer
}
Ident::AccessorFunction(string) => Expr::AccessorFunction(UnqualifiedIdent::new(string)),
Ident::AccessorFunction(string) => Expr::AccessorFunction(string),
Ident::Malformed(string) => Expr::MalformedIdent(string),
}
}

View file

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

View file

@ -17,7 +17,6 @@ mod test_parse {
use crate::helpers::parse_with;
use bumpalo::collections::vec::Vec;
use bumpalo::{self, Bump};
use roc::ident::UnqualifiedIdent;
use roc::module::ModuleName;
use roc::operator::BinOp::*;
use roc::operator::CalledVia;
@ -696,9 +695,8 @@ mod test_parse {
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 expected = Access(arena.alloc(var), "field");
let actual = parse_with(&arena, "rec.field");
assert_eq!(Ok(expected), actual);
@ -708,9 +706,8 @@ mod test_parse {
fn parenthetical_basic_field() {
let arena = Bump::new();
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 expected = Access(arena.alloc(paren_var), field);
let expected = Access(arena.alloc(paren_var), "field");
let actual = parse_with(&arena, "(rec).field");
assert_eq!(Ok(expected), actual);
@ -720,9 +717,8 @@ mod test_parse {
fn parenthetical_field_qualified_var() {
let arena = Bump::new();
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 expected = Access(arena.alloc(paren_var), field);
let expected = Access(arena.alloc(paren_var), "field");
let actual = parse_with(&arena, "(One.Two.rec).field");
assert_eq!(Ok(expected), actual);
@ -732,13 +728,10 @@ mod test_parse {
fn multiple_fields() {
let arena = Bump::new();
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 expected = Access(
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), abc)), def)),
ghi,
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
"ghi",
);
let actual = parse_with(&arena, "rec.abc.def.ghi");
@ -749,13 +742,10 @@ mod test_parse {
fn qualified_field() {
let arena = Bump::new();
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 expected = Access(
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), abc)), def)),
ghi,
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
"ghi",
);
let actual = parse_with(&arena, "One.Two.rec.abc.def.ghi");