File: ConvertSpec.hs

package info (click to toggle)
hugs98 98.200311-4
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 12,964 kB
  • ctags: 8,084
  • sloc: ansic: 67,521; haskell: 61,497; xml: 4,566; sh: 3,264; cpp: 1,936; yacc: 1,094; makefile: 915; cs: 883; sed: 10
file content (556 lines) | stat: -rw-r--r-- 19,963 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
module Main ( main ) where

import Control.Monad      ( liftM, when )
import Data.Char          ( isSpace )
import Data.FiniteMap     ( FiniteMap, emptyFM, addListToFM_C, elemFM, fmToList,
                            lookupWithDefaultFM )
import Data.List          ( isPrefixOf, tails, delete )
import System.Environment ( getArgs )
import Text.ParserCombinators.Parsec
                          ( SourceName, Parser, parse, try, eof, oneOf, noneOf,
                            string, (<|>), (<?>), option, skipMany, many, many1,
                            sepBy, between, chainl1 )

--------------------------------------------------------------------------------
-- Preprocessing of spec files, making it more amenable to "real" parsing
--------------------------------------------------------------------------------

preprocess :: String -> String
preprocess = unlines .
             addSeparators . mangleColonLines .
             removeTrailingWhitespace . removePassthru . removeComments .
             lines

   where removeComments = map $ takeWhile (/= '#')
         removePassthru = map $ \l -> if "passthru:" `isPrefixOf` l then "" else l
         removeTrailingWhitespace = map $ reverse . dropWhile isSpace . reverse
         mangleColonLines = map $ \l ->
            case break (== ':') l of
               (xs, ':':ys) | noSpaceIn xs -> ":" ++ xs ++ " " ++ ys ++ ";"
               _ -> l
         noSpaceIn = not . any (`elem` ['\t',' '])

         addSeparators = map addSeparator . tails

         addSeparator []                                  = []
         addSeparator xs@(l:ls) | startsWithTabbedLine xs = l ++ separatorFor ls
                                | otherwise               = l 

         separatorFor ls | startsWithTabbedLine (dropEmpty ls) = ","
                         | otherwise                           = ";"

         dropEmpty = dropWhile ((== 0) . length)

         startsWithTabbedLine (('\t':_):_) = True
         startsWithTabbedLine _            = False

--------------------------------------------------------------------------------
-- The function spec file's abstract syntax
--------------------------------------------------------------------------------

data Spec = Spec [PropertyName] [ValidProperty] [Category]

data ValidProperty = ValidProperty PropertyName PropertyValues

data PropertyValues =
     AnyValue
   | Values [PropertyValue]

data Category = Category (Maybe CategoryName) [FunctionDeclaration]

data FunctionDeclaration =
   FunctionDeclaration FunctionName [ParameterName] TypeName
                       [ParameterDeclaration] [FunctionProperty]

data ParameterDeclaration =
   ParameterDeclaration ParameterName ParameterType
                        (Maybe LengthDescriptor) [PropertyValue]

data ParameterType = ParameterType TypeName Direction TransferType

data Direction = In | Out | InOut

data TransferType = Array | Reference | Value

data LengthDescriptor = LengthDescriptor [IndexExpression]

data IndexExpression =
     Add IndexExpression IndexExpression
   | Sub IndexExpression IndexExpression
   | Mul IndexExpression IndexExpression
   | Div IndexExpression IndexExpression
   | Number Integer
   | Parameter ParameterName
   | CompSize [ParameterName]

data FunctionProperty = FunctionProperty PropertyName [MetaPropertyValue]

data MetaPropertyValue =
     AddAllPropertyValues
   | RemoveAllPropertyValues
   | AddPropertyValue PropertyValue
   | RemovePropertyValue PropertyValue
   deriving Eq

newtype PropertyValue = PropertyValue String   deriving Eq

newtype PropertyName  = PropertyName  String   deriving (Eq, Ord)
newtype CategoryName  = CategoryName  String
newtype FunctionName  = FunctionName  String
newtype TypeName      = TypeName      String
newtype ParameterName = ParameterName String

