Use UppercaseIdent over PlatformRigids

This commit is contained in:
Richard Feldman 2022-01-23 11:09:29 -05:00
parent 620e3f2913
commit c1c0ffb25f
10 changed files with 157 additions and 51 deletions

View file

@ -12,10 +12,11 @@ use roc_parse::ast::{
}; };
use roc_parse::header::{ use roc_parse::header::{
AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry, AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
PackageName, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent, PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
}; };
use roc_parse::{ use roc_parse::{
ast::{Def, Module}, ast::{Def, Module},
ident::UppercaseIdent,
module::{self, module_defs}, module::{self, module_defs},
parser::{Parser, SyntaxError}, parser::{Parser, SyntaxError},
state::State, state::State,
@ -285,7 +286,7 @@ impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
} }
} }
impl<'a> RemoveSpaces<'a> for PlatformRigid<'a> { impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self { fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self *self
} }

View file

@ -4,6 +4,7 @@ use crate::{
Buf, Buf,
}; };
use roc_parse::ast::{AliasHeader, AssignedField, Expr, Tag, TypeAnnotation}; use roc_parse::ast::{AliasHeader, AssignedField, Expr, Tag, TypeAnnotation};
use roc_parse::ident::UppercaseIdent;
use roc_region::all::Loc; use roc_region::all::Loc;
/// Does an AST node need parens around it? /// Does an AST node need parens around it?
@ -107,6 +108,22 @@ where
} }
} }
impl<'a> Formattable for UppercaseIdent<'a> {
fn is_multiline(&self) -> bool {
false
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: Parens,
_newlines: Newlines,
_indent: u16,
) {
buf.push_str((*self).into())
}
}
impl<'a> Formattable for TypeAnnotation<'a> { impl<'a> Formattable for TypeAnnotation<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
use roc_parse::ast::TypeAnnotation::*; use roc_parse::ast::TypeAnnotation::*;

View file

@ -6,7 +6,7 @@ use crate::Buf;
use roc_parse::ast::{Collection, Module, Spaced}; use roc_parse::ast::{Collection, Module, Spaced};
use roc_parse::header::{ use roc_parse::header::{
AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry, AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
PackageName, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent, PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
}; };
use roc_region::all::Loc; use roc_region::all::Loc;
@ -206,18 +206,6 @@ impl<'a, T: Formattable> Formattable for Spaced<'a, T> {
} }
} }
impl<'a> Formattable for PlatformRigid<'a> {
fn is_multiline(&self) -> bool {
false
}
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) {
buf.push_str(self.rigid);
buf.push_str("=>");
buf.push_str(self.alias);
}
}
fn fmt_imports<'a, 'buf>( fn fmt_imports<'a, 'buf>(
buf: &mut Buf<'buf>, buf: &mut Buf<'buf>,
loc_entries: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>, loc_entries: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,

View file

@ -1,6 +1,6 @@
use crate::ast::{Collection, CommentOrNewline, Spaced, StrLiteral, TypeAnnotation}; use crate::ast::{Collection, CommentOrNewline, Spaced, StrLiteral, TypeAnnotation};
use crate::blankspace::space0_e; use crate::blankspace::space0_e;
use crate::ident::lowercase_ident; use crate::ident::{lowercase_ident, UppercaseIdent};
use crate::parser::Progress::*; use crate::parser::Progress::*;
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser}; use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
use crate::state::State; use crate::state::State;
@ -126,15 +126,9 @@ pub struct PackageHeader<'a> {
pub after_imports: &'a [CommentOrNewline<'a>], pub after_imports: &'a [CommentOrNewline<'a>],
} }
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PlatformRigid<'a> {
pub rigid: &'a str,
pub alias: &'a str,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PlatformRequires<'a> { pub struct PlatformRequires<'a> {
pub rigids: Collection<'a, Loc<Spaced<'a, PlatformRigid<'a>>>>, pub rigids: Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>,
pub signature: Loc<Spaced<'a, TypedIdent<'a>>>, pub signature: Loc<Spaced<'a, TypedIdent<'a>>>,
} }

View file

@ -5,6 +5,23 @@ use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_region::all::Position; use roc_region::all::Position;
/// A global tag, for example. Must start with an uppercase letter
/// and then contain only letters and numbers afterwards - no dots allowed!
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct UppercaseIdent<'a>(&'a str);
impl<'a> From<&'a str> for UppercaseIdent<'a> {
fn from(string: &'a str) -> Self {
UppercaseIdent(string)
}
}
impl<'a> From<UppercaseIdent<'a>> for &'a str {
fn from(ident: UppercaseIdent<'a>) -> Self {
ident.0
}
}
/// 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 tag!" if you wrote `Foo = ...` or /// "you can't redefine this tag!" if you wrote `Foo = ...` or
@ -91,6 +108,21 @@ pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
} }
} }
/// This could be:
///
/// * A module name
/// * A type name
/// * A global tag
pub fn uppercase<'a>() -> impl Parser<'a, UppercaseIdent<'a>, ()> {
move |_, state: State<'a>| match chomp_uppercase_part(state.bytes()) {
Err(progress) => Err((progress, (), state)),
Ok(ident) => {
let width = ident.len();
Ok((MadeProgress, ident.into(), state.advance(width)))
}
}
}
/// This could be: /// This could be:
/// ///
/// * A module name /// * A module name

View file

