purescript-cst 0.4.0.0 → 0.5.0.0
raw patch · 6 files changed
+170/−20 lines, 6 filesdep ~basedep ~containersdep ~dlist
Dependency ranges changed: base, containers, dlist, microlens, scientific, semigroups, serialise, vector
Files
- README.md +3/−0
- purescript-cst.cabal +9/−9
- src/Language/PureScript/AST/Declarations.hs +2/−1
- src/Language/PureScript/CST/Layout.hs +150/−2
- src/Language/PureScript/Environment.hs +2/−7
- src/Language/PureScript/Names.hs +4/−1
README.md view
@@ -11,6 +11,9 @@ | 0.14.2 | 0.2.0.0 | | 0.14.3 | 0.3.0.0 | | 0.14.4 | 0.4.0.0 |+| 0.14.5 | 0.4.0.0 |+| 0.14.6 | 0.4.0.0 |+| 0.14.7 | 0.5.0.0 | Before v0.14.2, there was a third package, `purescript-ast`. In v0.14.2, `purescript-ast` was merged into `purescript-cst`.
purescript-cst.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: purescript-cst-version: 0.4.0.0+version: 0.5.0.0 license: BSD-3-Clause license-file: LICENSE copyright:@@ -83,18 +83,18 @@ build-depends: aeson >=1.5.6.0 && <1.6, array >=0.5.4.0 && <0.6,- base >=4.14.1.0 && <4.15,+ base >=4.14.3.0 && <4.15, base-compat >=0.11.2 && <0.12, bytestring >=0.10.12.0 && <0.11,- containers >=0.6.2.1 && <0.7,+ containers >=0.6.5.1 && <0.7, deepseq >=1.4.4.0 && <1.5,- dlist >=0.8.0.8 && <0.9,+ dlist ==1.0.*, filepath >=1.4.2.1 && <1.5,- microlens >=0.4.11.2 && <0.5,+ microlens >=0.4.12.0 && <0.5, mtl >=2.2.2 && <2.3, protolude >=0.3.0 && <0.4,- scientific >=0.3.6.2 && <0.4,- semigroups >=0.19.1 && <0.20,+ scientific >=0.3.7.0 && <0.4,+ semigroups >=0.19.2 && <0.20,+ serialise >=0.2.4.0 && <0.3, text >=1.2.4.1 && <1.3,- serialise >=0.2.3.0 && <0.3,- vector >=0.12.1.2 && <0.13+ vector >=0.12.3.1 && <0.13
src/Language/PureScript/AST/Declarations.hs view
@@ -97,6 +97,7 @@ | CheckHint | PositionHint | SolverHint+ | DeclarationHint | OtherHint deriving (Show, Eq) @@ -504,13 +505,13 @@ declName (FixityDeclaration _ (Right (TypeFixity _ _ n))) = Just (TyOpName n) declName (TypeClassDeclaration _ n _ _ _ _) = Just (TyClassName n) declName (TypeInstanceDeclaration _ _ _ n _ _ _ _) = IdentName <$> hush n+declName (RoleDeclaration RoleDeclarationData{..}) = Just (TyName rdeclIdent) declName ImportDeclaration{} = Nothing declName BindingGroupDeclaration{} = Nothing declName DataBindingGroupDeclaration{} = Nothing declName BoundValueDeclaration{} = Nothing declName KindDeclaration{} = Nothing declName TypeDeclaration{} = Nothing-declName RoleDeclaration{} = Nothing -- | -- Test if a declaration is a value declaration
src/Language/PureScript/CST/Layout.hs view
@@ -1,4 +1,11 @@--- | The parser itself is unaware of indentation, and instead only parses explicit+-- |+-- ## High-Level Summary+--+-- This section provides a high-level summary of this file. For those who+-- know more about compiler-development, the below explanation is likely enough.+-- For everyone else, see the next section.+--+-- The parser itself is unaware of indentation, and instead only parses explicit -- delimiters which are inserted by this layout algorithm (much like Haskell). -- This is convenient because the actual grammar can be specified apart from the -- indentation rules. Haskell has a few problematic productions which make it@@ -17,7 +24,148 @@ -- such an algorithm. Unquoted properties for layout keywords introduce a domino -- effect of complexity since we have to mask and unmask any usage of . (also in -- foralls!) or labels in record literals.-+--+-- ## Detailed Summary+--+-- ### The Problem+--+-- The parser itself is unaware of indentation or other such layout concerns.+-- Rather than dealing with it explicitly, the parser and its+-- grammar rules are only aware of normal tokens (e.g. @TokLowerName@) and+-- three special zero-width tokens, @TokLayoutStart@, @TokLayoutSep@,+-- and @TokLayoutEnd@. This is convenient because the actual grammar+-- can be specified apart from the indentation rules and other such+-- layout concerns.+--+-- For a simple example, the parser parses all three examples of the code below+-- using the exact same grammar rules for the @let@ keyword despite+-- each example using different indentations levels:+--+-- @+-- -- Example 1+-- let foo = 5+-- x = 2 in foo+--+-- -- Example 2+-- let+-- bar = 5+-- y = 2+-- in bar+--+-- -- Example 3+-- let baz+-- =+-- 5+-- z= 2 in baz+-- @+--+-- Each block of code might appear to the parser as a stream of the+-- following source tokens where the @\{@ sequence represents+-- @TokLayoutStart@, the @\;@ sequence represents @TokLayoutSep@,+-- and the @\}@ sequence represents @TokLayoutEnd@:+-- - @let \{foo = 5\;x = 2\} in foo@+-- - @let \{bar = 5\;y = 2\} in bar@+-- - @let \{baz = 5\;z = 2\} in baz@+--+--+-- For a more complex example, consider commas:+--+-- @+-- case one, { twoA, twoB }, [ three1+-- , three2+-- , do+-- { three3, three4 } <- case arg1, arg2 of+-- Nothing, _ -> { three3: 1, three4: 2 }+-- Just _, Nothing -> { three3: 2, three4: 3 }+-- _, _ -> { three3: 3, three4: 4 }+-- pure $ three3 + three4+-- ] of+-- @+--+-- Which of the above 13 commas function as the separaters between the+-- case binders (e.g. @one@) in the outermost @case ... of@ context?+--+-- ### The Solution+--+-- The parser doesn't have to care about layout concerns (e.g. indentation+-- or what starts and ends a context, such as a case binder) because the+-- lexer solves that problem instead.+--+-- So, how does the lexer solve this problem? It follows this general algorithm:+-- 1. Lex the source code text into an initial stream of `SourceToken`s+-- that do not have any of the three special tokens mentioned previously.+-- 2. On a token-by-token basis, determine whether the lexer should+-- 1. insert one of the three special tokens,+-- 2. modify the current context (e.g. are we within a case binder?+-- Are we in a record expression?)+--+-- Step 2 is handled via 'insertLayout' and is essentially a state machine.+-- The layout delimiters, (e.g. 'LytCase', 'LytBrace', 'LytProperty',+-- and 'LytOf' in the next section's example) either stop certain "rules"+-- from applying or ensure that certain "rules" now apply. By "rules",+-- we mean whether and where one of the three special tokens are added.+-- The comments in the source code for the 'insertLayout' algorithm call+-- pushing these delimiters onto the stack "masking" and popping them off+-- as "unmasking". Seeing when a layout delimiter is pushed and popped+-- are the keys to understanding this algorithm.+--+-- ### Walking Through an Example+--+-- Before showing an example, let's remember a few things.+-- 1. The @TokLowerName "case"@ token (i.e. a "case" keyword) indicates the start+-- of a @case ... of@ context. That context includes case binders (like the+-- example shown previously) that can get quite complex. When encountered,+-- we may need to insert one or more of the three special tokens here+-- until we encounter the terminating @TokLowerName "of"@ token that+-- signifies its end.+-- 2. "case" and "of" can also appear as a record field's name. In such a context,+-- they would not start or end a @case ... of@ block.+--+-- Given the below source code...+--+-- @+-- case { case: "foo", of: "bar" } of+-- @+--+-- the lexer would go through something like the following states:+-- 1. Encountered @TokLowerName "case"@. Update current context to+-- "within a case of expression" by pushing the 'LytCase' delimiter+-- onto the layout delimiter stack. Insert the @case@ token+-- into the stream of source tokens.+-- 2. Encountered @TokLeftBrace@. Update current context to+-- "within a record expression" by pushing the 'LytBrace' delimiter.+-- Since we expect a field name to be the next token we see,+-- which may include a reserved keyword, update the current context again to+-- "expecting a field name" by pushing the `LytProperty`.+-- delimiter. Insert the @{@ token into the stream of source tokens.+-- 3. Encountered @TokLowerName "case"@. Check the current context.+-- Since it's a `LytProperty`, this is a field name and we shouldn't+-- assume that the next few tokens will be case binders. However,+-- since this might be a record with no more fields, update the+-- current context back to "within a record expression" by popping+-- the `LytProperty` off the layout delimiter stack. Insert the @case@ token+-- 4. Encountered @TokColon@. Insert the @:@ token+-- 5. Encountered @TokLowerName "foo"@. Insert the @foo@ token.+-- 6. Encountered @TokComma@. Check the current context. Since it's a `LytBrace`,+-- we're in a record expression and there is another field. Update the+-- current context by pushing `LytProperty` as we expect a field name again.+-- 7. Encountered @TokLowerName "of"@. Check the current context.+-- Since it's a `LytProperty`, this is a field name rather+-- than the end of a case binder. Thus, we don't expect the next tokens+-- to be the @body@ in a @case ... of body@ expression. However, since+-- this might be a record with no more fields, update the current context+-- back to "within a record expression" by popping the `LytProperty`+-- off the stack. Insert the @of@ token.+-- 8. Encountered @TokRightBrace@. Check the current context.+-- Since it's a `LytBrace`, this is the end of a record expression.+-- Update the current context to "within a case of expression"+-- by popping the `LytBrace` off the stack. Insert the @}@ token.+-- 9. Encountered @TokLowername "of"@. Check the current context.+-- Since it's a 'LytCase', this is the end of a @case ... of@ expression+-- and the body will follow. Update the current context to+-- "body of a case of expression" by pushing 'LytOf' onto the layout stack.+-- Insert the @of@ token into the stream of tokens.+-- module Language.PureScript.CST.Layout where import Prelude
src/Language/PureScript/Environment.hs view
@@ -35,11 +35,6 @@ , dataConstructors :: M.Map (Qualified (ProperName 'ConstructorName)) (DataDeclType, ProperName 'TypeName, SourceType, [Ident]) -- ^ Data constructors currently in scope, along with their associated type -- constructor name, argument types and return type.- , roleDeclarations :: M.Map (Qualified (ProperName 'TypeName)) [Role]- -- ^ Explicit role declarations currently in scope. Note that this field is- -- only used to store declared roles temporarily until they can be checked;- -- to find a type's real checked and/or inferred roles, refer to the TypeKind- -- in the `types` field. , typeSynonyms :: M.Map (Qualified (ProperName 'TypeName)) ([(Text, Maybe SourceType)], SourceType) -- ^ Type synonyms currently in scope , typeClassDictionaries :: M.Map (Maybe ModuleName) (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict)))@@ -103,7 +98,7 @@ -- | The initial environment with no values and only the default javascript types defined initEnvironment :: Environment-initEnvironment = Environment M.empty allPrimTypes M.empty M.empty M.empty M.empty allPrimClasses+initEnvironment = Environment M.empty allPrimTypes M.empty M.empty M.empty allPrimClasses -- | A constructor for TypeClassData that computes which type class arguments are fully determined -- and argument covering sets.@@ -434,7 +429,7 @@ , (primSubName C.typeError "Fail", (kindDoc -:> kindConstraint, ExternData [Nominal])) , (primSubName C.typeError "Warn", (kindDoc -:> kindConstraint, ExternData [Nominal])) , (primSubName C.typeError "Text", (kindSymbol -:> kindDoc, ExternData [Phantom]))- , (primSubName C.typeError "Quote", (kindType -:> kindDoc, ExternData [Phantom]))+ , (primSubName C.typeError "Quote", (tyForall "k" kindType $ tyVar "k" -:> kindDoc, ExternData [Phantom])) , (primSubName C.typeError "QuoteLabel", (kindSymbol -:> kindDoc, ExternData [Phantom])) , (primSubName C.typeError "Beside", (kindDoc -:> kindDoc -:> kindDoc, ExternData [Phantom, Phantom])) , (primSubName C.typeError "Above", (kindDoc -:> kindDoc -:> kindDoc, ExternData [Phantom, Phantom]))
src/Language/PureScript/Names.hs view
@@ -78,11 +78,14 @@ instance NFData Ident instance Serialise Ident +unusedIdent :: Text+unusedIdent = "$__unused"+ runIdent :: Ident -> Text runIdent (Ident i) = i runIdent (GenIdent Nothing n) = "$" <> T.pack (show n) runIdent (GenIdent (Just name) n) = "$" <> name <> T.pack (show n)-runIdent UnusedIdent = "$__unused"+runIdent UnusedIdent = unusedIdent showIdent :: Ident -> Text showIdent = runIdent