Permalink
Browse files

Starts reworking to a new internal design.

A new design for the internal of the generator (and especially RawSpec)
is made (see NewDesign.txt). This is an initial update of the
generator.
  • Loading branch information...
1 parent 7c912e9 commit 2120fea18317afb6c539c8eea73c6427f233d728 @Laar committed Dec 22, 2012
Showing with 141 additions and 155 deletions.
  1. +49 −0 NewDesign.txt
  2. +45 −56 src/Spec/Parsing.hs
  3. +47 −99 src/Spec/RawSpec.hs
View
@@ -0,0 +1,49 @@
+The original design of the generator is quite complex, especially the
+processing of the spec is a hard task (partially caused by the input
+files). The intermediate representation makes the processing rules
+quite a bit harder. Therefore a new design was made where the
+intermediate representation is simpler and the processing simpler.
+
+
+Old design:
+Per category a map from entity (function of enum) to it's value. There
+are several steps in the process
+- Remove duplicate definitions (and replace all but one by reuses).
+- Filtering empty categories.
+- Sorting values to get the imports right (no circular dependencies).
+- Ensuring there is a definition for entities that are reused from a
+ deleted category.
+- Adding reuses from a different file.
+As you can see most of the functions are there to get the right
+definition or reuse value in the right category. After all things have
+been sorted the module generating is quite simple as the spec specifies
+exactly what the module should contain (including if it's a reuse or a
+definition).
+
+The problem is that the processing steps are too complex. This is
+caused by a bad design of the intermediate datatype (RawSpec).
+
+New design:
+Split the definitions of the entities and what entities are used in a
+module. Thus the RawSpec contains two maps per entity type, one
+defining all the values and one defining what values (by name) are used
+in each category.
+
+This directly eliminates all trouble with keeping the right type of
+definition/reuse in each category as a category now only keeps track of
+names. Most of the processing steps look quite simple now,
+- Removing duplicates is no longer necessary.
+- Empty categories are no longer added (as the parsing step is far
+ simpler).
+- Sorting values for imports is no longer needed (see next paragraph).
+- There are always definitions availible.
+- Adding reuses is quite simple.
+
+However this change results in that the module generator no longer gets
+information on whether an entity should be defined or reexported (it
+only has names to work on). This is tackeled by letting the generator
+keep a mapping of defined entities and there location. When a module
+should contain an entity the generator looks it up, when it already
+exists a reexport is added else a new definition is outputed and the
+entity is added. By using this solution the import problem is also
+solved as imports can only be done of already generated modules.
View
@@ -21,25 +21,27 @@ module Spec.Parsing (
-----------------------------------------------------------------------------
-import Control.Arrow((***))
-import Data.List
+import Control.Monad.State
+import Control.Monad.Writer
+
import qualified Data.Map as M
-import Data.Maybe
-import Data.Monoid
-import Spec.RawSpec
import Language.Haskell.Exts.Syntax
import Code.Generating.Utils
import Control.Applicative
-import Text.ParserCombinators.Parsec hiding
+
+import Text.Parsec.String(GenParser)
+import Text.Parsec hiding
(many, optional, (<|>), token)
import Text.OpenGL.Api as A
import Text.OpenGL.Spec as S hiding (Value)
import qualified Text.OpenGL.Spec as S
+import Spec.RawSpec
+
-----------------------------------------------------------------------------
-- | Parse the needed OpenGL spec files and generate a 'RawSpec' based on
@@ -54,71 +56,58 @@ parseSpecs esf gsf tmf = do
fspecf <- readFile gsf
tymapf <- readFile tmf
return $ do -- (Either ParseError) monad
- espec <- enumLines especf >>= parseEnumSpec
+ espec <- enumLines especf >>= return . parseEnumSpec
tymap <- tmLines tymapf >>= return . mkTypeMap
fspec <- funLines fspecf >>= return . extractFunctions
let fspec' = parseFSpec fspec tymap
return $ mappend espec fspec' --mkRawSpec espec fspec'
-----------------------------------------------------------------------------
+
-- | Parse the enumeration spec.
-parseEnumSpec :: [EnumLine] -> Either ParseError RawSpec -- [(Category, [(String, EnumValue)])]
-parseEnumSpec ls =
- let ls' = filter inputFilter ls
- in parse pEnumSpec "EnumLines" ls' -- >>= return . nubSpec
-
-type EP = GenParser EnumLine ()
-
-inputFilter :: EnumLine -> Bool
-inputFilter (Start _ _) = True
-inputFilter (Enum _ _ _) = True
-inputFilter (Use _ _) = True
-inputFilter _ = False
-
-pEnumSpec :: EP RawSpec -- [(Category, [(String, EnumValue)])]
-pEnumSpec = mconcat <$> many pCategory'
-
--- | Parse a category header and the enums that come with it.
-pCategory' :: EP RawSpec
-pCategory' = do
- cat <- pCategoryHeader
- vals <- many pGLValue
- return $ categoryRawSpec cat vals
-
-pCategoryHeader :: EP Category
-pCategoryHeader = tokenPrim showValue nextPos testValue
- where showValue = show
- testValue (Start cat _) = Just cat
- testValue _ = Nothing
- nextPos sp _ _ = incSourceColumn sp 1
-
-pGLValue :: EP (EnumName, EnumValue)
-pGLValue = tokenPrim showValue nextPos testValue
+parseEnumSpec :: [EnumLine] -> RawSpec
+parseEnumSpec eLines =
+ let specWriter = evalStateT (parseEnumLines eLines)
+ (error $ "No category")
+ endoSpec = execWriter specWriter
+ in appEndo endoSpec mempty
+
+type EP = StateT Category (Writer (Endo RawSpec))
+
+parseEnumLines :: [EnumLine] -> EP ()
+parseEnumLines = sequence_ . map parseEnumLine
+
+parseEnumLine :: EnumLine -> EP ()
+parseEnumLine (Start cat _) = put cat
+parseEnumLine (Enum n v _) = addEnumValue n v >> addEnumLocation n
+parseEnumLine (Use _ n) = addEnumLocation n
+parseEnumLine _ = return ()
+
+addEnumLocation :: String -> EP ()
+addEnumLocation name = do
+ cat <- get
+ tell . Endo $ addLocation cat (wrapName name :: EnumName)
+
+addEnumValue :: String -> S.Value -> EP ()
+addEnumValue name v = case v of
+ Identifier _ -> return ()
+ Hex i _ _ -> addValue' i
+ Deci i -> addValue' $ fromIntegral i
where
- showValue = show
--- TODO : try to find a better way of determining the valuetype of the enum
- -- name in the second doesn't need to be extension scrapped
- testValue (Enum name value _) = Just (wrapName name, partialValue value . valueType $ name)
- testValue (Use ucat name) = Just (wrapName name, Redirect ucat . valueType $ name)
- testValue _ = Nothing
- nextPos sp _ _ = incSourceColumn sp 1
- partialValue (Hex v _ _) = Value v
- partialValue (Deci i) = Value $ fromIntegral i
- partialValue (Identifier i) = ReUse (wrapName . fromMaybe i . stripPrefix "GL_" $ i)
- valueType name = if isBitfieldName name then tyCon' "GLbitfield" else tyCon' "GLenum"
+ addValue' :: Integer -> EP ()
+ addValue' i = tell . Endo $ addValue (wrapName name) (Value i ty)
+ ty :: Type
+ ty = if isBitfieldName name then tyCon' "GLbitfield" else tyCon' "GLenum"
-----------------------------------------------------------------------------
-- | Parse (or process) the function as the are supplied by the openGL-api
-- package.
parseFSpec :: [Function] -> TypeMap -> RawSpec
-parseFSpec funcs tm =
--- map (\x -> (fst $ head x, map snd x)) -- pull out the categories
--- . groupBy ((==) `on` fst) -- group them by category
--- $ map (convertFunc tm) funcs
- mconcat . map (uncurry categoryRawSpec . (id *** pure))
- $ map (convertFunc tm) funcs
+parseFSpec funcs tm = foldr add mempty $ map (convertFunc tm) funcs
+ where
+ add (c, (fn, fv)) rs = addValue fn fv . addLocation c fn $ rs
convertFunc :: TypeMap -> Function -> (Category, (FuncName, FuncValue))
convertFunc tm rf = (funCategory rf, (wrapName name, RawFunc name ty alias))
Oops, something went wrong.

0 comments on commit 2120fea

Please sign in to comment.