--------------------------------------------------------------------------------
-- Show instances for abstract syntax
--------------------------------------------------------------------------------

instance Show Spec where
   showsPrec _ (Spec reqProps validProps categories) =
      punctuate (showString "\n\n")
                (vcat (showReq reqProps ++ map shows validProps) :
                 map shows categories)
      where showReq [] = []
            showReq ps = [ hsep (showString "required-props:" : map shows ps) ]

instance Show ValidProperty where
   showsPrec _ (ValidProperty name values) =
      shows name . showChar ':' . showChar ' ' . shows values

instance Show PropertyValues where
   showsPrec _ AnyValue    = showChar '*'
   showsPrec _ (Values vs) = hsep (map shows vs)

instance Show Category where
   showsPrec _ (Category Nothing     decls) = vcat (map shows decls)
   showsPrec _ (Category (Just name) decls) =
      vcat [ showString "newcategory: " . shows name,
             vcat (map shows decls),
             showString "endcategory:" ]

instance Show FunctionDeclaration where
   showsPrec _ (FunctionDeclaration name params retType paramDecls props) =
      vcat ([ shows name . parens (punctuate (showString ", ") 
                                             (map shows params)),
              showString "\treturn " . shows retType ] ++
            map shows paramDecls ++
            map shows props)

instance Show ParameterDeclaration where
   showsPrec _ (ParameterDeclaration name typ mbLen vals) =
      hsep [ showString "\tparam", shows name, shows typ, showLen mbLen,
             hsep (map shows vals) ]
      where showLen Nothing   = showString ""
            showLen (Just ld) = brackets (shows ld)

instance Show ParameterType where
   showsPrec _ (ParameterType name dir trans) =
      hsep [ shows name, shows dir, shows trans ]

instance Show Direction where
   showsPrec _ In    = showString "in"
   showsPrec _ Out   = showString "out"
   showsPrec _ InOut = showString "in/out"

instance Show TransferType where
   showsPrec _ Array     = showString "array"
   showsPrec _ Reference = showString "reference"
   showsPrec _ Value     = showString "value"

instance Show LengthDescriptor where
   showsPrec _ (LengthDescriptor exprs) =
      punctuate (showString ", ") (map shows exprs)

instance Show IndexExpression where
    showsPrec _ (Add l r)        = parens (shows l . showString " + " . shows r)
    showsPrec _ (Sub l r)        = parens (shows l . showString " - " . shows r)
    showsPrec _ (Mul l r)        = parens (shows l . showString " * " . shows r)
    showsPrec _ (Div l r)        = parens (shows l . showString " / " . shows r)
    showsPrec _ (Number n)       = shows n
    showsPrec _ (Parameter p)    = shows p
    showsPrec _ (CompSize names) =
       showString "COMPSIZE" .
       parens (punctuate (showChar '/') (map shows names))

instance Show FunctionProperty where
   showsPrec _ (FunctionProperty name metaProps) =
      showChar '\t' . hsep (shows name : map shows metaProps)

instance Show MetaPropertyValue where
    showsPrec _ AddAllPropertyValues    = showString "all"
    showsPrec _ RemoveAllPropertyValues = showString "! all"
    showsPrec _ (AddPropertyValue v)    = shows v
    showsPrec _ (RemovePropertyValue v) = showString "! " . shows v

instance Show PropertyValue where
   showsPrec _ (PropertyValue v) = showString v

instance Show PropertyName where
   showsPrec _ (PropertyName n) = showString n

instance Show CategoryName where
   showsPrec _ (CategoryName n) = showString n

instance Show FunctionName where
   showsPrec _ (FunctionName n) = showString n

instance Show TypeName where
   showsPrec _ (TypeName n) = showString n

instance Show ParameterName where
   showsPrec _ (ParameterName n) = showString n

--------------------------------------------------------------------------------
-- Helper functions for Show instances
--------------------------------------------------------------------------------

hsep :: [ShowS] -> ShowS
hsep = punctuate (showChar ' ')

vcat :: [ShowS] -> ShowS
vcat = punctuate (showChar '\n')

