From d73b8ca1fcfd740f1875e6590375db84084dc76e Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 16 Dec 2019 17:33:00 -0500 Subject: [PATCH] Remove UnqualifiedIdent --- src/can/expr.rs | 2 +- src/can/ident.rs | 13 ++++++------- src/can/operator.rs | 2 +- src/can/symbol.rs | 8 ++------ src/fmt/module.rs | 2 +- src/ident.rs | 40 ---------------------------------------- src/load/mod.rs | 4 ++-- src/module.rs | 5 ++--- src/parse/ast.rs | 15 +++++---------- src/parse/ident.rs | 8 ++------ src/parse/mod.rs | 7 +++---- src/parse/parser.rs | 24 +++--------------------- tests/test_parse.rs | 24 +++++++----------------- 13 files changed, 35 insertions(+), 119 deletions(-) diff --git a/src/can/expr.rs b/src/can/expr.rs index e4b44106b4..97a531889e 100644 --- a/src/can/expr.rs +++ b/src/can/expr.rs @@ -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); diff --git a/src/can/ident.rs b/src/can/ident.rs index a3b45eb256..3b224a5af7 100644 --- a/src/can/ident.rs +++ b/src/can/ident.rs @@ -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); #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Uppercase(Box); -impl Lowercase { - pub fn from_unqualified_ident(ident: &UnqualifiedIdent<'_>) -> Self { - Self(ident.as_str().into()) - } -} - impl Into> for Lowercase { fn into(self) -> Box { 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") diff --git a/src/can/operator.rs b/src/can/operator.rs index f14800437c..692688c619 100644 --- a/src/can/operator.rs +++ b/src/can/operator.rs @@ -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, }; diff --git a/src/can/symbol.rs b/src/can/symbol.rs index 086416d4f8..6f9cee9852 100644 --- a/src/can/symbol.rs +++ b/src/can/symbol.rs @@ -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, ident: Box) -> Symbol { diff --git a/src/fmt/module.rs b/src/fmt/module.rs index 1df0505129..776dd9f1f6 100644 --- a/src/fmt/module.rs +++ b/src/fmt/module.rs @@ -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); diff --git a/src/ident.rs b/src/ident.rs index fc6c996373..131fecccfb 100644 --- a/src/ident.rs +++ b/src/ident.rs @@ -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 diff --git a/src/load/mod.rs b/src/load/mod.rs index adcf9eb9e2..9e4d786ff9 100644 --- a/src/load/mod.rs +++ b/src/load/mod.rs @@ -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. diff --git a/src/module.rs b/src/module.rs index 4f703291c2..c22742cef7 100644 --- a/src/module.rs +++ b/src/module.rs @@ -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>]), diff --git a/src/parse/ast.rs b/src/parse/ast.rs index 297cdf7e57..30560f713e 100644 --- a/src/parse/ast.rs +++ b/src/parse/ast.rs @@ -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>>), @@ -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>, - &'a [CommentOrNewline<'a>], - &'a Loc, - ), + LabeledValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc), // A label with no value, e.g. `{ name }` (this is sugar for { name: name }) - LabelOnly(Loc>), + LabelOnly(Loc<&'a str>), // We preserve this for the formatter; canonicalization ignores it. SpaceBefore(&'a AssignedField<'a, Val>, &'a [CommentOrNewline<'a>]), diff --git a/src/parse/ident.rs b/src/parse/ident.rs index f41ea3563f..1841e7bdcc 100644 --- a/src/parse/ident.rs +++ b/src/parse/ident.rs @@ -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()) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 99dccc5729..039abf234f 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -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(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), } } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index a69ac75dff..46221955fc 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -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) } } }; diff --git a/tests/test_parse.rs b/tests/test_parse.rs index 0ade82de38..9fd492fbe6 100644 --- a/tests/test_parse.rs +++ b/tests/test_parse.rs @@ -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");