diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2024-09-25 22:51:32 -0600 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2024-09-25 22:51:32 -0600 |
commit | 0274c964874801d7cbde8f13fa13e11ed7948660 (patch) | |
tree | 97d72203edc5f7c4f4ea073166a35d3191a4c06a | |
parent | fffe42ce4861f53dd86113ab8320e4754f2c570c (diff) | |
download | fiddle-0274c964874801d7cbde8f13fa13e11ed7948660.tar.gz fiddle-0274c964874801d7cbde8f13fa13e11ed7948660.tar.bz2 fiddle-0274c964874801d7cbde8f13fa13e11ed7948660.zip |
feat: Add AdvanceStage typeclass and refactor code to use it
Introduced the `AdvanceStage` typeclass, which provides a mechanism to
transition AST elements between different compilation stages. This
abstraction facilitates easier traversal and modification of the syntax
tree as it progresses through various compilation phases.
-rw-r--r-- | src/Language/Fiddle/Ast/FileInterface.hs | 55 | ||||
-rw-r--r-- | src/Language/Fiddle/Ast/Internal/Instances.hs | 336 | ||||
-rw-r--r-- | src/Language/Fiddle/Ast/Internal/SyntaxTree.hs | 581 | ||||
-rw-r--r-- | src/Language/Fiddle/Compiler/Stage1.hs | 355 | ||||
-rw-r--r-- | src/Language/Fiddle/Compiler/Stage2.hs | 644 | ||||
-rw-r--r-- | src/Language/Fiddle/GenericTree.hs | 39 | ||||
-rw-r--r-- | src/Language/Fiddle/Internal/Scopes.hs | 101 | ||||
-rw-r--r-- | src/Language/Fiddle/Parser.hs | 240 | ||||
-rw-r--r-- | src/Main.hs | 31 |
9 files changed, 1334 insertions, 1048 deletions
diff --git a/src/Language/Fiddle/Ast/FileInterface.hs b/src/Language/Fiddle/Ast/FileInterface.hs index d29fc9d..c1cfac8 100644 --- a/src/Language/Fiddle/Ast/FileInterface.hs +++ b/src/Language/Fiddle/Ast/FileInterface.hs @@ -6,28 +6,33 @@ module Language.Fiddle.Ast.FileInterface where -- the import statements should supply an fdi (fiddle interface) file to speed -- up subsequent compilations. --- import Data.Text --- --- data ObjectType = ObjectType --- { objectTypeSize :: Word32 --- } --- --- data Metatype --- = Object --- { objectLocation :: Word64, --- objectType :: Text --- } --- | Type --- { typeSizeBytes :: Word32 --- } --- --- data Element a = Element --- { elementFullyQualifiedSymbol :: Text, --- elementDocumentation :: Maybe Text, --- elementMetatype :: Metatype, --- elementAnnotation :: a --- } --- --- data FileInterface a = FiddleInterface --- { exportedElements :: [Element a] --- } +import Data.Text +import Data.Word + +data ObjectType = ObjectType + { objectTypeSize :: Word32 + } + +data Metatype + = Object + { objectLocation :: Word64, + objectType :: Text + } + | Type + { typeSizeBytes :: Word32 + } + +data Element a = Element + { elementFullyQualifiedSymbol :: Text, + elementDocumentation :: Maybe Text, + elementMetatype :: Metatype, + elementAnnotation :: a + } + +data ResolvedImport a = ResolvedImport { + dependencies :: [String] +} + +data FileInterface a = FiddleInterface + { exportedElements :: [Element a] + } diff --git a/src/Language/Fiddle/Ast/Internal/Instances.hs b/src/Language/Fiddle/Ast/Internal/Instances.hs index c8c606c..2f3707e 100644 --- a/src/Language/Fiddle/Ast/Internal/Instances.hs +++ b/src/Language/Fiddle/Ast/Internal/Instances.hs @@ -15,27 +15,6 @@ import GHC.TypeError as TypeError import Language.Fiddle.Ast.Internal.Kinds import Language.Fiddle.Ast.Internal.Stage --- Type class to easily switch between stages if there is no difference in the --- syntax tree structure between these stages. Can make things much cleaner and --- avoids the boilerplate and bugprone-ness of needing to rote copy everything. -class - (Functor f) => - EasySwitchStage - (t :: Stage -> SynTree) - (f :: Type -> Type) - (fromStage :: Stage) - (toStage :: Stage) - where - switchStage :: t fromStage f a -> t toStage f a - default switchStage :: - ( Generic (t fromStage f a), - Generic (t toStage f a), - GEasySwitchStage (Rep (t fromStage f a)) (Rep (t toStage f a)) - ) => - t fromStage f a -> - t toStage f a - switchStage t = to $ gSwitchStage (from t) - -- Class for walking a syntax tree under the context of a monad and modifying -- the different parts of the SynTree type.. class Alter (t :: SynTree) where @@ -63,6 +42,154 @@ class Alter (t :: SynTree) where m (t f2 a2) alter ffn fn t = to <$> galter (proxyOf t) ffn fn (from t) +-- | 'CompilationStage' is a type class representing a stage in the compiler +-- pipeline. Each 'stage' has associated types that define how it transforms +-- syntax trees, manages state, and handles annotations. +-- +-- This class requires: +-- * A 'StageMonad' for handling monadic actions during the compilation process. +-- * A 'StageFunctor' for traversing the syntax tree. +-- * A 'StageState' to represent local state information during traversal. +class + (Monad (StageMonad stage), Traversable (StageFunctor stage)) => + CompilationStage stage + where + -- | The next stage in the compilation pipeline. + type StageAfter stage :: Stage + + -- | The monadic context for this stage (e.g., 'Compile' or 'Either'). + type StageMonad stage :: Type -> Type + + -- | The state type associated with this stage. This state is designed to be + -- used as a 'local state', meaning any modifications to this state are + -- only visible to child nodes during the traversal. To share state across + -- sibling nodes, use the 'StageMonad'. + type StageState stage :: Type + + -- | The functor used for traversing the syntax tree during this stage. + type StageFunctor stage :: Type -> Type + + -- | The type of annotations associated with nodes in the syntax tree. + type StageAnnotation stage :: Type + +-- | Utility type that captures the structure of a syntax tree node for a given +-- stage. This type alias simplifies references to the full tree structure in +-- other parts of the code. +type TreeType (t :: StagedSynTree) (s :: Stage) = + t s (StageFunctor s) (StageAnnotation s) + +-- | 'AdvanceStage' defines how to transform an Abstract Syntax Tree (AST) node +-- from one stage to the next in the compiler pipeline. This transformation +-- can be customized per node type, or a default generic implementation can be +-- used. +class + (CompilationStage stage) => + AdvanceStage (stage :: Stage) (t :: StagedSynTree) + where + -- | 'advanceStage' transitions a syntax tree node from the current stage to + -- the next stage ('StageAfter stage'). It takes the current local state + -- ('StageState') and the syntax tree node ('TreeType') as input and returns + -- a monadic action that produces the transformed node for the next stage. + -- + -- This function typically performs tree transformations, checks, or other + -- modifications necessary for the compilation process. + -- + -- Parameters: + -- * 'StageState stage' - The local state for this stage of the compiler. + -- This state is only visible to child nodes. Any changes do not affect + -- sibling nodes. + -- * 'TreeType t stage' - The syntax tree node at the current stage. + -- + -- Returns: A monadic action that produces the transformed syntax tree node + -- at the next stage. + advanceStage :: + StageState stage -> -- Local state for the current stage + TreeType t stage -> -- Syntax tree node at the current stage + StageMonad + stage + ( t + (StageAfter stage) -- The next stage in the pipeline + (StageFunctor stage) -- The functor associated with the next stage + (StageAnnotation stage) -- Annotation type for the next stage + ) + + -- | Default implementation of 'advanceStage' using generics. This leverages + -- 'GAdvanceStage' to automatically traverse and transform the syntax tree. + -- If an AST type derives 'Generic', this default can be used to reduce + -- boilerplate code. + -- + -- Before performing the generic transformation with 'gAdvanceStage', the + -- 'modifyState' function is called to potentially alter the local state + -- based on the current node. + -- + -- This implementation is useful for cases where the tree structure remains + -- mostly unchanged between stages, and only the state or annotations need + -- to be adjusted. + default advanceStage :: + ( GAdvanceStage + (StageState stage) -- The local state for this stage + (StageMonad stage) -- The monadic context of this stage + (Rep (TreeType t stage)) -- Generic representation of the current tree type + (Rep (t (StageAfter stage) (StageFunctor stage) (StageAnnotation stage))), -- Generic representation of the next stage's tree type + Generic (TreeType t stage), -- The current tree type must be an instance of 'Generic' + Generic + ( t + (StageAfter stage) -- The tree type at the next stage + (StageFunctor stage) -- The functor for the next stage + (StageAnnotation stage) -- The annotation type for the next stage + ) + ) => + StageState stage -> -- Local state for the current stage + TreeType t stage -> -- Syntax tree node at the current stage + StageMonad + stage + ( t + (StageAfter stage) -- The tree type for the next stage + (StageFunctor stage) -- The functor for the next stage + (StageAnnotation stage) -- The annotation type for the next stage + ) + advanceStage s t = do + -- Modify the local state for this node before performing the transformation + s' <- modifyState t s + -- Perform the generic transformation using 'gAdvanceStage' + to <$> gAdvanceStage s' (from t) + + -- | 'modifyState' allows for changes to the local state ('StageState') before + -- transforming the syntax tree node. This is called on each node during the + -- traversal, allowing the state to be adjusted based on the current node's + -- information. + -- + -- The default implementation simply returns the unchanged state, but it can + -- be overridden to implement custom state modifications. + -- + -- Note: This state modification is local to the current node and its + -- children. Changes to this state are not visible to sibling nodes. If + -- information needs to be shared across siblings, consider using the + -- 'StageMonad' for that purpose. + -- + -- 'modifyState' is only called by the default implementation of + -- advanceStage. If 'advaceStage' is overridden, then overriding this + -- function is superfluous. + -- + -- Parameters: + -- * 'TreeType t stage' - The syntax tree node at the current stage. + -- * 'StageState stage' - The local state for this stage. + -- + -- Returns: A monadic action that produces the (potentially modified) state. + modifyState :: + TreeType t stage -> -- Syntax tree node at the current stage + StageState stage -> -- Local state for the current stage + StageMonad stage (StageState stage) -- The modified local state + modifyState _ = return + +-- | 'GAdvanceStage' is a helper type class that performs the transformation +-- of the generic representation of a syntax tree node. It is used by the +-- default implementation of 'advanceStage' to traverse and modify nodes +-- automatically. +class GAdvanceStage s m from to where + gAdvanceStage :: s -> from x -> m (to x) + + -- A syntax tree object is annotated if it has an annotation 'a' as the last -- element. class Annotated (t :: SynTree) where @@ -170,59 +297,154 @@ instance (GAnnot a r, GAnnot a l) => GAnnot a (l :+: r) where instance (GAnnot a r) => GAnnot a (M1 i c r) where gannot (M1 a) = gannot a +-- instance +-- (GEasySwitchStage s1 s2) => +-- GEasySwitchStage (M1 i c s1) (M1 i c s2) +-- where +-- gSwitchStage (M1 a) = M1 (gSwitchStage a) +-- +-- instance +-- (GEasySwitchStage l1 l2, GEasySwitchStage r1 r2) => +-- GEasySwitchStage (l1 :+: r1) (l2 :+: r2) +-- where +-- gSwitchStage (R1 r) = R1 $ gSwitchStage r +-- gSwitchStage (L1 l) = L1 $ gSwitchStage l +-- +-- instance +-- (GEasySwitchStage l1 l2, GEasySwitchStage r1 r2) => +-- (GEasySwitchStage (l1 :*: r1) (l2 :*: r2)) +-- where +-- gSwitchStage (l :*: r) = gSwitchStage l :*: gSwitchStage r +-- +-- instance +-- (EasySwitchStage t f fs ts) => +-- (GEasySwitchStage (Rec0 (t fs f a)) (Rec0 (t ts f a))) +-- where +-- gSwitchStage (K1 val) = K1 (switchStage val) +-- +-- instance +-- ( EasySwitchStage t f fs ts, +-- Functor func +-- ) => +-- (GEasySwitchStage (Rec0 (func (t fs f a))) (Rec0 (func (t ts f a)))) +-- where +-- gSwitchStage (K1 val) = K1 (switchStage <$> val) +-- +-- instance (GEasySwitchStage (Rec0 a) (Rec0 a)) where +-- gSwitchStage = id +-- +-- instance +-- ( TypeError +-- ( TypeError.Text "Unable to match type " +-- :<>: TypeError.ShowType a +-- :<>: TypeError.Text " with " +-- :<>: TypeError.ShowType b +-- ) +-- ) => +-- (GEasySwitchStage (Rec0 a) (Rec0 b)) +-- where +-- gSwitchStage = error "Cannot be called" + +-- class GEasySwitchStage r1 r2 where +-- gSwitchStage :: r1 x -> r2 x + +proxyOf :: t f a -> Proxy t +proxyOf _ = Proxy + +instance (Alter t, Traversable f) => Functor (t f) where + fmap f t = runIdentity (alter return (return . f) t) + +-- | 'GAdvanceStage' instance for metadata wrappers ('M1'). +-- This instance allows advancing the stage of a metadata node in a generic +-- representation. The metadata node ('M1') wraps another node ('s1'), which +-- is recursively advanced to the next stage using 'gAdvanceStage'. instance - (GEasySwitchStage s1 s2) => - GEasySwitchStage (M1 i c s1) (M1 i c s2) + (Monad m, GAdvanceStage s m s1 s2) => + GAdvanceStage s m (M1 i c s1) (M1 i c s2) where - gSwitchStage (M1 a) = M1 (gSwitchStage a) + gAdvanceStage s (M1 a) = M1 <$> gAdvanceStage s a +-- | 'GAdvanceStage' instance for sum types (':+:'). This handles the case +-- where the generic representation of a type is a sum (i.e., an 'Either'-like +-- choice between two alternatives). The sum type can be either 'L1' (left) +-- or 'R1' (right), and 'gAdvanceStage' is called recursively on the selected +-- branch. instance - (GEasySwitchStage l1 l2, GEasySwitchStage r1 r2) => - GEasySwitchStage (l1 :+: r1) (l2 :+: r2) + (Monad m, GAdvanceStage s m l1 l2, GAdvanceStage s m r1 r2) => + GAdvanceStage s m (l1 :+: r1) (l2 :+: r2) where - gSwitchStage (R1 r) = R1 $ gSwitchStage r - gSwitchStage (L1 l) = L1 $ gSwitchStage l + gAdvanceStage s (R1 r) = R1 <$> gAdvanceStage s r + gAdvanceStage s (L1 l) = L1 <$> gAdvanceStage s l +-- | 'GAdvanceStage' instance for product types (':*:'). This handles cases +-- where the generic representation of a type is a product (i.e., a tuple of +-- multiple components). It recursively advances each component ('l' and 'r') +-- to the next stage. instance - (GEasySwitchStage l1 l2, GEasySwitchStage r1 r2) => - (GEasySwitchStage (l1 :*: r1) (l2 :*: r2)) + (Monad m, GAdvanceStage s m l1 l2, GAdvanceStage s m r1 r2) => + GAdvanceStage s m (l1 :*: r1) (l2 :*: r2) where - gSwitchStage (l :*: r) = gSwitchStage l :*: gSwitchStage r + gAdvanceStage s (l :*: r) = + (:*:) <$> gAdvanceStage s l <*> gAdvanceStage s r +-- | 'GAdvanceStage' instance for record fields ('Rec0') containing a single +-- AST element ('t') to be advanced. This instance covers the case where the +-- field is an individual AST node that implements 'AdvanceStage'. +-- It advances this node using 'advanceStage'. instance - (EasySwitchStage t f fs ts) => - (GEasySwitchStage (Rec0 (t fs f a)) (Rec0 (t ts f a))) + ( Monad m, + AdvanceStage stage t', + Traversable f, + StageAfter stage ~ stage', + StageMonad stage ~ m, + StageState stage ~ s, + StageFunctor stage ~ f, + StageAnnotation stage ~ a + ) => + GAdvanceStage s m (Rec0 (t' stage f a)) (Rec0 (t' stage' f a)) where - gSwitchStage (K1 val) = K1 (switchStage val) + gAdvanceStage st (K1 val) = K1 <$> advanceStage st val +-- | 'GAdvanceStage' instance for record fields ('Rec0') containing a functor +-- ('func') of AST elements ('t'). This handles cases where the field is a +-- container (e.g., list, 'Maybe') of AST nodes that need to be advanced. +-- Each node in the container is transformed using 'advanceStage'. instance - ( EasySwitchStage t f fs ts, - Functor func + ( Monad m, + AdvanceStage stage t', + Traversable f, + StageAfter stage ~ stage', + StageMonad stage ~ m, + StageState stage ~ s, + StageFunctor stage ~ f, + StageAnnotation stage ~ a, + Traversable func ) => - (GEasySwitchStage (Rec0 (func (t fs f a))) (Rec0 (func (t ts f a)))) + GAdvanceStage s m (Rec0 (func (t' stage f a))) (Rec0 (func (t' stage' f a))) where - gSwitchStage (K1 val) = K1 (switchStage <$> val) - -instance (GEasySwitchStage (Rec0 a) (Rec0 a)) where - gSwitchStage = id + gAdvanceStage st (K1 val) = K1 <$> mapM (advanceStage st) val +-- | 'GAdvanceStage' instance for record fields ('Rec0') containing a +-- functor ('f') wrapping an AST element. This handles cases where the field +-- is a container ('f') of AST nodes that need to be advanced. Each node in +-- the container is transformed using 'advanceStage'. instance - ( TypeError - ( TypeError.Text "Unable to match type " - :<>: TypeError.ShowType a - :<>: TypeError.Text " with " - :<>: TypeError.ShowType b - ) + ( Monad m, + AdvanceStage stage t', + Traversable f, + StageAfter stage ~ stage', + StageMonad stage ~ m, + StageState stage ~ s, + StageFunctor stage ~ f, + StageAnnotation stage ~ a ) => - (GEasySwitchStage (Rec0 a) (Rec0 b)) + GAdvanceStage s m (Rec0 (f (t' stage f a))) (Rec0 (f (t' stage' f a))) where - gSwitchStage = error "Cannot be called" - -class GEasySwitchStage r1 r2 where - gSwitchStage :: r1 x -> r2 x + gAdvanceStage st (K1 val) = K1 <$> mapM (advanceStage st) val -proxyOf :: t f a -> Proxy t -proxyOf _ = Proxy - -instance (Alter t, Traversable f) => Functor (t f) where - fmap f t = runIdentity (alter return (return . f) t) +-- | 'GAdvanceStage' instance for simple record fields ('Rec0') that do not +-- need to change between stages. This is used for fields that are not AST +-- nodes and remain the same when advancing the stage (e.g., primitive +-- types like 'Int', 'Bool', etc.). +instance (Monad m) => GAdvanceStage s m (Rec0 a) (Rec0 a) where + gAdvanceStage _ (K1 val) = return (K1 val) diff --git a/src/Language/Fiddle/Ast/Internal/SyntaxTree.hs b/src/Language/Fiddle/Ast/Internal/SyntaxTree.hs index 48852ee..827f712 100644 --- a/src/Language/Fiddle/Ast/Internal/SyntaxTree.hs +++ b/src/Language/Fiddle/Ast/Internal/SyntaxTree.hs @@ -109,429 +109,400 @@ data Name :: SynTree where Name :: NonEmpty (Identifier f a) -> a -> Name f a deriving (Generic, Annotated, Alter, Typeable) --- Syntax tree fo the directive sublanguage. Directives can be on many elements --- and provide the compiler with additional information about the emitted code. --- --- The directive subtree by design does not depend on the compilation stage. --- This is because the directive sublanguage should pass directly to the backend --- compilation stage. +-- | Represents a directive in the Fiddle language. A directive provides +-- additional metadata or instructions that the compiler can use during +-- code generation. Directives can be attached to many elements in the +-- syntax tree. data Directive :: SynTree where - Directive :: f (DirectiveBody f a) -> a -> Directive f a + Directive :: + { directiveBody :: f (DirectiveBody f a), -- ^ The body of the directive. + directiveAnnot :: a -- ^ Annotation for the directive. + } -> Directive f a deriving (Generic, Annotated, Alter, Typeable) --- A directive body has multiple directive elements. +-- | Represents the body of a directive, which consists of multiple elements. data DirectiveBody :: SynTree where - DirectiveBody :: [DirectiveElement f a] -> a -> DirectiveBody f a + DirectiveBody :: + { directiveElements :: [DirectiveElement f a], -- ^ Elements of the directive. + directiveBodyAnnot :: a -- ^ Annotation for the directive body. + } -> DirectiveBody f a deriving (Generic, Annotated, Alter, Typeable) --- Element in the directive. +-- | Represents an element in a directive. Can be either a key or a key-value +-- pair. data DirectiveElement :: SynTree where - -- A directive can just be a key. Where the mere presence of the key has - -- semantic value. + -- | A simple directive element with a key. The mere presence of this key + -- holds semantic value. DirectiveElementKey :: - -- Which backend is this directive intended for? - Maybe (Identifier f a) -> - Identifier f a -> - a -> - DirectiveElement f a - -- A directive can be more complex too. It can have an optional backend - -- specificer, a key and a value. + { directiveBackend :: Maybe (Identifier f a), -- ^ Optional backend target. + directiveKey :: Identifier f a, -- ^ The key of the directive. + directiveKeyAnnot :: a -- ^ Annotation for the directive element. + } -> DirectiveElement f a + -- | A more complex directive element with a key-value pair, optionally + -- specifying a backend. DirectiveElementKeyValue :: - -- Which backend is this directive intendend for? - Maybe (Identifier f a) -> - -- The key for this directive. - Identifier f a -> - -- The value for this directive. - DirectiveExpression f a -> - a -> - DirectiveElement f a + { directiveBackend :: Maybe (Identifier f a), -- ^ Optional backend target. + directiveKey :: Identifier f a, -- ^ The key of the directive. + directiveValue :: DirectiveExpression f a, -- ^ The value of the directive. + directiveKeyValueAnnot :: a -- ^ Annotation for the key-value directive. + } -> DirectiveElement f a deriving (Generic, Annotated, Alter, Typeable) --- Expressions which can be found in the directive. +-- | Represents expressions that can be used within a directive, either a +-- string or a number. data DirectiveExpression f a where - DirectiveString :: Text -> a -> DirectiveExpression f a - DirectiveNumber :: Text -> a -> DirectiveExpression f a + DirectiveString :: + { directiveStringValue :: Text, -- ^ String value of the directive. + directiveStringAnnot :: a -- ^ Annotation for the directive string. + } -> DirectiveExpression f a + DirectiveNumber :: + { directiveNumberValue :: Text, -- ^ Number value of the directive. + directiveNumberAnnot :: a -- ^ Annotation for the directive number. + } -> DirectiveExpression f a deriving (Generic, Annotated, Alter, Typeable) --- A type, which wraps another syntax tree, but tacks on an array of directives. --- that apply to the subtree. +-- | A type that wraps another syntax tree and applies a list of directives to +-- it. data Directed t stage f a where - Directed :: [Directive f a] -> t stage f a -> a -> Directed t stage f a + Directed :: + { directedDirectives :: [Directive f a], -- ^ List of directives. + directedSubtree :: t stage f a, -- ^ The wrapped syntax tree. + directedAnnot :: a -- ^ Annotation for the directed subtree. + } -> Directed t stage f a deriving (Generic, Annotated, Alter, Typeable) --- Apply a function to the underlying subtree in a Directed type. +-- | Apply a function to the underlying subtree in a 'Directed' type. mapDirected :: (t s f a -> t' s' f a) -> Directed t s f a -> Directed t' s' f a mapDirected fn (Directed dr tfa a) = Directed dr (fn tfa) a --- Apply a monadic function to the underlying subtree in a Directed type. +-- | Apply a monadic function to the underlying subtree in a 'Directed' type. mapDirectedM :: - (Monad m) => (t s f a -> m (t' s' f a)) -> Directed t s f a -> m (Directed t' s' f a) + (Monad m) => + (t s f a -> m (t' s' f a)) -> + Directed t s f a -> + m (Directed t' s' f a) mapDirectedM fn (Directed dr tfa a) = Directed dr <$> fn tfa <*> pure a +-- | Convert an annotated syntax tree element to a 'Directed' type with +-- an empty directive list. asDirected :: (Annotated (t s)) => t s f a -> Directed t s f a asDirected tfa = Directed [] tfa (annot tfa) +-- | Extract the underlying subtree from a 'Directed' type, discarding any +-- directives. undirected :: Directed t s f a -> t s f a undirected (Directed _ tfa _) = tfa --- Root of the parse tree. Just contains a list of declarations. +-- | The root of the parse tree, containing a list of top-level declarations. data FiddleUnit (stage :: Stage) (f :: Type -> Type) a where - FiddleUnit :: [Directed FiddleDecl stage f a] -> a -> FiddleUnit stage f a + FiddleUnit :: + { fiddleDecls :: [Directed FiddleDecl stage f a], -- ^ List of declarations. + fiddleUnitAnnot :: a -- ^ Annotation for the 'FiddleUnit'. + } -> FiddleUnit stage f a deriving (Generic, Annotated, Typeable) deriving instance (Alter (ImportType stage)) => Alter (FiddleUnit stage) --- Just an identifier. -data Identifier f a = Identifier !Text a +-- | Represents an identifier with an associated annotation. +data Identifier f a = Identifier + { identifierName :: !Text, -- ^ The name of the identifier. + identifierAnnot :: a -- ^ Annotation for the identifier. + } deriving (Generic, Annotated, Alter, Typeable) --- Expression. +-- | Expressions used within Fiddle, including literals and variables. data Expression (s :: Stage) :: SynTree where - -- Just a string. Parsing the number comes in stage2. - LitNum :: NumberType stage -> a -> Expression stage f a - Var :: Identifier f a -> a -> Expression stage f a + -- | A numeric literal, whose value is dependent on the compilation stage. + LitNum :: + { litNumValue :: NumberType stage, -- ^ The numeric value. + litNumAnnot :: a -- ^ Annotation for the literal. + } -> Expression stage f a + -- | A variable reference. + Var :: + { varIdentifier :: Identifier f a, -- ^ The identifier of the variable. + varAnnot :: a -- ^ Annotation for the variable. + } -> Expression stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents an import statement in the Fiddle language. data ImportStatement f a where - ImportStatement :: Text -> Maybe (ImportList f a) -> a -> ImportStatement f a + ImportStatement :: + { importPath :: Text, -- ^ The path to import. + importList :: Maybe (ImportList f a), -- ^ Optional list of imported items. + importStatementAnnot :: a -- ^ Annotation for the import statement. + } -> ImportStatement f a deriving (Generic, Annotated, Alter, Typeable) +-- | A list of imported identifiers. data ImportList f a where - ImportList :: [Identifier f a] -> a -> ImportList f a + ImportList :: + { importIdentifiers :: [Identifier f a], -- ^ The list of identifiers. + importListAnnot :: a -- ^ Annotation for the import list. + } -> ImportList f a deriving (Generic, Annotated, Alter, Typeable) --- Top-level declarations. +-- | Represents top-level declarations in Fiddle. data FiddleDecl :: StagedSynTree where - {- - - An option is a key/value pair. - - option <ident> <ident>; - -} + -- | An option declaration in the form 'option <ident> <ident>'. OptionDecl :: - Identifier f a -> - Identifier f a -> - a -> - FiddleDecl stage f a + { optionKey :: Identifier f a, -- ^ The key of the option. + optionValue :: Identifier f a, -- ^ The value of the option. + optionAnnot :: a -- ^ Annotation for the option declaration. + } -> FiddleDecl stage f a + -- | An import declaration. ImportDecl :: - ImportType stage f a -> - a -> - FiddleDecl stage f a + { importType :: ImportType stage f a, -- ^ The imported type. + importDeclAnnot :: a -- ^ Annotation for the import declaration. + } -> FiddleDecl stage f a + -- | A using declaration. UsingDecl :: - Name f a -> a -> FiddleDecl stage f a - {- Package Statement. Package Name, Package body -} + { usingName :: Name f a, -- ^ The name being used. + usingAnnot :: a -- ^ Annotation for the using declaration. + } -> FiddleDecl stage f a + -- | A package declaration. PackageDecl :: - Name f a -> - f (PackageBody stage f a) -> - a -> - FiddleDecl stage f a - {- location <identifier> = <expr>. -} + { packageName :: Name f a, -- ^ The package name. + packageBody :: f (PackageBody stage f a), -- ^ The body of the package. + packageAnnot :: a -- ^ Annotation for the package declaration. + } -> FiddleDecl stage f a + -- | A location declaration in the form 'location <identifier> = <expr>'. LocationDecl :: - Identifier f a -> - Expression stage f a -> - a -> - FiddleDecl stage f a - {- bits <identifier> : <type> -} + { locationIdent :: Identifier f a, -- ^ The location identifier. + locationExpr :: Expression stage f a, -- ^ The associated expression. + locationAnnot :: a -- ^ Annotation for the location declaration. + } -> FiddleDecl stage f a + -- | A bits declaration in the form 'bits <identifier> : <type>'. BitsDecl :: - Identifier f a -> - BitType stage f a -> - a -> - FiddleDecl stage f a - {- objtype <identifier> : <type> -} + { bitsIdent :: Identifier f a, -- ^ The identifier of the bits. + bitsType :: BitType stage f a, -- ^ The type of the bits. + bitsAnnot :: a -- ^ Annotation for the bits declaration. + } -> FiddleDecl stage f a + -- | An object type declaration. ObjTypeDecl :: - Identifier f a -> - f (ObjTypeBody stage f a) -> - a -> - FiddleDecl stage f a - {- object <ident> at <expr> : <type> -} + { objTypeIdent :: Identifier f a, -- ^ The identifier of the object type. + objTypeBody :: f (ObjTypeBody stage f a), -- ^ The body of the object type. + objTypeAnnot :: a -- ^ Annotation for the object type declaration. + } -> FiddleDecl stage f a + -- | An object declaration in the form 'object <ident> at <expr> : <type>'. ObjectDecl :: - Identifier f a -> - Expression stage f a -> - ObjType stage f a -> - a -> - FiddleDecl stage f a + { objectIdent :: Identifier f a, -- ^ The identifier of the object. + objectLocation :: Expression stage f a, -- ^ The location expression. + objectType :: ObjType stage f a, -- ^ The type of the object. + objectAnnot :: a -- ^ Annotation for the object declaration. + } -> FiddleDecl stage f a deriving (Generic, Annotated, Typeable) deriving instance (Alter (ImportType stage)) => Alter (FiddleDecl stage) +-- | Represents the body of an object type, containing a body type (struct or +-- union), a list of object declarations, and an annotation. data ObjTypeBody (stage :: Stage) (f :: Type -> Type) a where ObjTypeBody :: - BodyType f a -> - [Directed ObjTypeDecl stage f a] -> - a -> - ObjTypeBody stage f a + { objBodyType :: BodyType f a, -- ^ The body type (struct or union). + objBodyDecls :: [Directed ObjTypeDecl stage f a], -- ^ Object declarations. + objBodyAnnot :: a -- ^ Annotation for the object type body. + } -> ObjTypeBody stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents an object type, which can be anonymous, an array, or a +-- reference to another type. data ObjType stage f a where - -- { <body> } - -- Anonymous types are only allowed in stage1. Stage2 should have them be - -- de-anonymized. + -- | An anonymous object type, allowed only in Stage1. AnonymousObjType :: - (Witness (stage == Stage1)) -> - f (ObjTypeBody stage f a) -> - a -> - ObjType stage f a - -- <type>[<expr>] - ArrayObjType :: ObjType stage f a -> Expression stage f a -> a -> ObjType stage f a - -- <identifier> - ReferencedObjType :: Name f a -> a -> ObjType stage f a + { anonWitness :: Witness (stage == Stage1), -- ^ Witness for stage constraint. + anonBody :: f (ObjTypeBody stage f a), -- ^ The body of the anonymous type. + anonAnnot :: a -- ^ Annotation for the anonymous type. + } -> ObjType stage f a + -- | An array of object types. + ArrayObjType :: + { arrayObjType :: ObjType stage f a, -- ^ The type of the array elements. + arraySize :: Expression stage f a, -- ^ The size of the array. + arrayAnnot :: a -- ^ Annotation for the array type. + } -> ObjType stage f a + -- | A reference to an existing type by name. + ReferencedObjType :: + { refName :: Name f a, -- ^ The name of the referenced type. + refAnnot :: a -- ^ Annotation for the referenced type. + } -> ObjType stage f a deriving (Typeable, Generic, Alter, Annotated, Typeable) +-- | Represents a declaration inside an object type, such as a register, an +-- assertion, or a substructure. data ObjTypeDecl stage f a where - {- assert_pos(<expr>) -} + -- | An assertion statement for a specific position. AssertPosStatement :: - Witness (StageLessThan stage 3) -> - Expression stage f a -> - a -> - ObjTypeDecl stage f a - {- reg <ident>(<expr>) : <regtype> -} + { assertWitness :: Witness (StageLessThan stage 3), -- ^ Witness for stage constraint. + assertExpr :: Expression stage f a, -- ^ The expression for the assertion. + assertAnnot :: a -- ^ Annotation for the assertion. + } -> ObjTypeDecl stage f a + -- | A register declaration. RegisterDecl :: - Maybe (Modifier f a) -> - Maybe (Identifier f a) -> - Expression stage f a -> - Maybe (RegisterBody stage f a) -> - a -> - ObjTypeDecl stage f a - {- reserved(n); -} - ReservedDecl :: Expression stage f a -> a -> ObjTypeDecl stage f a - {- <struct|union> { subfields } <name>; -} + { regModifier :: Maybe (Modifier f a), -- ^ Optional register modifier. + regIdent :: Maybe (Identifier f a), -- ^ Optional register identifier. + regSize :: Expression stage f a, -- ^ Size of the register. + regBody :: Maybe (RegisterBody stage f a), -- ^ Optional register body. + regAnnot :: a -- ^ Annotation for the register declaration. + } -> ObjTypeDecl stage f a + -- | A reserved declaration for padding or alignment. + ReservedDecl :: + { reservedExpr :: Expression stage f a, -- ^ The expression for reserved space. + reservedAnnot :: a -- ^ Annotation for the reserved declaration. + } -> ObjTypeDecl stage f a + -- | A declaration for a substructure (struct or union). TypeSubStructure :: - f (ObjTypeBody stage f a) -> - Maybe (Identifier f a) -> - a -> - ObjTypeDecl stage f a + { subStructureBody :: f (ObjTypeBody stage f a), -- ^ The body of the substructure. + subStructureName :: Maybe (Identifier f a), -- ^ Optional name for the substructure. + subStructureAnnot :: a -- ^ Annotation for the substructure. + } -> ObjTypeDecl stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents a modifier for registers (e.g., read-only, read-write). data Modifier f a where - ModifierKeyword :: ModifierKeyword -> a -> Modifier f a + ModifierKeyword :: + { modifierKey :: ModifierKeyword, -- ^ The keyword for the modifier. + modifierAnnot :: a -- ^ Annotation for the modifier. + } -> Modifier f a deriving (Generic, Annotated, Alter, Typeable) -data ModifierKeyword = Rw | Ro | Wo deriving (Eq, Ord, Show, Read, Typeable) +-- | Enumerates the different types of register modifiers. +data ModifierKeyword = Rw | Ro | Wo + deriving (Eq, Ord, Show, Read, Typeable) +-- | Represents a deferred register body, consisting of a list of bit +-- declarations. data DeferredRegisterBody stage f a where DeferredRegisterBody :: - [Directed RegisterBitsDecl stage f a] -> - a -> - DeferredRegisterBody stage f a + { deferredBits :: [Directed RegisterBitsDecl stage f a], -- ^ Bit declarations. + deferredAnnot :: a -- ^ Annotation for the deferred register body. + } -> DeferredRegisterBody stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents the body type (struct or union) in an object. data BodyType (f :: Type -> Type) a where - Union :: a -> BodyType f a - Struct :: a -> BodyType f a + Union :: + { unionAnnot :: a -- ^ Annotation for the union. + } -> BodyType f a + Struct :: + { structAnnot :: a -- ^ Annotation for the struct. + } -> BodyType f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents a register body with a body type and deferred bit declarations. data RegisterBody stage f a where RegisterBody :: - BodyType f a -> - f (DeferredRegisterBody stage f a) -> - a -> - RegisterBody stage f a + { regBodyType :: BodyType f a, -- ^ The body type of the register. + regDeferredBody :: f (DeferredRegisterBody stage f a), -- ^ Deferred body. + regBodyAnnot :: a -- ^ Annotation for the register body. + } -> RegisterBody stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents declarations within a register, such as defined bits, +-- reserved bits, or substructures. data RegisterBitsDecl stage f a where - -- reserved(<expr>) - ReservedBits :: Expression stage f a -> a -> RegisterBitsDecl stage f a - -- <modifer> <ident> : <type> + -- | Declaration for reserved bits. + ReservedBits :: + { reservedBitsExpr :: Expression stage f a, -- ^ Expression for reserved bits. + reservedBitsAnnot :: a -- ^ Annotation for the reserved bits. + } -> RegisterBitsDecl stage f a + -- | Declaration for defined bits in a register. DefinedBits :: - Maybe (Modifier f a) -> - Identifier f a -> - RegisterBitsTypeRef stage f a -> - a -> - RegisterBitsDecl stage f a + { definedBitsModifier :: Maybe (Modifier f a), -- ^ Optional modifier for the bits. + definedBitsIdent :: Identifier f a, -- ^ Identifier for the bits. + definedBitsTypeRef :: RegisterBitsTypeRef stage f a, -- ^ Type reference for the bits. + definedBitsAnnot :: a -- ^ Annotation for the defined bits. + } -> RegisterBitsDecl stage f a + -- | Substructure within a register. BitsSubStructure :: - RegisterBody stage f a -> - Maybe (Identifier f a) -> - a -> - RegisterBitsDecl stage f a + { bitsSubRegBody :: RegisterBody stage f a, -- ^ The body of the substructure. + bitsSubName :: Maybe (Identifier f a), -- ^ Optional name for the substructure. + bitsSubAnnot :: a -- ^ Annotation for the substructure. + } -> RegisterBitsDecl stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents different ways to refer to register bits, either as an array, +-- a reference to a type, an anonymous type, or just bits. data RegisterBitsTypeRef stage f a where - -- <type>[<expr>] + -- | An array of bits with a specified size. RegisterBitsArray :: - RegisterBitsTypeRef stage f a -> - Expression stage f a -> - a -> - RegisterBitsTypeRef stage f a - {- Reference to a type. -} - RegisterBitsReference :: Name f a -> a -> RegisterBitsTypeRef stage f a - {- enum(<expr>) { <body> } - Anonymous types are only allowed in stage1. - Stage2 should de-anonymize these type. -} + { bitsArrayTypeRef :: RegisterBitsTypeRef stage f a, -- ^ Reference to the array type. + bitsArraySize :: Expression stage f a, -- ^ Size of the array. + bitsArrayAnnot :: a -- ^ Annotation for the array. + } -> RegisterBitsTypeRef stage f a + -- | A reference to another type by name. + RegisterBitsReference :: + { bitsRefName :: Name f a, -- ^ The name of the referenced type. + bitsRefAnnot :: a -- ^ Annotation for the reference. + } -> RegisterBitsTypeRef stage f a + -- | An anonymous type for register bits, used in Stage1. RegisterBitsAnonymousType :: - Witness (stage == Stage1) -> - AnonymousBitsType stage f a -> - a -> - RegisterBitsTypeRef stage f a - {- (<expr>) - - - - The expression is just bits ... i.e. an integer. - -} + { anonBitsWitness :: Witness (stage == Stage1), -- ^ Witness for stage constraint. + anonBitsType :: AnonymousBitsType stage f a, -- ^ The anonymous type. + anonBitsAnnot :: a -- ^ Annotation for the anonymous type. + } -> RegisterBitsTypeRef stage f a + -- | A direct specification of bits as an expression. RegisterBitsJustBits :: - Expression stage f a -> - a -> - RegisterBitsTypeRef stage f a + { justBitsExpr :: Expression stage f a, -- ^ Expression for the bits. + justBitsAnnot :: a -- ^ Annotation for the bits. + } -> RegisterBitsTypeRef stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents an anonymous bit type, such as an enum, used in Stage1. data AnonymousBitsType stage f a where - -- enum(<expr>) { <body> } AnonymousEnumBody :: - Expression stage f a -> - f (EnumBody stage f a) -> - a -> - AnonymousBitsType stage f a + { anonEnumExpr :: Expression stage f a, -- ^ Expression defining the enum size. + anonEnumBody :: f (EnumBody stage f a), -- ^ The body of the enum. + anonEnumAnnot :: a -- ^ Annotation for the anonymous enum. + } -> AnonymousBitsType stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents a bit type, either an enumeration or raw bits. data BitType (stage :: Stage) (f :: Type -> Type) a where - -- enum(<expr>) { <body> } + -- | An enumeration type for bits. EnumBitType :: - Expression stage f a -> - f (EnumBody stage f a) -> - a -> - BitType stage f a - -- (<expr>) - RawBits :: Expression stage f a -> a -> BitType stage f a + { enumBitExpr :: Expression stage f a, -- ^ Expression defining the enum size. + enumBitBody :: f (EnumBody stage f a), -- ^ The body of the enum. + enumBitAnnot :: a -- ^ Annotation for the enumeration. + } -> BitType stage f a + -- | A raw bit type. + RawBits :: + { rawBitsExpr :: Expression stage f a, -- ^ Expression defining the bits. + rawBitsAnnot :: a -- ^ Annotation for the raw bits. + } -> BitType stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents the body of an enumeration. data EnumBody (stage :: Stage) (f :: Type -> Type) a where - -- <decl>, - EnumBody :: [Directed EnumConstantDecl stage f a] -> a -> EnumBody stage f a + EnumBody :: + { enumConsts :: [Directed EnumConstantDecl stage f a], -- ^ Enum constant declarations. + enumBodyAnnot :: a -- ^ Annotation for the enum body. + } -> EnumBody stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents a declaration for an enumeration constant. data EnumConstantDecl stage f a where - -- <ident> = <expr> + -- | A named constant in the enum. EnumConstantDecl :: - Identifier f a -> - Expression stage f a -> - a -> - EnumConstantDecl stage f a - -- reserved = <expr> + { enumConstIdent :: Identifier f a, -- ^ Identifier for the constant. + enumConstExpr :: Expression stage f a, -- ^ Expression defining the constant. + enumConstAnnot :: a -- ^ Annotation for the constant. + } -> EnumConstantDecl stage f a + -- | A reserved value in the enum. EnumConstantReserved :: - Expression stage f a -> - a -> - EnumConstantDecl stage f a + { enumReservedExpr :: Expression stage f a, -- ^ Expression for the reserved value. + enumReservedAnnot :: a -- ^ Annotation for the reserved value. + } -> EnumConstantDecl stage f a deriving (Generic, Annotated, Alter, Typeable) +-- | Represents the body of a package, containing a list of declarations. data PackageBody (stage :: Stage) (f :: Type -> Type) a where - {- The body of a package -} - PackageBody :: [Directed FiddleDecl stage f a] -> a -> PackageBody stage f a + PackageBody :: + { packageBodyDecls :: [Directed FiddleDecl stage f a], -- ^ Declarations in the package. + packageBodyAnnot :: a -- ^ Annotation for the package body. + } -> PackageBody stage f a deriving (Generic, Annotated, Typeable) deriving instance (Alter (ImportType stage)) => Alter (PackageBody stage) + squeeze :: (Alter t, Traversable f, Monad f) => t f a -> f (t Identity a) squeeze = alter (fmap Identity) return - --- Expression involves NumberType, so we add the constraint: -deriving instance (Functor f, NumberType s ~ NumberType s') => EasySwitchStage Expression f s s' - --- FiddleDecl includes both NumberType and ImportType, so we need both constraints: -deriving instance - ( Functor f, - StageLessThan s 3 ~ StageLessThan s' 3, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s', - ImportType s ~ ImportType s' - ) => - EasySwitchStage FiddleDecl f s s' - --- ObjType includes NumberType, so we add the constraint: -deriving instance - ( Functor f, - StageLessThan s 3 ~ StageLessThan s' 3, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s' - ) => - EasySwitchStage ObjType f s s' - --- ObjTypeBody doesn't have any special type families, so no additional constraints: -deriving instance - ( Functor f, - StageLessThan s 3 ~ StageLessThan s' 3, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s' - ) => - EasySwitchStage ObjTypeBody f s s' - --- ObjTypeDecl doesn't have special type families, so no additional constraints: -deriving instance - ( Functor f, - StageLessThan s 3 ~ StageLessThan s' 3, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s' - ) => - EasySwitchStage ObjTypeDecl f s s' - --- DeferredRegisterBody doesn't have special type families: -deriving instance - ( Functor f, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s' - ) => - EasySwitchStage DeferredRegisterBody f s s' - --- RegisterBody depends on NumberType, so we add that constraint: -deriving instance - ( Functor f, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s' - ) => - EasySwitchStage RegisterBody f s s' - --- RegisterBitsDecl depends on NumberType: -deriving instance - ( Functor f, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s' - ) => - EasySwitchStage RegisterBitsDecl f s s' - --- RegisterBitsTypeRef depends on NumberType: -deriving instance - ( Functor f, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s' - ) => - EasySwitchStage RegisterBitsTypeRef f s s' - --- AnonymousBitsType depends on NumberType: -deriving instance - (Functor f, NumberType s ~ NumberType s') => - EasySwitchStage AnonymousBitsType f s s' - --- BitType depends on NumberType: -deriving instance - (Functor f, NumberType s ~ NumberType s') => - EasySwitchStage BitType f s s' - --- EnumBody doesn't depend on any type families: -deriving instance - (Functor f, NumberType s ~ NumberType s') => - EasySwitchStage EnumBody f s s' - --- EnumConstantDecl depends on NumberType: -deriving instance - (Functor f, NumberType s ~ NumberType s') => - EasySwitchStage EnumConstantDecl f s s' - --- PackageBody includes both NumberType and ImportType: -deriving instance - ( Functor f, - StageLessThan s 3 ~ StageLessThan s' 3, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s', - ImportType s ~ ImportType s' - ) => - EasySwitchStage PackageBody f s s' - --- FiddleUnit includes NumberType and ImportType, so we need both constraints: -deriving instance - ( Functor f, - StageLessThan s 3 ~ StageLessThan s' 3, - (s == Stage1) ~ (s' == Stage1), - NumberType s ~ NumberType s', - ImportType s ~ ImportType s' - ) => - EasySwitchStage FiddleUnit f s s' - --- Directed depends on its underlying AST type: -deriving instance - (EasySwitchStage t f s s') => - EasySwitchStage (Directed t) f s s' diff --git a/src/Language/Fiddle/Compiler/Stage1.hs b/src/Language/Fiddle/Compiler/Stage1.hs index d2fe885..aae80e4 100644 --- a/src/Language/Fiddle/Compiler/Stage1.hs +++ b/src/Language/Fiddle/Compiler/Stage1.hs @@ -1,6 +1,7 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE IncoherentInstances #-} -{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module Language.Fiddle.Compiler.Stage1 (toStage2) where @@ -14,216 +15,133 @@ import Data.Text (Text) import qualified Data.Text as Text import Data.Type.Bool import Debug.Trace -import GHC.Generics import GHC.TypeLits import Language.Fiddle.Ast import Language.Fiddle.Compiler import Language.Fiddle.Types import Text.Printf (printf) -newtype Linkage = Linkage Text deriving (Show) +type Annot = Commented SourceSpan newtype Path = Path [PathExpression] newtype PathExpression = PathExpression String -type Annot = Commented SourceSpan +type M = Compile State joinPath :: Path -> String joinPath (Path l) = intercalate "#" $ reverse (map (\(PathExpression s) -> s) l) +toStage2 :: FiddleUnit Stage1 I Annot -> Compile () (FiddleUnit Stage2 I Annot) +toStage2 = fmap snd . subCompile (State [] []) . advanceStage (Path mempty) + -- Shorthand for Identity type I = Identity -data Stage2CompilerState a - = Stage2CompilerState +newtype Linkage = Linkage Text deriving (Show) + +data State + = State -- Anonymous object type bodies that need to be re-linked - ![(Linkage, ObjTypeBody Stage2 I a)] + ![(Linkage, ObjTypeBody Stage2 I Annot)] -- Anonymous enum bodies that need to be re-linked - ![(Linkage, AnonymousBitsType Stage2 I a)] - -type M a = Compile (Stage2CompilerState a) - -internObjType :: Path -> ObjTypeBody Stage2 I a -> M a (Identifier I a) -internObjType path body = - let str = Text.pack $ joinPath path - in do - modify $ \(Stage2CompilerState objTypeBodies a) -> - Stage2CompilerState ((Linkage str, body) : objTypeBodies) a - return (Identifier str (annot body)) - -internAnonymousBitsType :: Path -> AnonymousBitsType Stage2 I a -> M a (Identifier I a) -internAnonymousBitsType path anonymousBitsType = - let str = Text.pack $ joinPath path - in do - modify $ \(Stage2CompilerState a anonymousBitsTypes) -> - Stage2CompilerState a ((Linkage str, anonymousBitsType) : anonymousBitsTypes) - return (Identifier str (annot anonymousBitsType)) - --- The second stage is a simplified version of the AST without anonymous --- declarations. -toStage2 :: FiddleUnit Stage1 I Annot -> Compile () (FiddleUnit Stage2 I Annot) -toStage2 (FiddleUnit decls annot) = do - (s, a) <- - subCompile (Stage2CompilerState [] []) $ - FiddleUnit <$> reconfigureFiddleDecls (Path []) decls <*> pure annot - return a - -reconfigureFiddleDecls :: Path -> [Directed FiddleDecl Stage1 I Annot] -> M Annot [Directed FiddleDecl Stage2 I Annot] -reconfigureFiddleDecls p decls = do - -- Stage2CompilerState anonymousObjTypes anonymousBitsTypes, decls <- pushState $ do - -- put (Stage2CompilerState [] []) - -- gets (,) <*> mapM (fiddleDeclToStage2 p) decls - - lastState <- get - put (Stage2CompilerState [] []) - decls <- mapM (mapDirectedM $ fiddleDeclToStage2 p) decls - (Stage2CompilerState anonymousObjTypes anonymousBitsTypes) <- get - put lastState - - return $ - map (asDirected . resolveAnonymousObjType) anonymousObjTypes - ++ map (asDirected . resolveAnonymousBitsType) anonymousBitsTypes - ++ decls - where - resolveAnonymousObjType (Linkage linkage, objTypeBody) = - ObjTypeDecl (Identifier linkage (annot objTypeBody)) (pure objTypeBody) (annot objTypeBody) - - resolveAnonymousBitsType (Linkage linkage, AnonymousEnumBody expr body a) = - BitsDecl (Identifier linkage a) (EnumBitType expr body a) a - -pushId :: Identifier f a -> Path -> Path -pushId (Identifier str _) (Path lst) = - Path (PathExpression (Text.unpack str) : lst) - -pushName :: Name f a -> Path -> Path -pushName (Name idents _) path = - foldl (flip pushId) path idents - -fiddleDeclToStage2 :: Path -> FiddleDecl Stage1 I Annot -> M Annot (FiddleDecl Stage2 I Annot) -fiddleDeclToStage2 path decl = do - case decl of - (OptionDecl i1 i2 a) -> return $ OptionDecl i1 i2 a - (PackageDecl n (Identity body) a) -> do - (PackageDecl n . Identity <$> packageBodyToStage2 (pushName n path) body) <*> pure a - (UsingDecl n a) -> return $ UsingDecl n a - (LocationDecl i expr a) -> LocationDecl i <$> toStage2Expr expr <*> pure a - (BitsDecl i typ a) -> BitsDecl i <$> bitsTypeToStage2 (pushId i path) typ <*> pure a - (ObjTypeDecl i body a) -> ObjTypeDecl i <$> mapM (objTypeBodyToStage2 (pushId i path)) body <*> pure a - (ImportDecl importStatement a) -> return $ ImportDecl importStatement a - (ObjectDecl i expr typ a) -> - ObjectDecl i <$> toStage2Expr expr <*> objectTypeToStage2 (pushId i path) typ <*> pure a - -bitsTypeToStage2 :: Path -> BitType Stage1 I Annot -> M Annot (BitType Stage2 I Annot) -bitsTypeToStage2 path = \case - RawBits expr a -> RawBits <$> toStage2Expr expr <*> pure a - EnumBitType expr enumBody a -> - EnumBitType <$> toStage2Expr expr <*> mapM (enumBodyToStage2 path) enumBody <*> pure a - -enumBodyToStage2 :: Path -> EnumBody Stage1 I Annot -> M Annot (EnumBody Stage2 I Annot) -enumBodyToStage2 path = \case - EnumBody constants a -> EnumBody <$> mapM (mapDirectedM (enumConstantToStage2 path)) constants <*> pure a - -enumConstantToStage2 :: Path -> EnumConstantDecl Stage1 I Annot -> M Annot (EnumConstantDecl Stage2 I Annot) -enumConstantToStage2 path = \case - EnumConstantDecl i e a -> EnumConstantDecl i <$> toStage2Expr e <*> pure a - EnumConstantReserved e a -> EnumConstantReserved <$> toStage2Expr e <*> pure a - -objTypeBodyToStage2 :: Path -> ObjTypeBody Stage1 I Annot -> M Annot (ObjTypeBody Stage2 I Annot) -objTypeBodyToStage2 path (ObjTypeBody bodyType decls annot) = - ObjTypeBody bodyType <$> mapM (mapDirectedM $ objTypeDeclToStage2 path) decls <*> pure annot - -objTypeDeclToStage2 :: Path -> ObjTypeDecl Stage1 I Annot -> M Annot (ObjTypeDecl Stage2 I Annot) -objTypeDeclToStage2 path = \case - (AssertPosStatement w expr annot) -> - AssertPosStatement w - <$> toStage2Expr expr - <*> pure annot - (TypeSubStructure (Identity deferredBody) maybeIdent annot) -> - let path' = maybe path (`pushId` path) maybeIdent - in TypeSubStructure . Identity - <$> objTypeBodyToStage2 path' deferredBody - <*> pure maybeIdent - <*> pure annot - (ReservedDecl expr a) -> - ReservedDecl <$> toStage2Expr expr <*> pure a - (RegisterDecl maybeModifier maybeIdentifier expression maybeBody annot) -> - let path' = maybe path (`pushId` path) maybeIdentifier - in RegisterDecl - maybeModifier - maybeIdentifier - <$> toStage2Expr expression - <*> mapM (registerBodyToStage2 path') maybeBody - <*> pure annot - -registerBodyToStage2 :: Path -> RegisterBody Stage1 I Annot -> M Annot (RegisterBody Stage2 I Annot) -registerBodyToStage2 path (RegisterBody bodyType (Identity (DeferredRegisterBody registerBitsDecl a1)) a2) = - RegisterBody bodyType . Identity - <$> ( DeferredRegisterBody - <$> mapM (mapDirectedM $ registerBitsDeclToStage2 path) registerBitsDecl - <*> pure a1 - ) - <*> pure a2 - -registerBitsDeclToStage2 :: Path -> RegisterBitsDecl Stage1 I Annot -> M Annot (RegisterBitsDecl Stage2 I Annot) -registerBitsDeclToStage2 path = \case - ReservedBits expr a -> ReservedBits <$> toStage2Expr expr <*> pure a - BitsSubStructure registerBody maybeIdent annot -> - let path' = maybe path (`pushId` path) maybeIdent - in BitsSubStructure <$> registerBodyToStage2 path' registerBody <*> pure maybeIdent <*> pure annot - DefinedBits maybeModifier identifier registerBitsTyperef annot -> - let path' = pushId identifier path - in ( DefinedBits - maybeModifier - identifier - <$> registerBitsTypeRefToStage2 path' registerBitsTyperef - <*> pure annot - ) - -registerBitsTypeRefToStage2 :: Path -> RegisterBitsTypeRef Stage1 I Annot -> M Annot (RegisterBitsTypeRef Stage2 I Annot) -registerBitsTypeRefToStage2 path = \case - RegisterBitsArray typeref expr annot -> - RegisterBitsArray - <$> registerBitsTypeRefToStage2 path typeref - <*> toStage2Expr expr - <*> pure annot - RegisterBitsReference name annot -> return (RegisterBitsReference name annot) - RegisterBitsJustBits expr annot -> RegisterBitsJustBits <$> toStage2Expr expr <*> pure annot - RegisterBitsAnonymousType _ anonType annot -> do - ident <- internAnonymousBitsType path =<< anonymousBitsTypeToStage2 path anonType - return $ RegisterBitsReference (identToName ident) annot - -identToName :: Identifier I a -> Name I a -identToName ident = Name (NonEmpty.singleton ident) (annot ident) - -anonymousBitsTypeToStage2 :: Path -> AnonymousBitsType Stage1 I Annot -> M Annot (AnonymousBitsType Stage2 I Annot) -anonymousBitsTypeToStage2 path = \case - AnonymousEnumBody expr (Identity body) annot -> - AnonymousEnumBody - <$> toStage2Expr expr - <*> (Identity <$> enumBodyToStage2 path body) - <*> pure annot - -objectTypeToStage2 :: Path -> ObjType Stage1 I Annot -> M Annot (ObjType Stage2 I Annot) -objectTypeToStage2 path = \case - (AnonymousObjType _ (Identity body) annot) -> do - body' <- objTypeBodyToStage2 path body - identifier <- internObjType path body' - return (ReferencedObjType (identToName identifier) annot) - (ReferencedObjType name annot) -> return $ ReferencedObjType name annot - (ArrayObjType objType expr a) -> - ArrayObjType <$> objectTypeToStage2 path objType <*> toStage2Expr expr <*> pure a - -packageBodyToStage2 :: Path -> PackageBody Stage1 I Annot -> M Annot (PackageBody Stage2 I Annot) -packageBodyToStage2 p (PackageBody decls a) = - PackageBody <$> reconfigureFiddleDecls p decls <*> pure a - -toStage2Expr :: Expression Stage1 I Annot -> M Annot (Expression Stage2 I Annot) -toStage2Expr = \case - (Var i a) -> return $ Var i a - (LitNum t a) -> LitNum <$> parseNum (unCommented a) t <*> pure a - -parseNum :: SourceSpan -> Text -> M a Integer + ![(Linkage, AnonymousBitsType Stage2 I Annot)] + +instance CompilationStage Stage1 where + type StageAfter Stage1 = Stage2 + type StageMonad Stage1 = M + type StageState Stage1 = Path + type StageFunctor Stage1 = Identity + type StageAnnotation Stage1 = Annot + +deriving instance AdvanceStage Stage1 ObjTypeBody + +deriving instance AdvanceStage Stage1 DeferredRegisterBody + +deriving instance AdvanceStage Stage1 RegisterBody + +deriving instance AdvanceStage Stage1 AnonymousBitsType + +deriving instance AdvanceStage Stage1 BitType + +deriving instance AdvanceStage Stage1 EnumBody + +deriving instance AdvanceStage Stage1 EnumConstantDecl + +deriving instance (AdvanceStage Stage1 t) => AdvanceStage Stage1 (Directed t) + +instance AdvanceStage Stage1 RegisterBitsDecl where + modifyState t = + return + . case t of + DefinedBits {definedBitsIdent = i} -> pushId i + _ -> id + +instance AdvanceStage Stage1 PackageBody where + advanceStage p (PackageBody decls a) = + PackageBody <$> reconfigureFiddleDecls p decls <*> pure a + +instance AdvanceStage Stage1 ObjTypeDecl where + modifyState t = + return + . case t of + TypeSubStructure {subStructureName = (Just n)} -> pushId n + RegisterDecl {regIdent = (Just n)} -> pushId n + _ -> id + +instance AdvanceStage Stage1 FiddleDecl where + modifyState t = + return + . case t of + PackageDecl {packageName = n} -> pushName n + BitsDecl {bitsIdent = i} -> pushId i + ObjTypeDecl {objTypeIdent = i} -> pushId i + ObjectDecl {objectIdent = i} -> pushId i + _ -> id + +instance AdvanceStage Stage1 FiddleUnit where + advanceStage path (FiddleUnit decls a) = + FiddleUnit <$> reconfigureFiddleDecls path decls <*> pure a + +instance AdvanceStage Stage1 Expression where + advanceStage _ = \case + (Var i a) -> return $ Var i a + (LitNum t a) -> LitNum <$> parseNum (unCommented a) t <*> pure a + +instance AdvanceStage Stage1 RegisterBitsTypeRef where + advanceStage path = \case + RegisterBitsArray typeref expr annot -> + RegisterBitsArray + <$> advanceStage path typeref + <*> advanceStage path expr + <*> pure annot + RegisterBitsReference name annot -> + return $ RegisterBitsReference name annot + RegisterBitsJustBits expr annot -> + RegisterBitsJustBits + <$> advanceStage path expr + <*> pure annot + RegisterBitsAnonymousType _ anonType annot -> do + ident <- + internAnonymousBitsType path + =<< advanceStage path anonType + return $ RegisterBitsReference (identToName ident) annot + +instance AdvanceStage Stage1 ObjType where + advanceStage path = \case + (AnonymousObjType _ (Identity body) annot) -> do + body' <- advanceStage path body + identifier <- internObjType path body' + return (ReferencedObjType (identToName identifier) annot) + (ReferencedObjType name annot) -> + return $ ReferencedObjType name annot + (ArrayObjType objType expr a) -> + ArrayObjType + <$> advanceStage path objType + <*> advanceStage path expr + <*> pure a + +parseNum :: SourceSpan -> Text -> Compile s Integer parseNum span txt = fromMayberOrFail span "Unable to parse number" $ case Text.unpack (Text.take 2 txt) of "0b" -> toNumWithRadix (Text.drop 2 txt) 2 @@ -255,3 +173,58 @@ parseNum span txt = fromMayberOrFail span "Unable to parse number" $ then Nothing else Just (fromIntegral a') ) + +reconfigureFiddleDecls :: + Path -> + [Directed FiddleDecl Stage1 I Annot] -> + M [Directed FiddleDecl Stage2 I Annot] +reconfigureFiddleDecls p decls = do + lastState <- get + put (State [] []) + decls <- mapM (mapDirectedM $ advanceStage p) decls + (State anonymousObjTypes anonymousBitsTypes) <- get + put lastState + + return $ + map (asDirected . resolveAnonymousObjType) anonymousObjTypes + ++ map (asDirected . resolveAnonymousBitsType) anonymousBitsTypes + ++ decls + where + resolveAnonymousObjType (Linkage linkage, objTypeBody) = + ObjTypeDecl + (Identifier linkage (annot objTypeBody)) + (pure objTypeBody) + (annot objTypeBody) + + resolveAnonymousBitsType (Linkage linkage, AnonymousEnumBody expr body a) = + BitsDecl (Identifier linkage a) (EnumBitType expr body a) a + +identToName :: Identifier I a -> Name I a +identToName ident = Name (NonEmpty.singleton ident) (annot ident) + +internObjType :: Path -> ObjTypeBody Stage2 I Annot -> M (Identifier I Annot) +internObjType path body = + let str = Text.pack $ joinPath path + in do + modify $ \(State objTypeBodies a) -> + State ((Linkage str, body) : objTypeBodies) a + return (Identifier str (annot body)) + +internAnonymousBitsType :: + Path -> + AnonymousBitsType Stage2 I Annot -> + M (Identifier I Annot) +internAnonymousBitsType path anonymousBitsType = + let str = Text.pack $ joinPath path + in do + modify $ \(State a anonymousBitsTypes) -> + State a ((Linkage str, anonymousBitsType) : anonymousBitsTypes) + return (Identifier str (annot anonymousBitsType)) + +pushId :: Identifier f a -> Path -> Path +pushId (Identifier str _) (Path lst) = + Path (PathExpression (Text.unpack str) : lst) + +pushName :: Name f a -> Path -> Path +pushName (Name idents _) path = + foldl (flip pushId) path idents diff --git a/src/Language/Fiddle/Compiler/Stage2.hs b/src/Language/Fiddle/Compiler/Stage2.hs index 2035e3d..1363620 100644 --- a/src/Language/Fiddle/Compiler/Stage2.hs +++ b/src/Language/Fiddle/Compiler/Stage2.hs @@ -1,13 +1,14 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} --- Stage3 doesn't change much from Stage2. Stage3 primarily removes the assert --- statements and checks that they are consistent with the calculations. module Language.Fiddle.Compiler.Stage2 (toStage3) where import Control.Monad (forM, forM_, unless, when) +import Control.Monad.Identity (Identity (Identity)) import Control.Monad.RWS (MonadState (get), MonadWriter (tell), gets, modify') import Data.Foldable (Foldable (toList), foldlM) import Data.Functor.Identity @@ -21,13 +22,20 @@ import qualified Data.Map as Map import Data.Maybe (fromMaybe) import qualified Data.Set as Set import qualified Data.Text as Text -import Data.Word +import Data.Word (Word32) import Language.Fiddle.Ast import Language.Fiddle.Compiler +import Language.Fiddle.Internal.Scopes import Language.Fiddle.Types (Commented (unCommented), SourceSpan) import Text.Printf (printf) import Prelude hiding (unzip) +newtype GlobalState = GlobalState + { globalScope :: Scope String (Either SizeBits SizeBytes) + } + +newtype LocalState = LocalState (ScopePath String) + type I = Identity type Annot = Commented SourceSpan @@ -36,344 +44,77 @@ type SizeBits = Word32 type SizeBytes = Word32 -data Scope t - = Scope - { subScopes :: Map String (Scope t), - scopeValues :: Map String t - } - -instance Semigroup (Scope t) where - (Scope a1 b1) <> (Scope a2 b2) = Scope (a1 <> a2) (b1 <> b2) - -instance Monoid (Scope t) where - mempty = Scope mempty mempty - -data ScopePath = ScopePath - { currentScope :: [String], - usingPaths :: [[String]] - } - -instance Semigroup ScopePath where - (ScopePath a1 b1) <> (ScopePath a2 b2) = ScopePath (a1 <> a2) (b1 <> b2) - -instance Monoid ScopePath where - mempty = ScopePath mempty mempty - -emptyScope :: Scope t -emptyScope = Scope mempty mempty - -insertScope :: NonEmpty String -> t -> Scope t -> Scope t -insertScope (s :| []) v (Scope ss sv) = Scope ss (Map.insert s v sv) -insertScope (s :| (a : as)) v (Scope ss sv) = - Scope - ( Map.alter - ( \case - (fromMaybe emptyScope -> mp) -> Just (insertScope (a :| as) v mp) - ) - s - ss - ) - sv - -lookupScope :: NonEmpty String -> Scope t -> Maybe t -lookupScope (s :| []) (Scope _ sv) = Map.lookup s sv -lookupScope (s :| (a : as)) (Scope ss _) = do - subscope <- Map.lookup s ss - lookupScope (a :| as) subscope - -lookupScopeWithPath :: ScopePath -> NonEmpty String -> Scope t -> Maybe t -lookupScopeWithPath (ScopePath current others) key scope = - let all = reverse (inits current) ++ others - e = forM all $ \prefix -> do - case lookupScope (NonEmpty.prependList prefix key) scope of - Just s -> Left s - Nothing -> Right () - in case e of - Left v -> Just v - Right _ -> Nothing - -data Stage3State = Stage3State - { inScope :: Scope (Either SizeBits SizeBytes), - scopePath :: ScopePath - } - -insertTypeSize :: Identifier f a -> SizeBits -> Compile Stage3State () -insertTypeSize (Identifier s _) size = do - modify' $ - \stage3State -> - let fullName = - NonEmpty.prependList - ((currentScope . scopePath) stage3State) - (NonEmpty.singleton (Text.unpack s)) - in stage3State - { inScope = - insertScope fullName (Right size) (inScope stage3State) - } - -lookupTypeSize :: Name I Annot -> Compile Stage3State SizeBits -lookupTypeSize (Name idents a) = do - let path = fmap (\(Identifier s _) -> Text.unpack s) idents - scopePath <- gets scopePath - mSize <- gets $ lookupScopeWithPath scopePath path . inScope - case mSize of - Just (Right sz) -> return sz - _ -> do - tell - [ Diagnostic - Error - ( printf - "Cannot resolve %s" - (intercalate "." $ NonEmpty.toList path) - ) - (unCommented a) - ] - compilationFailure - -emptyState = Stage3State mempty mempty - toStage3 :: FiddleUnit Stage2 I Annot -> Compile () (FiddleUnit Stage3 I Annot) -toStage3 (FiddleUnit decls a) = - snd - <$> subCompile - emptyState - ( FiddleUnit <$> mapM (mapDirectedM fiddleDeclToStage3) decls <*> pure a - ) +toStage3 = fmap snd . subCompile (GlobalState mempty) . advanceStage (LocalState mempty) + +instance CompilationStage Stage2 where + type StageAfter Stage2 = Stage3 + type StageMonad Stage2 = Compile GlobalState + type StageState Stage2 = LocalState + type StageFunctor Stage2 = Identity + type StageAnnotation Stage2 = Commented SourceSpan + +deriving instance AdvanceStage Stage2 FiddleUnit + +deriving instance AdvanceStage Stage2 Expression + +instance AdvanceStage Stage2 FiddleDecl where + modifyState t s = case t of + (BitsDecl id typ a) -> do + typeSize <- getTypeSize typ + insertTypeSize s id typeSize + return s + (PackageDecl n _ _) -> do + let strs = nameToList n + let (LocalState scopePath) = s + + return $ + LocalState $ + scopePath {currentScope = strs ++ currentScope scopePath} + (UsingDecl n _) -> + let (LocalState scopePath) = s + in return $ + LocalState $ + scopePath + { usingPaths = nameToList n : usingPaths scopePath + } + _ -> return s -exprToSize :: - (NumberType stage ~ Integer) => - Expression stage I Annot -> - Compile s Integer -exprToSize (LitNum num _) = return num -exprToSize e = do - tell [Diagnostic Error "Variables not allowed" (unCommented $ annot e)] - compilationFailure +nameToList :: Name f a -> [String] +nameToList (Name idents _) = map (\(Identifier (Text.unpack -> s) _) -> s) (toList idents) -getTypeSize :: BitType Stage2 I Annot -> Compile s SizeBits -getTypeSize (RawBits expr _) = fromIntegral <$> exprToSize expr -getTypeSize (EnumBitType expr (Identity (EnumBody constants _)) ann) = do - declaredSize <- fromIntegral <$> exprToSize expr +instance AdvanceStage Stage2 ObjTypeBody where + advanceStage s body = fst <$> objTypeBodyToStage3 s body 0 - -- If the declared size is less than or equal to 4, we'll enforce that the - -- enum is packed. This is to make sure the user has covered all bases. - when (declaredSize <= 4) $ do - imap <- - foldlM - ( \imap (undirected -> enumConst) -> do - number <- case enumConst of - EnumConstantDecl _ expr _ -> exprToSize expr - EnumConstantReserved expr _ -> exprToSize expr +deriving instance AdvanceStage Stage2 ObjType - when (number >= 2 ^ declaredSize) $ - tell - [ Diagnostic - Error - ( printf - "Enum constant too large. Max allowed %d\n" - ((2 :: Int) ^ declaredSize) - ) - (unCommented (annot enumConst)) - ] +deriving instance AdvanceStage Stage2 DeferredRegisterBody - return $ IntMap.insert (fromIntegral number) True imap - ) - IntMap.empty - constants - let missing = - filter (not . (`IntMap.member` imap)) [0 .. 2 ^ declaredSize - 1] - unless (null missing) $ - tell - [ Diagnostic - Warning - ( printf - "Missing enum constants %s. Small enums should be fully \ - \ populated. Use 'reserved' if needed." - (intercalate ", " (map show missing)) - ) - (unCommented ann) - ] +deriving instance AdvanceStage Stage2 RegisterBitsDecl - return declaredSize +instance AdvanceStage Stage2 RegisterBody where + advanceStage s body = fst <$> registerBodyToStage3 s body -addCurrentScope :: [String] -> Compile Stage3State () -addCurrentScope s = do - modify' $ \st@(Stage3State {scopePath = (ScopePath current others)}) -> - st {scopePath = ScopePath (current ++ s) others} - -fiddleDeclToStage3 :: - FiddleDecl Stage2 I Annot -> Compile Stage3State (FiddleDecl Stage3 I Annot) -fiddleDeclToStage3 = \case - OptionDecl i1 i2 a -> return $ OptionDecl i1 i2 a - PackageDecl n@(Name idents _) body a -> do - let strs = map (\(Identifier (Text.unpack -> s) _) -> s) (toList idents) - Stage3State {scopePath = savedScopePath} <- get - addCurrentScope strs - PackageDecl n - <$> mapM packageBodyToStage3 body - <*> pure a - <* modify' (\st -> st {scopePath = savedScopePath}) - UsingDecl n@(Name idents _) a -> do - let strs = map (\(Identifier t _) -> Text.unpack t) (toList idents) - modify' - ( \st -> - let (ScopePath cur using) = scopePath st - in st - { scopePath = ScopePath cur (strs : using) - } - ) - return $ UsingDecl n a - LocationDecl id expr a -> return $ LocationDecl id (switchStage expr) a - BitsDecl id typ a -> do - typeSize <- getTypeSize typ - insertTypeSize id typeSize - return $ BitsDecl id (switchStage typ) a - ObjTypeDecl ident body a -> - ObjTypeDecl ident - <$> mapM (\bt -> fst <$> objTypeBodyToStage3 bt 0) body - <*> pure a - ImportDecl importStatement a -> return $ ImportDecl importStatement a - ObjectDecl ident expr typ a -> - ObjectDecl - ident - (switchStage expr) - <$> objTypeToStage3 typ - <*> pure a - -objTypeToStage3 :: - ObjType Stage2 I Annot -> Compile Stage3State (ObjType Stage3 I Annot) -objTypeToStage3 = \case - ArrayObjType objtype expr a -> - ArrayObjType - <$> objTypeToStage3 objtype - <*> pure (switchStage expr) - <*> pure a - ReferencedObjType ident a -> return $ ReferencedObjType ident a +deriving instance AdvanceStage Stage2 RegisterBitsTypeRef -registerBodyToStage3 :: - RegisterBody Stage2 I Annot -> - Compile Stage3State (RegisterBody Stage3 I Annot, Word32) -registerBodyToStage3 - (RegisterBody bodyType (Identity deferredRegisterBody) a') = do - let isUnion = case bodyType of - Union {} -> True - _ -> False +deriving instance AdvanceStage Stage2 AnonymousBitsType - case deferredRegisterBody of - DeferredRegisterBody decls a -> do - (cur, returned) <- - foldlM - ( \(cursor, returned) decl -> - case undirected decl of - ReservedBits expr a -> do - size <- fromIntegral <$> exprToSize expr - let s3 = - mapDirected - (const $ ReservedBits (switchStage expr) a) - decl - if isUnion - then checkUnion cursor size (s3 : returned) a - else - return (cursor + size, s3 : returned) - BitsSubStructure registerBody maybeIdent annot -> do - checkBitsSubStructure registerBody maybeIdent annot - - (newBody, subsize) <- registerBodyToStage3 registerBody - let s3 = - mapDirected - (const $ BitsSubStructure newBody maybeIdent annot) - decl - - if isUnion - then checkUnion cursor subsize (s3 : returned) a - else - return (cursor + subsize, s3 : returned) - DefinedBits modifier identifier typeref a -> do - (s3TypeRef, size) <- registerBitsTypeRefToStage3 typeref - let s3 = - mapDirected - (const $ DefinedBits modifier identifier s3TypeRef a) - decl +deriving instance AdvanceStage Stage2 BitType - if isUnion - then checkUnion cursor size (s3 : returned) a - else - return (cursor + size, s3 : returned) - ) - (0, []) - decls +deriving instance AdvanceStage Stage2 EnumBody - return - ( RegisterBody - bodyType - (Identity (DeferredRegisterBody (reverse returned) a)) - a', - cur - ) - where - checkBitsSubStructure - (RegisterBody bodyType (Identity (DeferredRegisterBody decls _)) _) - maybeIdent - annot = - let emitWarning s = tell [Diagnostic Warning s (unCommented annot)] - in case () of - () - | [_] <- decls, - (Union {}) <- bodyType -> - emitWarning "Union with a single field. Should this be a struct?" - () - | [_] <- decls, - (Struct {}) <- bodyType, - Nothing <- maybeIdent -> - emitWarning "Anonymous sub-struct with single field is superfluous." - () - | [] <- decls -> - emitWarning - ( printf - "Empty sub-%s is superfluous." - ( case bodyType of - Union {} -> "union" - Struct {} -> "struct" - ) - ) - _ -> return () +deriving instance AdvanceStage Stage2 EnumConstantDecl -checkUnion :: Word32 -> Word32 -> b -> Commented SourceSpan -> Compile Stage3State (Word32, b) -checkUnion cursor subsize ret a = do - when (cursor /= 0 && subsize /= cursor) $ do - tell - [ Diagnostic - Warning - ( printf - "Jagged union found. Found size %d, expected %d.\n \ - \ Please wrap smaller fields in a struct with padding so all \ - \ fields are the same size?" - subsize - cursor - ) - (unCommented a) - ] - return (max cursor subsize, ret) +deriving instance AdvanceStage Stage2 PackageBody -registerBitsTypeRefToStage3 :: - RegisterBitsTypeRef Stage2 I Annot -> - Compile Stage3State (RegisterBitsTypeRef Stage3 I Annot, Word32) -registerBitsTypeRefToStage3 = \case - RegisterBitsArray ref expr a -> do - (ref', size) <- registerBitsTypeRefToStage3 ref - multiplier <- exprToSize expr - return - ( RegisterBitsArray ref' (switchStage expr) a, - size * fromIntegral multiplier - ) - RegisterBitsReference name a -> - (RegisterBitsReference name a,) <$> lookupTypeSize name - RegisterBitsJustBits expr a -> - (RegisterBitsJustBits (switchStage expr) a,) - . fromIntegral - <$> exprToSize expr +deriving instance (AdvanceStage Stage2 t) => AdvanceStage Stage2 (Directed t) objTypeBodyToStage3 :: - ObjTypeBody Stage2 I Annot -> Word32 -> Compile Stage3State (ObjTypeBody Stage3 I Annot, Word32) -objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do + LocalState -> + ObjTypeBody Stage2 I Annot -> + Word32 -> + Compile GlobalState (ObjTypeBody Stage3 I Annot, Word32) +objTypeBodyToStage3 st (ObjTypeBody bodyType decls a) startOff = do let isUnion = case bodyType of Union {} -> True _ -> False @@ -383,7 +124,9 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do case undirected decl of RegisterDecl mMod mIdent expr mBody a -> do (s3RegisterBody, mCalculatedSize) <- - fUnzip <$> mapM registerBodyToStage3 mBody + fUnzip <$> mapM (registerBodyToStage3 st) mBody + + nExpr <- advanceStage st expr let s3 = mapDirected @@ -391,7 +134,7 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do RegisterDecl mMod mIdent - (switchStage expr) + nExpr s3RegisterBody a ) @@ -436,6 +179,7 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do TypeSubStructure (Identity subBody) maybeIdent annot -> do (newBody, size) <- objTypeBodyToStage3 + st subBody ( if isUnion then startOff else cursor ) @@ -456,8 +200,10 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do "Can only reserve a multiple of 8 bits in this context." (unCommented a) ] + + expr' <- advanceStage st expr let size = size' `div` 8 - let s3 = mapDirected (const $ ReservedDecl (switchStage expr) annot) decl + let s3 = mapDirected (const $ ReservedDecl expr' annot) decl if isUnion then checkUnion cursor size (s3 : returned) a @@ -517,6 +263,242 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do pushApply (Just (a, b)) = (Just a, Just b) pushApply Nothing = (Nothing, Nothing) -packageBodyToStage3 :: PackageBody Stage2 I Annot -> Compile Stage3State (PackageBody Stage3 I Annot) -packageBodyToStage3 (PackageBody decls a) = - PackageBody <$> mapM (mapDirectedM fiddleDeclToStage3) decls <*> pure a +registerBodyToStage3 :: + LocalState -> + RegisterBody Stage2 I Annot -> + Compile GlobalState (RegisterBody Stage3 I Annot, Word32) +registerBodyToStage3 + st + (RegisterBody bodyType (Identity deferredRegisterBody) a') = do + let isUnion = case bodyType of + Union {} -> True + _ -> False + + case deferredRegisterBody of + DeferredRegisterBody decls a -> do + (cur, returned) <- + foldlM + ( \(cursor, returned) decl -> + case undirected decl of + ReservedBits expr a -> do + size <- fromIntegral <$> exprToSize expr + expr' <- advanceStage st expr + let s3 = + mapDirected + (const $ ReservedBits expr' a) + decl + if isUnion + then checkUnion cursor size (s3 : returned) a + else + return (cursor + size, s3 : returned) + BitsSubStructure registerBody maybeIdent annot -> do + checkBitsSubStructure registerBody maybeIdent annot + + (newBody, subsize) <- registerBodyToStage3 st registerBody + let s3 = + mapDirected + (const $ BitsSubStructure newBody maybeIdent annot) + decl + + if isUnion + then checkUnion cursor subsize (s3 : returned) a + else + return (cursor + subsize, s3 : returned) + DefinedBits modifier identifier typeref a -> do + (s3TypeRef, size) <- registerBitsTypeRefToStage3 st typeref + let s3 = + mapDirected + (const $ DefinedBits modifier identifier s3TypeRef a) + decl + + if isUnion + then checkUnion cursor size (s3 : returned) a + else + return (cursor + size, s3 : returned) + ) + (0, []) + decls + + return + ( RegisterBody + bodyType + (Identity (DeferredRegisterBody (reverse returned) a)) + a', + cur + ) + where + checkBitsSubStructure + (RegisterBody bodyType (Identity (DeferredRegisterBody decls _)) _) + maybeIdent + annot = + let emitWarning s = tell [Diagnostic Warning s (unCommented annot)] + in case () of + () + | [_] <- decls, + (Union {}) <- bodyType -> + emitWarning "Union with a single field. Should this be a struct?" + () + | [_] <- decls, + (Struct {}) <- bodyType, + Nothing <- maybeIdent -> + emitWarning "Anonymous sub-struct with single field is superfluous." + () + | [] <- decls -> + emitWarning + ( printf + "Empty sub-%s is superfluous." + ( case bodyType of + Union {} -> "union" + Struct {} -> "struct" + ) + ) + _ -> return () + +registerBitsTypeRefToStage3 :: + LocalState -> + RegisterBitsTypeRef Stage2 I Annot -> + Compile GlobalState (RegisterBitsTypeRef Stage3 I Annot, Word32) +registerBitsTypeRefToStage3 localState = \case + RegisterBitsArray ref expr a -> do + (ref', size) <- registerBitsTypeRefToStage3 localState ref + multiplier <- exprToSize expr + expr' <- advanceStage localState expr + return + ( RegisterBitsArray ref' expr' a, + size * fromIntegral multiplier + ) + RegisterBitsReference name a -> + (RegisterBitsReference name a,) <$> lookupTypeSize localState name + RegisterBitsJustBits expr a -> do + expr' <- advanceStage localState expr + (RegisterBitsJustBits expr' a,) + . fromIntegral + <$> exprToSize expr + +checkUnion :: Word32 -> Word32 -> b -> Commented SourceSpan -> Compile s (Word32, b) +checkUnion cursor subsize ret a = do + when (cursor /= 0 && subsize /= cursor) $ do + tell + [ Diagnostic + Warning + ( printf + "Jagged union found. Found size %d, expected %d.\n \ + \ Please wrap smaller fields in a struct with padding so all \ + \ fields are the same size?" + subsize + cursor + ) + (unCommented a) + ] + return (max cursor subsize, ret) + +exprToSize :: + (NumberType stage ~ Integer) => + Expression stage I Annot -> + Compile s Integer +exprToSize (LitNum num _) = return num +exprToSize e = do + tell [Diagnostic Error "Variables not allowed" (unCommented $ annot e)] + compilationFailure + +lookupTypeSize :: LocalState -> Name I Annot -> Compile GlobalState SizeBits +lookupTypeSize (LocalState scopePath) (Name idents a) = do + -- Convert the list of identifiers to a string path + let path = fmap (\(Identifier s _) -> Text.unpack s) idents + + -- Get the current scope and perform the lookup + results <- gets $ lookupScopeWithPath scopePath path . globalScope + + case results of + -- Successfully resolved to a unique size + [(_, Right sz)] -> return sz + -- Multiple ambiguous results found + matches@(_ : _) -> do + -- Generate a list of ambiguous paths for error reporting + let ambiguousPaths = + map + ( \(resolvedPath, _) -> + intercalate "." (NonEmpty.toList resolvedPath) + ) + matches + tell + [ Diagnostic + Error + ( printf + "Ambiguous occurrence of '%s'. Multiple matches found:\n%s" + (intercalate "." $ NonEmpty.toList path) + (unlines ambiguousPaths) -- List all ambiguous paths + ) + (unCommented a) + ] + compilationFailure + + -- No matches found + _ -> do + tell + [ Diagnostic + Error + ( printf + "Cannot resolve '%s'. No matching symbols found." + (intercalate "." $ NonEmpty.toList path) + ) + (unCommented a) + ] + compilationFailure + +getTypeSize :: BitType Stage2 I Annot -> Compile s SizeBits +getTypeSize (RawBits expr _) = fromIntegral <$> exprToSize expr +getTypeSize (EnumBitType expr (Identity (EnumBody constants _)) ann) = do + declaredSize <- fromIntegral <$> exprToSize expr + + -- If the declared size is less than or equal to 4, we'll enforce that the + -- enum is packed. This is to make sure the user has covered all bases. + when (declaredSize <= 4) $ do + imap <- + foldlM + ( \imap (undirected -> enumConst) -> do + number <- case enumConst of + EnumConstantDecl _ expr _ -> exprToSize expr + EnumConstantReserved expr _ -> exprToSize expr + + when (number >= 2 ^ declaredSize) $ + tell + [ Diagnostic + Error + ( printf + "Enum constant too large. Max allowed %d\n" + ((2 :: Int) ^ declaredSize) + ) + (unCommented (annot enumConst)) + ] + + return $ IntMap.insert (fromIntegral number) True imap + ) + IntMap.empty + constants + let missing = + filter (not . (`IntMap.member` imap)) [0 .. 2 ^ declaredSize - 1] + unless (null missing) $ + tell + [ Diagnostic + Warning + ( printf + "Missing enum constants %s. Small enums should be fully \ + \ populated. Use 'reserved' if needed." + (intercalate ", " (map show missing)) + ) + (unCommented ann) + ] + + return declaredSize + +insertTypeSize :: LocalState -> Identifier f a -> SizeBits -> Compile GlobalState () +insertTypeSize (LocalState scopePath) (Identifier s _) size = do + modify' $ + \(GlobalState globalScope) -> + let fullName = + NonEmpty.prependList + (currentScope scopePath) + (NonEmpty.singleton (Text.unpack s)) + in GlobalState $ + insertScope fullName (Right size) globalScope diff --git a/src/Language/Fiddle/GenericTree.hs b/src/Language/Fiddle/GenericTree.hs index 59db6aa..031f6ab 100644 --- a/src/Language/Fiddle/GenericTree.hs +++ b/src/Language/Fiddle/GenericTree.hs @@ -40,17 +40,38 @@ type Context stage = data GenericSyntaxTree f a where {- GenericSyntaxtTree with a name and children. -} SyntaxTreeObject :: - forall a f tree. - (Typeable tree) => + forall t f a. + (Typeable t, Typeable f, Typeable a) => String -> [GenericSyntaxTree f a] -> a -> - tree -> + t f a -> GenericSyntaxTree f a SyntaxTreeList :: [GenericSyntaxTree f a] -> GenericSyntaxTree f a SyntaxTreeDeferred :: f (GenericSyntaxTree f a) -> GenericSyntaxTree f a SyntaxTreeValue :: String -> GenericSyntaxTree f a +alterGenericSyntaxTree :: + (Functor f) => + (GenericSyntaxTree f a -> Maybe (GenericSyntaxTree f a)) -> + GenericSyntaxTree f a -> + GenericSyntaxTree f a +alterGenericSyntaxTree fn genericTree + | (Just newGenericTree) <- fn genericTree = newGenericTree + | otherwise = + case genericTree of + SyntaxTreeObject str members annot tree -> + SyntaxTreeObject + str + (map (alterGenericSyntaxTree fn) members) + annot + tree + SyntaxTreeList members -> + SyntaxTreeList $ map (alterGenericSyntaxTree fn) members + SyntaxTreeDeferred sub -> + SyntaxTreeDeferred $ fmap (alterGenericSyntaxTree fn) sub + v -> v + instance ToJSON Comment where toJSON (NormalComment str) = object ["normal" .= str] toJSON (DocComment str) = object ["doc" .= str] @@ -71,9 +92,11 @@ instance ToJSON SourcePos where "col" .= sourceColumn sourcePos ] -instance (Foldable f, ToJSON a) => ToJSON (GenericSyntaxTree f a) where +instance (Foldable f) => ToJSON (GenericSyntaxTree f (Maybe Value)) where toJSON = \case - (SyntaxTreeObject typ membs a _) -> + (SyntaxTreeObject typ membs Nothing _) -> + object ["_con" .= typ, "_members" .= membs] + (SyntaxTreeObject typ membs (Just a) _) -> object ["_con" .= typ, "_members" .= membs, "_annot" .= a] (SyntaxTreeList l) -> Array $ Data.Vector.fromList $ map toJSON l @@ -82,8 +105,10 @@ instance (Foldable f, ToJSON a) => ToJSON (GenericSyntaxTree f a) where (SyntaxTreeValue s) -> String (Data.Text.pack s) toEncoding = \case - (SyntaxTreeObject typ membs a t) -> + (SyntaxTreeObject typ membs (Just a) t) -> pairs $ "_type" .= show (typeOf t) <> "_con" .= typ <> "_members" .= membs <> "_annot" .= a + (SyntaxTreeObject typ membs Nothing t) -> + pairs $ "_type" .= show (typeOf t) <> "_con" .= typ <> "_members" .= membs (SyntaxTreeList l) -> foldable $ map toJSON l (SyntaxTreeDeferred fdef) -> @@ -118,7 +143,7 @@ class ToGenericSyntaxTree (t :: SynTree) where toGenericSyntaxTree t = gToGenericSyntaxTree t (from t) class GToGenericSyntaxTree r f a where - gToGenericSyntaxTree :: (Typeable t) => t -> r x -> GenericSyntaxTree f a + gToGenericSyntaxTree :: (Typeable t, Typeable f, Typeable a) => t f a -> r x -> GenericSyntaxTree f a class GToMemberList r f a where gToMemberList :: Int -> r x -> [GenericSyntaxTree f a] diff --git a/src/Language/Fiddle/Internal/Scopes.hs b/src/Language/Fiddle/Internal/Scopes.hs new file mode 100644 index 0000000..280945d --- /dev/null +++ b/src/Language/Fiddle/Internal/Scopes.hs @@ -0,0 +1,101 @@ +module Language.Fiddle.Internal.Scopes where + +import Control.Monad (forM) +import Data.List (inits, intercalate) +import Data.List.NonEmpty (NonEmpty (..), prependList) +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe) + +-- | 'Scope' represents a hierarchical structure for storing key-value pairs. +-- It can contain nested sub-scopes, which are stored in 'subScopes', +-- and the values for a specific scope are stored in 'scopeValues'. +data Scope k v = Scope + { subScopes :: Map k (Scope k v), -- Nested sub-scopes + scopeValues :: Map k v -- Values stored in the current scope + } + +-- | 'ScopePath' keeps track of the current scope path as a list of keys, +-- and also includes any additional paths (like imported modules or +-- using namespaces) that might be referenced for lookup. +data ScopePath k = ScopePath + { currentScope :: [k], -- Current path within the scope hierarchy + usingPaths :: [[k]] -- Additional paths for resolving symbols + } + +-- | The 'Semigroup' instance for 'Scope' allows combining two scopes, +-- where sub-scopes and values are merged together. +instance (Ord k) => Semigroup (Scope k t) where + (Scope ss1 sv1) <> (Scope ss2 sv2) = + Scope (Map.unionWith (<>) ss1 ss2) (Map.union sv1 sv2) + +-- | The 'Monoid' instance for 'Scope' provides an empty scope with +-- no sub-scopes or values. +instance (Ord k) => Monoid (Scope k t) where + mempty = Scope mempty mempty + +-- | The 'Semigroup' instance for 'ScopePath' allows combining paths, +-- appending the current scope path and using paths. +instance Semigroup (ScopePath k) where + (ScopePath a1 b1) <> (ScopePath a2 b2) = ScopePath (a1 <> a2) (b1 <> b2) + +-- | The 'Monoid' instance for 'ScopePath' provides an empty path. +instance Monoid (ScopePath k) where + mempty = ScopePath mempty mempty + +-- | 'insertScope' inserts a value 'v' into the scope at the specified +-- key path ('NonEmpty k'). If the key path does not exist, it is created. +insertScope :: (Ord k) => NonEmpty k -> t -> Scope k t -> Scope k t +insertScope (s :| []) v (Scope ss sv) = Scope ss (Map.insert s v sv) +insertScope (s :| (a : as)) v (Scope ss sv) = + Scope + ( Map.alter + ( \case + (fromMaybe mempty -> mp) -> Just (insertScope (a :| as) v mp) + ) + s + ss + ) + sv + +-- | 'lookupScope' performs a lookup of a value in the scope using a key path +-- ('NonEmpty k'). It traverses through sub-scopes as defined by the path. +lookupScope :: (Ord k) => NonEmpty k -> Scope k t -> Maybe t +lookupScope (s :| []) (Scope _ sv) = Map.lookup s sv +lookupScope (s :| (a : as)) (Scope ss _) = do + subscope <- Map.lookup s ss + lookupScope (a :| as) subscope + +-- | 'lookupScopeWithPath' searches for a key in the scope by trying all possible +-- paths, including the current scope path and any additional 'using' paths. +-- It returns a list of all valid matches, each paired with the corresponding +-- full key path that resolved to a value. This is useful in cases where symbol +-- resolution might be ambiguous or multiple valid resolutions exist. +-- +-- The result is a list of tuples, where the first element of each tuple is the +-- fully-resolved key path (as a 'NonEmpty' list) and the second element is the +-- resolved value. +-- +-- This function handles multiple levels of scope resolution, such as those +-- introduced by 'using' directives, and concatenates the results from each +-- possible path. +-- +-- Parameters: +-- * 'ScopePath k' - The current scope path and any additional 'using' paths. +-- * 'NonEmpty k' - The key path to resolve within the scope. +-- * 'Scope k t' - The scope containing sub-scopes and values. +-- +-- Returns: A list of tuples where each tuple contains a fully-resolved key +-- path (as a 'NonEmpty k') and the corresponding value ('t'). +lookupScopeWithPath :: + (Ord k) => + ScopePath k -> + NonEmpty k -> + Scope k t -> + [(NonEmpty k, t)] +lookupScopeWithPath (ScopePath current others) key scope = + let allPaths = reverse (inits current) ++ others + in flip concatMap allPaths $ \prefix -> do + case lookupScope (prependList prefix key) scope of + Just s -> [(prependList prefix key, s)] + Nothing -> [] diff --git a/src/Language/Fiddle/Parser.hs b/src/Language/Fiddle/Parser.hs index 85ae65e..980925f 100644 --- a/src/Language/Fiddle/Parser.hs +++ b/src/Language/Fiddle/Parser.hs @@ -33,8 +33,8 @@ type Pa (a :: Stage -> (Type -> Type) -> Type -> Type) = P (a 'Stage1 F (Comment type PaS (a :: (Type -> Type) -> Type -> Type) = P (a F (Commented SourceSpan)) -comment :: P Comment -comment = +commentP :: P Comment +commentP = token $ \case (TokComment c) -> Just (NormalComment c) (TokDocComment c) -> Just (DocComment c) @@ -52,46 +52,46 @@ isComment (Token t _) = stripTrailingComments :: [Token s] -> [Token s] stripTrailingComments = reverse . dropWhile isComment . reverse -directed :: Pa t -> PaS (Directed t 'Stage1) -directed subparser = withMeta $ do - Directed <$> many directive <*> subparser +directedP :: Pa t -> PaS (Directed t 'Stage1) +directedP subparser = withMeta $ do + Directed <$> many directiveP <*> subparser -directive :: PaS Directive -directive = +directiveP :: PaS Directive +directiveP = withMeta $ - Directive <$> defer directiveBodyTokens directiveBody + Directive <$> defer directiveBodyTokens directiveBodyP -directiveBody :: PaS DirectiveBody -directiveBody = withMeta $ do - DirectiveBody <$> many (directiveElement <* (void (tok TokComma) <|> eof)) +directiveBodyP :: PaS DirectiveBody +directiveBodyP = withMeta $ do + DirectiveBody <$> many (directiveElementP <* (void (tok TokComma) <|> eof)) -directiveElement :: PaS DirectiveElement -directiveElement = withMeta $ do - identifier1 <- nextText +directiveElementP :: PaS DirectiveElement +directiveElementP = withMeta $ do + identifier1 <- nextTextP choice [ do tok TokColon let backend = identifier1 - key <- nextText + key <- nextTextP choice [ do tok TokEq - DirectiveElementKeyValue (Just backend) key <$> directiveExpression, + DirectiveElementKeyValue (Just backend) key <$> directiveExpressionP, do return (DirectiveElementKey (Just backend) key) ], do tok TokEq let key = identifier1 - DirectiveElementKeyValue Nothing key <$> directiveExpression, + DirectiveElementKeyValue Nothing key <$> directiveExpressionP, return $ DirectiveElementKey Nothing identifier1 ] -nextText :: PaS Identifier -nextText = withMeta $ Identifier <$> token textOf +nextTextP :: PaS Identifier +nextTextP = withMeta $ Identifier <$> token textOf -directiveExpression :: PaS DirectiveExpression -directiveExpression = withMeta $ do +directiveExpressionP :: PaS DirectiveExpression +directiveExpressionP = withMeta $ do choice [ do token $ \case @@ -103,80 +103,64 @@ directiveExpression = withMeta $ do fiddleUnit :: Pa FiddleUnit fiddleUnit = do withMeta - ( FiddleUnit <$> many1 (directed fiddleDecl <* tok TokSemi) + ( FiddleUnit <$> many1 (directedP fiddleDeclP <* tok TokSemi) ) - <* many comment + <* many commentP -stringToken :: P Text -stringToken = +stringTokenP :: P Text +stringTokenP = token ( \case (TokString str) -> Just str _ -> Nothing ) -importList :: PaS ImportList -importList = withMeta $ do +importListP :: PaS ImportList +importListP = withMeta $ do tok TokLParen ImportList <$> many (ident <* (tok TokComma <|> lookAhead (tok TokRParen))) <* tok TokRParen -importStatement :: PaS ImportStatement -importStatement = +importStatementP :: PaS ImportStatement +importStatementP = withMeta $ - ImportStatement <$> stringToken <*> optionMaybe importList + ImportStatement <$> stringTokenP <*> optionMaybe importListP -fiddleDecl :: Pa FiddleDecl -fiddleDecl = do +fiddleDeclP :: Pa FiddleDecl +fiddleDeclP = do withMeta $ do t <- tokenType <$> anyToken case t of - KWOption -> OptionDecl <$> nextText <*> nextText + KWOption -> OptionDecl <$> nextTextP <*> nextTextP KWPackage -> PackageDecl <$> name - <*> defer body packageBody + <*> defer body packageBodyP KWUsing -> UsingDecl <$> name - KWLocation -> LocationDecl <$> ident <*> (tok TokEq >> expression) - KWBits -> BitsDecl <$> ident <*> (tok TokColon >> bitType) - KWImport -> ImportDecl <$> importStatement + KWLocation -> LocationDecl <$> ident <*> (tok TokEq >> expressionP) + KWBits -> BitsDecl <$> ident <*> (tok TokColon >> bitTypeP) + KWImport -> ImportDecl <$> importStatementP KWType -> ObjTypeDecl <$> ident <*> ( do tok TokColon - bt <- bodyType - defer body (objTypeBody bt) + bt <- bodyTypeP + defer body (objTypeBodyP bt) ) KWInstance -> ObjectDecl <$> ident - <*> (tok KWAt *> expression) - <*> (tok TokColon *> objType) + <*> (tok KWAt *> expressionP) + <*> (tok TokColon *> objTypeP) _ -> fail $ printf "Unexpected token %s. Expected top-level declaration." (show t) --- choice --- [ tok KWOption >> OptionDecl <$> ident <*> ident, --- tok KWPackage >> PackageDecl --- <$> ident --- <*> defer body packageBody, --- tok KWLocation >> LocationDecl <$> ident <*> (tok TokEq >> expression), --- tok KWBits >> BitsDecl <$> ident <*> (tok TokColon >> bitType), --- tok KWObjtype --- >> ObjTypeDecl <$> ident <*> (tok TokColon >> defer body objTypeBody), --- tok KWObject --- >> ObjectDecl --- <$> ident --- <*> (tok KWAt *> expression) --- <*> (tok TokColon *> objType) --- ] - -objType :: Pa ObjType -objType = do - base <- withMeta baseObj +objTypeP :: Pa ObjType +objTypeP = do + base <- withMeta baseObjP recur' <- recur return $ recur' base where @@ -184,54 +168,54 @@ objType = do recur = ( do withMeta $ do - expr <- tok TokLBracket *> expression <* tok TokRBracket + expr <- tok TokLBracket *> expressionP <* tok TokRBracket recur' <- recur return (\met base -> recur' (ArrayObjType base expr met)) ) <|> return id - baseObj :: P (A -> ObjType Stage1 F A) - baseObj = + baseObjP :: P (A -> ObjType Stage1 F A) + baseObjP = (ReferencedObjType <$> name) <|> ( do - t <- bodyType - AnonymousObjType (Witness ()) <$> defer body (objTypeBody t) + t <- bodyTypeP + AnonymousObjType (Witness ()) <$> defer body (objTypeBodyP t) ) -exprInParen :: Pa Expression -exprInParen = tok TokLParen *> expression <* tok TokRParen +exprInParenP :: Pa Expression +exprInParenP = tok TokLParen *> expressionP <* tok TokRParen -objTypeBody :: BodyType F (Commented SourceSpan) -> Pa ObjTypeBody -objTypeBody bt = +objTypeBodyP :: BodyType F (Commented SourceSpan) -> Pa ObjTypeBody +objTypeBodyP bt = withMeta $ - ObjTypeBody bt <$> many (directed objTypeDecl <* tok TokSemi) + ObjTypeBody bt <$> many (directedP objTypeDeclP <* tok TokSemi) -objTypeDecl :: Pa ObjTypeDecl -objTypeDecl = +objTypeDeclP :: Pa ObjTypeDecl +objTypeDeclP = withMeta $ ( do tok KWAssertPos - AssertPosStatement (Witness ()) <$> exprInParen + AssertPosStatement (Witness ()) <$> exprInParenP ) <|> ( do tok KWReserved - ReservedDecl <$> exprInParen + ReservedDecl <$> exprInParenP ) <|> ( do - bt <- bodyType - TypeSubStructure <$> defer body (objTypeBody bt) <*> optionMaybe ident + bt <- bodyTypeP + TypeSubStructure <$> defer body (objTypeBodyP bt) <*> optionMaybe ident ) <|> ( do - mod <- optionMaybe modifier + mod <- optionMaybe modifierP tok KWReg RegisterDecl mod <$> optionMaybe ident - <*> exprInParen - <*> optionMaybe (tok TokColon *> registerBody) + <*> exprInParenP + <*> optionMaybe (tok TokColon *> registerBodyP) ) -modifier :: PaS Modifier -modifier = +modifierP :: PaS Modifier +modifierP = withMeta $ ModifierKeyword <$> choice @@ -240,87 +224,87 @@ modifier = tok KWWo >> return Wo ] -bitBodyType :: PaS BodyType -bitBodyType = +bitBodyTypeP :: PaS BodyType +bitBodyTypeP = withMeta $ (tok KWStruct >> return Struct) <|> (tok KWUnion >> return Union) -bodyType :: PaS BodyType -bodyType = +bodyTypeP :: PaS BodyType +bodyTypeP = withMeta $ (tok KWStruct >> return Struct) <|> (tok KWUnion >> return Union) -registerBody :: Pa RegisterBody -registerBody = withMeta $ RegisterBody <$> bitBodyType <*> defer body deferredRegisterBody +registerBodyP :: Pa RegisterBody +registerBodyP = withMeta $ RegisterBody <$> bitBodyTypeP <*> defer body deferredRegisterBodyP -deferredRegisterBody :: Pa DeferredRegisterBody -deferredRegisterBody = +deferredRegisterBodyP :: Pa DeferredRegisterBody +deferredRegisterBodyP = withMeta $ - DeferredRegisterBody <$> many (directed registerBitsDecl <* tok TokSemi) + DeferredRegisterBody <$> many (directedP registerBitsDeclP <* tok TokSemi) -registerBitsDecl :: Pa RegisterBitsDecl -registerBitsDecl = +registerBitsDeclP :: Pa RegisterBitsDecl +registerBitsDeclP = withMeta $ ( do - tok KWReserved >> ReservedBits <$> exprInParen + tok KWReserved >> ReservedBits <$> exprInParenP ) - <|> (BitsSubStructure <$> registerBody <*> optionMaybe ident) + <|> (BitsSubStructure <$> registerBodyP <*> optionMaybe ident) <|> ( DefinedBits - <$> optionMaybe modifier + <$> optionMaybe modifierP <*> ident - <*> (tok TokColon >> registerBitsTypeRef) + <*> (tok TokColon >> registerBitsTypeRefP) ) -registerBitsTypeRef :: Pa RegisterBitsTypeRef -registerBitsTypeRef = do +registerBitsTypeRefP :: Pa RegisterBitsTypeRef +registerBitsTypeRefP = do base <- baseTypeRef - recur' <- recur + recur' <- recurP return (recur' base) where - recur :: P (RegisterBitsTypeRef Stage1 F A -> RegisterBitsTypeRef Stage1 F A) - recur = + recurP :: P (RegisterBitsTypeRef Stage1 F A -> RegisterBitsTypeRef Stage1 F A) + recurP = ( do withMeta $ do - expr <- tok TokLBracket *> expression <* tok TokRBracket - recur' <- recur + expr <- tok TokLBracket *> expressionP <* tok TokRBracket + recur' <- recurP return (\met base -> recur' (RegisterBitsArray base expr met)) ) <|> return id baseTypeRef = withMeta $ - (RegisterBitsJustBits <$> exprInParen) - <|> (RegisterBitsAnonymousType (Witness ()) <$> anonymousBitsType) + (RegisterBitsJustBits <$> exprInParenP) + <|> (RegisterBitsAnonymousType (Witness ()) <$> anonymousBitsTypeP) <|> (RegisterBitsReference <$> name) -anonymousBitsType :: Pa AnonymousBitsType -anonymousBitsType = withMeta $ do +anonymousBitsTypeP :: Pa AnonymousBitsType +anonymousBitsTypeP = withMeta $ do tok KWEnum - AnonymousEnumBody <$> exprInParen <*> defer body enumBody + AnonymousEnumBody <$> exprInParenP <*> defer body enumBodyP -bitType :: Pa BitType -bitType = withMeta $ rawBits <|> enumType +bitTypeP :: Pa BitType +bitTypeP = withMeta $ rawBits <|> enumType where - rawBits = RawBits <$> (tok TokLParen *> expression <* tok TokRParen) + rawBits = RawBits <$> (tok TokLParen *> expressionP <* tok TokRParen) enumType = do tok KWEnum - expr <- exprInParen - EnumBitType expr <$> defer body enumBody + expr <- exprInParenP + EnumBitType expr <$> defer body enumBodyP -enumBody :: Pa EnumBody -enumBody = +enumBodyP :: Pa EnumBody +enumBodyP = withMeta $ - EnumBody <$> many (directed enumConstantDecl <* tok TokComma) + EnumBody <$> many (directedP enumConstantDeclP <* tok TokComma) -enumConstantDecl :: Pa EnumConstantDecl -enumConstantDecl = +enumConstantDeclP :: Pa EnumConstantDecl +enumConstantDeclP = withMeta $ - (tok KWReserved >> EnumConstantReserved <$> (tok TokEq >> expression)) - <|> (EnumConstantDecl <$> ident <*> (tok TokEq >> expression)) + (tok KWReserved >> EnumConstantReserved <$> (tok TokEq >> expressionP)) + <|> (EnumConstantDecl <$> ident <*> (tok TokEq >> expressionP)) -expression :: Pa Expression -expression = withMeta $ +expressionP :: Pa Expression +expressionP = withMeta $ token $ \case (TokLitNum num) -> Just (LitNum num) (TokIdent i) -> Just $ @@ -373,13 +357,13 @@ defer p0 pb = do (sourceName sourcePos) <$> p0 -packageBody :: Pa PackageBody -packageBody = +packageBodyP :: Pa PackageBody +packageBodyP = withMeta $ PackageBody <$> many - ( directed $ - fiddleDecl + ( directedP $ + fiddleDeclP <* ( tok TokSemi <|> fail "Expected ';'" ) ) @@ -409,7 +393,7 @@ name = withMeta $ do -- and after and sets the positions and adds it to the annotation. withMeta :: P (Commented SourceSpan -> b) -> P b withMeta p = do - comments <- many comment + comments <- many commentP start <- getPosition fn <- p end <- getPosition @@ -431,7 +415,7 @@ tokKeepComment t' = do tok :: T -> P (Token SourceSpan) tok t' = do - many comment + many commentP Text.Parsec.token (\(Token t _) -> show t) (\(Token _ (SourceSpan s1 _)) -> s1) diff --git a/src/Main.hs b/src/Main.hs index f92d6c6..6fba502 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -1,17 +1,21 @@ module Main where import Control.Monad (forM_) +import Control.Monad.Identity (Identity) import Control.Monad.Writer -import Data.Aeson (encode) +import Data.Aeson (Value (Null), encode) import qualified Data.ByteString.Lazy.Char8 as BL +import Data.Data (cast) +import qualified Data.Text as Text import qualified Data.Text.IO +import Data.Typeable import GHC.IO.Exception (ExitCode (ExitFailure, ExitSuccess)) import Language.Fiddle.Ast import Language.Fiddle.Compiler (coloredFormat, compile_, printDiagnostic) import Language.Fiddle.Compiler.Stage0 import Language.Fiddle.Compiler.Stage1 import Language.Fiddle.Compiler.Stage2 -import Language.Fiddle.GenericTree (ToGenericSyntaxTree (toGenericSyntaxTree)) +import Language.Fiddle.GenericTree (GenericSyntaxTree (..), ToGenericSyntaxTree (toGenericSyntaxTree), alterGenericSyntaxTree) import qualified Language.Fiddle.Parser import qualified Language.Fiddle.Tokenizer import qualified System.Environment as System @@ -25,10 +29,17 @@ main = do [filePath] -> do text <- Data.Text.IO.readFile filePath let (diags, ma) = compile_ $ toStage3 =<< toStage2 =<< toStage1 =<< toStage0 filePath text - ec <- + ec <- case ma of Just ast -> do - putStrLn $ BL.unpack $ encode $ toGenericSyntaxTree ast + putStrLn $ + BL.unpack $ + encode $ + alterGenericSyntaxTree cleanupIdentifiers $ + toGenericSyntaxTree $ + fmap + (const (Nothing :: Maybe Value)) + ast return ExitSuccess Nothing -> do putStrLn "\x1b[1;31mCompilation Failed\x1b[0m" @@ -39,3 +50,15 @@ main = do _ -> do putStrLn "Wrong Args" exitWith (ExitFailure 2) + +cleanupIdentifiers :: GenericSyntaxTree Identity a -> Maybe (GenericSyntaxTree Identity a) +cleanupIdentifiers (SyntaxTreeObject _ _ _ tr) + | (Just (Identifier n _)) <- castT tr = + Just $ SyntaxTreeValue (Text.unpack n) + where + castT :: + (Typeable t, Typeable f, Typeable a, Typeable t') => + t f a -> + Maybe (t' f a) + castT = cast +cleanupIdentifiers _ = Nothing |