punctuate :: ShowS -> [ShowS] -> ShowS
punctuate _ [] = id
punctuate p xs = foldr1 (\l r -> l . p . r) xs

parens :: ShowS -> ShowS
parens s = showChar '(' . s . showChar ')'

brackets :: ShowS -> ShowS
brackets s = showChar '[' . s . showChar ']'

--------------------------------------------------------------------------------
-- Parser for function spec files
--------------------------------------------------------------------------------

spec :: Parser Spec
spec = do
   reqProps   <- option [] requiredProperties
   validProps <- many validProperty
   categories <- many category
   spaces
   eof
   return $ Spec reqProps validProps categories

requiredProperties :: Parser [PropertyName]
requiredProperties =
   between (symbol ":required-props") semi (many propertyName)

validProperty :: Parser ValidProperty
validProperty = do
   symbol ":"
   name   <- validPropertyName
   values <- validPropertyValues
   semi
   return $ ValidProperty name values

validPropertyName :: Parser PropertyName
validPropertyName =
       (do symbol "param"; return $ PropertyName "param")
   <|> propertyName

validPropertyValues :: Parser PropertyValues
validPropertyValues =
   option (Values []) (    (do symbol "*"; return AnyValue)
                       <|> liftM Values (many1 propertyValue))

category :: Parser Category
category = do
       (do decl <- functionDeclaration
           return $ Category Nothing [decl])
   <|> do cat <- newCategory
          funcDecls <- many functionDeclaration
          endCategory
          return $ Category (Just cat) funcDecls

newCategory :: Parser CategoryName
newCategory =
   between (symbol ":newcategory") semi categoryName

endCategory :: Parser ()
endCategory = do
   symbol ":endcategory"
   semi

functionDeclaration :: Parser FunctionDeclaration
functionDeclaration = do
  name <- functionName
  params <- parameters
  retType <- returnType
  (paramDecls, props) <- option ([], []) (do comma; paramsAndProps)
  semi
  return $ FunctionDeclaration name params retType paramDecls props

parameters :: Parser [ParameterName]
parameters = inParens (parameterName `sepBy` comma)

returnType :: Parser TypeName
returnType = do
   symbol "return"
   typeName

paramsAndProps :: Parser ([ParameterDeclaration],[FunctionProperty])
paramsAndProps = do
       (do param <- parameterDeclaration
           (params, props) <- option ([], []) (do comma; paramsAndProps)
           return (param:params, props))
   <|> (do props <- functionProperty `sepBy` comma
           return ([], props))

parameterDeclaration :: Parser ParameterDeclaration
parameterDeclaration = do
   symbol "param"
   name <- parameterName
   typ  <- parameterType
   len  <- option Nothing (liftM Just lengthDescriptor)
   vals <- many propertyValue
   return $ ParameterDeclaration name typ len vals

parameterType :: Parser ParameterType
parameterType = do
    name  <- typeName
    dir   <- direction
    trans <- transferType
    return $ ParameterType name dir trans

direction :: Parser Direction
direction =
       (do symbol "in";     return In   )
   <|> (do symbol "out";    return Out  )
   <|> (do symbol "in/out"; return InOut)

transferType :: Parser TransferType
transferType =
       (do symbol "array";     return Array    )
   <|> (do symbol "reference"; return Reference)
   <|> (do symbol "value";     return Value    )

lengthDescriptor :: Parser LengthDescriptor
lengthDescriptor =
   inBrackets (liftM LengthDescriptor (indexExpression `sepBy` comma))

indexExpression :: Parser IndexExpression
indexExpression = term `chainl1` addOp

addOp :: Parser (IndexExpression -> IndexExpression -> IndexExpression)
addOp =
       (do symbol "+"; return Add)
   <|> (do symbol "-"; return Sub)

term :: Parser IndexExpression
term = factor `chainl1` mulOp

mulOp :: Parser (IndexExpression -> IndexExpression -> IndexExpression)
mulOp =
       (do symbol "*"; return Mul)
   <|> (do symbol "/"; return Div)

factor :: Parser IndexExpression
factor =
       try compsize
   <|> inParens indexExpression
   <|> liftM Number integer
   <|> liftM Parameter parameterName