@ -2,13 +2,13 @@ use crate::ast::{Collection, CommentOrNewline, Def, Module, Spaced};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::header::{ use crate::header::{
package_entry, package_name, AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader, package_entry, package_name, AppHeader, Effects, ExposedName, ImportsEntry, InterfaceHeader,
ModuleName, PackageEntry, PlatformHeader, PlatformRequires, PlatformRigid, To, TypedIdent, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, To, TypedIdent,
}; };
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase_ident, UppercaseIdent};
use crate::parser::Progress::{self, *}; use crate::parser::Progress::{self, *};
use crate::parser::{ use crate::parser::{
backtrackable, specialize, specialize_region, word1, word2, EEffects, EExposes, EHeader, backtrackable, specialize, specialize_region, word1, EEffects, EExposes, EHeader, EImports,
EImports, EPackages, EProvides, ERequires, ETypedIdent, Parser, SourceError, SyntaxError, EPackages, EProvides, ERequires, ETypedIdent, Parser, SourceError, SyntaxError,
}; };
use crate::state::State; use crate::state::State;
use crate::string_literal; use crate::string_literal;
@ -439,10 +439,13 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
#[inline(always)] #[inline(always)]
fn requires_rigids<'a>( fn requires_rigids<'a>(
min_indent: u32, min_indent: u32,
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, PlatformRigid<'a>>>>, ERequires<'a>> { ) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, ERequires<'a>> {
collection_trailing_sep_e!( collection_trailing_sep_e!(
word1(b'{', ERequires::ListStart), word1(b'{', ERequires::ListStart),
specialize(|_, pos| ERequires::Rigid(pos), loc!(requires_rigid())), specialize(
|_, pos| ERequires::Rigid(pos),
loc!(map!(ident::uppercase(), Spaced::Item))
),
word1(b',', ERequires::ListEnd), word1(b',', ERequires::ListEnd),
word1(b'}', ERequires::ListEnd), word1(b'}', ERequires::ListEnd),
min_indent, min_indent,
@ -453,17 +456,6 @@ fn requires_rigids<'a>(
) )
} }
#[inline(always)]
fn requires_rigid<'a>() -> impl Parser<'a, Spaced<'a, PlatformRigid<'a>>, ()> {
map!(
and!(
lowercase_ident(),
skip_first!(word2(b'=', b'>', |_| ()), uppercase_ident())
),
|(rigid, alias)| Spaced::Item(PlatformRigid { rigid, alias })
)
}
#[inline(always)] #[inline(always)]
fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>> { fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>> {
skip_first!( skip_first!(

View file

@ -5,15 +5,14 @@ Platform {
), ),
requires: PlatformRequires { requires: PlatformRequires {
rigids: [ rigids: [
@36-48 PlatformRigid { @36-41 UppercaseIdent(
rigid: "model", "Model",
alias: "Model", ),
},
], ],
signature: @52-61 TypedIdent { signature: @45-54 TypedIdent {
ident: @52-56 "main", ident: @45-49 "main",
spaces_before_colon: [], spaces_before_colon: [],
ann: @59-61 Record { ann: @52-54 Record {
fields: [], fields: [],
ext: None, ext: None,
}, },
@ -21,17 +20,17 @@ Platform {
}, },
exposes: [], exposes: [],
packages: [ packages: [
@94-106 PackageEntry { @87-99 PackageEntry {
shorthand: "foo", shorthand: "foo",
spaces_after_shorthand: [], spaces_after_shorthand: [],
package_name: @99-106 PackageName( package_name: @92-99 PackageName(
"./foo", "./foo",
), ),
}, },
], ],
imports: [], imports: [],
provides: [ provides: [
@139-150 ExposedName( @132-143 ExposedName(
"mainForHost", "mainForHost",
), ),
], ],

View file

@ -1,5 +1,5 @@
platform "foo/barbaz" platform "foo/barbaz"
requires {model=>Model} { main : {} } requires {Model} { main : {} }
exposes [] exposes []
packages { foo: "./foo" } packages { foo: "./foo" }
imports [] imports []

View file

@ -0,0 +1,77 @@
Platform {
header: PlatformHeader {
name: @9-21 PackageName(
"test/types",
),
requires: PlatformRequires {
rigids: [
@37-42 UppercaseIdent(
"Flags",
),
@44-49 UppercaseIdent(
"Model",
),
],
signature: @55-77 TypedIdent {
ident: @55-59 "main",
spaces_before_colon: [],
ann: @62-77 Apply(
"",
"App",
[
@66-71 Apply(
"",
"Flags",
[],
),
@72-77 Apply(
"",
"Model",
[],
),
],
),
},
},
exposes: [],
packages: [],
imports: [],
provides: [
@141-152 ExposedName(
"mainForHost",
),
],
effects: Effects {
spaces_before_effects_keyword: [
Newline,
],
spaces_after_effects_keyword: [],
spaces_after_type_name: [],
effect_shortname: "fx",
effect_type_name: "Effect",
entries: [],
},
before_header: [],
after_platform_keyword: [],
before_requires: [
Newline,
],
after_requires: [],
before_exposes: [
Newline,
],
after_exposes: [],
before_packages: [
Newline,
],
after_packages: [],
before_imports: [
Newline,
],
after_imports: [],
before_provides: [
Newline,
],
after_provides: [],
},
}

View file

@ -266,8 +266,14 @@ mod test_parse {
let result = func(&input); let result = func(&input);
let actual_result = if should_pass { let actual_result = if should_pass {
eprintln!("The source code for this test did not successfully parse!\n");
result.unwrap() result.unwrap()
} else { } else {
eprintln!(
"The source code for this test successfully parsed, but it was not expected to!\n"
);
result.unwrap_err() result.unwrap_err()
}; };