compsize :: Parser IndexExpression
compsize = do
   symbol "COMPSIZE"
   inParens (liftM CompSize (parameterName `sepBy` symbol "/"))

integer :: Parser Integer
integer = read `liftM` do spaces; many1 (oneOf "0123456789")

functionProperty :: Parser FunctionProperty
functionProperty = do
   name <- propertyName
   metaProps <- many metaPropertyValue
   return $ FunctionProperty name metaProps

metaPropertyValue :: Parser MetaPropertyValue
metaPropertyValue = do
   remove <- option False (do symbol "!"; return True)
   (    (do symbol "all"
            return $ if remove then RemoveAllPropertyValues
                               else AddAllPropertyValues)
    <|> liftM (if remove then RemovePropertyValue else AddPropertyValue)
              propertyValue)

propertyValue :: Parser PropertyValue
propertyValue = liftM PropertyValue word <?> "property value"

propertyName :: Parser PropertyName
propertyName = liftM PropertyName word <?> "property name"

categoryName :: Parser CategoryName
categoryName = liftM CategoryName word <?> "category name"

functionName :: Parser FunctionName
functionName = liftM FunctionName word <?> "function name"

typeName :: Parser TypeName
typeName = liftM TypeName word <?> "type name"

parameterName :: Parser ParameterName
parameterName = liftM ParameterName word <?> "parameter name"

word :: Parser String
word = try $ do
   spaces
   many1 (noneOf wordTerminators)

wordTerminators :: String
wordTerminators = spaceChars ++ specialChars

symbol :: String -> Parser ()
symbol s = try (do spaces; string s; return ()) <?> show s

inParens :: Parser a -> Parser a
inParens = between (symbol "(") (symbol ")")

inBrackets :: Parser a -> Parser a
inBrackets = between (symbol "[") (symbol "]")

semi :: Parser ()
semi = symbol ";"

comma :: Parser ()
comma = symbol ","

spaces :: Parser ()
spaces = skipMany (oneOf spaceChars) <?> "white space"

spaceChars :: [Char]
spaceChars = " \t\n\r\f\v\xa0"

specialChars :: [Char]
specialChars = "()[]:,;+*/!"

parseSpec :: SourceName -> String -> Spec
parseSpec fileName content =
   case parse spec fileName content of
      Left err -> error ("parse error at " ++ show err)
      Right s  -> s

--------------------------------------------------------------------------------
-- Calculate a mapping from property names to their corresponding domains,
-- doing checks for duplicate names/values on the way...
--------------------------------------------------------------------------------

type PropertyEnvironment = FiniteMap PropertyName PropertyValues

lookupProperty :: PropertyEnvironment -> PropertyName -> PropertyValues
lookupProperty env name =
   lookupWithDefaultFM env (error ("unknow property '" ++ show name ++ "'")) name

buildPropertyEnvironment :: Spec -> PropertyEnvironment
buildPropertyEnvironment spec_ =
   case noDupReqProps . noDupPropNames . noDupPropValues $ spec_ of
      Spec _ validProps _ ->
         addListToFM_C (\old _ -> error ("duplicate property name '"++ show old ++ "'"))
                       emptyFM
                       [(name,values) | ValidProperty name values <- validProps]

noDupReqProps :: Spec -> Spec
noDupReqProps spec_@(Spec reqProps _ _) =
   noDups spec_ "required property" reqProps

noDupPropNames :: Spec -> Spec
noDupPropNames spec_@(Spec _ validProps _) =
   noDups spec_ "property name" [ name | ValidProperty name _ <- validProps ]

noDupPropValues :: Spec -> Spec
noDupPropValues spec_@(Spec _ validProps _) =
   foldl (\spc (ValidProperty name values) ->
              noDups spc ("property value for '" ++ show name ++ "'")
                     [ v | Values vs <- [values], v <- vs ])
         spec_
         validProps

-- Simply return retVal if there are no duplicates in xs, otherwise complain.
noDups :: (Show b, Eq b) => a -> String -> [b] -> a
noDups retVal what xs = check xs
   where check []                   = retVal
         check (y:ys) | y `elem` ys = error ("duplicate "++what++": '"++ show y ++ "'")
                      | otherwise   = check ys

--------------------------------------------------------------------------------
-- Expand MetaPropertyValues so that only AddPropertyValues are left, stealthily
-- throwing away (required) property declarations and category names on the way.
-- Checks for required properties are done here, too.
--------------------------------------------------------------------------------

expandMetaProperties :: PropertyEnvironment -> Spec -> [FunctionDeclaration]
expandMetaProperties env (Spec reqProps _ categories) =
   checkRequiredPropsDecl env reqProps .
   map (checkRequiredPropsUse reqProps) $
   [ expandMetaPropertiesFuncDecl env funcDecl
   | Category _ funcDecls <- categories
   , funcDecl <- funcDecls ]

expandMetaPropertiesFuncDecl :: PropertyEnvironment
                             -> FunctionDeclaration -> FunctionDeclaration
expandMetaPropertiesFuncDecl env (FunctionDeclaration name params retType parmDecls props) =
   FunctionDeclaration
      name params retType parmDecls
      [ FunctionProperty pName (expandMetaPropertyValues values metaProps)
      | FunctionProperty pName metaProps <- props
      , let values = lookupProperty env pName ]

expandMetaPropertyValues :: PropertyValues -> [MetaPropertyValue] -> [MetaPropertyValue]
expandMetaPropertyValues values = foldl (go values) []
   where go AnyValue    _ AddAllPropertyValues = error "can't use all with *"
         go (Values vs) _ AddAllPropertyValues = map AddPropertyValue vs
         go _ _    RemoveAllPropertyValues     = []
         go _ accu v@(AddPropertyValue _)      = v : accu
         go _ accu (RemovePropertyValue v)     = delete (AddPropertyValue v) accu

checkRequiredPropsDecl :: PropertyEnvironment -> [PropertyName] -> a -> a
checkRequiredPropsDecl env reqProps retVal =
   case [ reqProp | reqProp <- reqProps, not (reqProp `elemFM` env) ] of
      []    -> retVal
      (p:_) -> error ("unknown required property '" ++ show p ++ "'")

checkRequiredPropsUse :: [PropertyName] -> FunctionDeclaration -> FunctionDeclaration
checkRequiredPropsUse reqProps f@(FunctionDeclaration funcName _ _ _ props) =
    case [ reqProp | reqProp <- reqProps, not (reqProp `elem` usedProps) ] of
       []    -> f
       (p:_) -> error ("function '" ++ show funcName ++
                       "' does not use required property '" ++ show p ++ "'")
    where usedProps = [ propName | FunctionProperty propName _ <- props ]

--------------------------------------------------------------------------------
-- The driver
--------------------------------------------------------------------------------

parseArguments :: [String] -> (Bool, String, IO String)
parseArguments args =
   case restArgs of
      []   -> (verbose, "<stdin>", getContents)
      [fn] -> (verbose, fn, readFile fn)
      _    -> error "usage: ConvertSpec [-v] [input.spec]"
   where (verbose, restArgs) = case args of
                                  ("-v":rest) -> (True,  rest)
                                  rest        -> (False, rest)

execute :: Bool -> String -> (b -> String) -> (a -> b) -> a -> IO b
execute verbose header showFn f x = do
   let result = f x
   when verbose $ do
      putStrLn ("-- " ++ header ++ "----------------------------------------")
      putStrLn (showFn result)
   return result

-- TODO: Ugly!
mainWithArgs :: [String] -> IO ()
mainWithArgs args = do
   let (verbose, fileName, getInput) = parseArguments args
       exec = execute verbose
   input        <- getInput
   preprocInput <- exec "preprocessing" id preprocess input
   spec_        <- exec "parsing" show (parseSpec fileName) preprocInput
   propEnv      <- exec "building property environment" (unlines . map show . fmToList) buildPropertyEnvironment spec_
   expandedSpec <- exec "expanding properties" (unlines . map show) (expandMetaProperties propEnv) spec_
   return ()

main :: IO ()
main = getArgs >>= mainWithArgs