-
Notifications
You must be signed in to change notification settings - Fork 0
/
Main.hs
142 lines (129 loc) · 4.52 KB
/
Main.hs
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
{-# LANGUAGE FlexibleContexts, OverloadedStrings, ExistentialQuantification #-}
module Main where
import Lexer
import Orphans()
import Parser.Recursive
import Parser.LL
import Parser.LR
import System.FilePath
import System.Directory
import Data.Text.Encoding
import qualified Data.ByteString as BS
import Data.Proxy
import Control.Monad
import Data.List
import Data.Char
import Data.Bifunctor
import Data.Text (Text)
import qualified Data.Text as T
import Options.Applicative hiding (Parser)
import qualified Options.Applicative as OA
import Data.Version
import Paths_alpaca_parser_generator
type MainProgram = Bool -> Text -> FilePath -> FilePath -> IO ()
runProgram :: (LexerWriter lang, ParserWriter parser lang) =>
Proxy lang -> Proxy parser -> MainProgram
runProgram lang parserMethod debugLexer parserName baseFileName inputFile = do
input <- decodeUtf8 <$> BS.readFile inputFile
let (lexicRaw, grammarLines) = second (drop 1) . break (=="%%") $ T.lines $ T.filter (/='\r') input
rootdir = takeDirectory inputFile
grammar = T.unlines grammarLines
lexic = filter (not . T.null . T.strip) lexicRaw
setCurrentDirectory rootdir
runInIO $ do
writeFiles =<< makeLexer lang debugLexer lexic
unless (T.null grammar) $
wrap parserName $ makeParser lang parserMethod ParserOptions{
parserOptionsName = parserName
, parserOptionsBaseFileName = baseFileName
, parserOptionsGrammarDefinition = grammar
}
enumReader :: [([String], a)] -> ReadM a
enumReader tbl = eitherReader go
where go x | Just f <- find ((map toLower x `elem`) . fst) tbl
= Right $ snd f
go _ = Left $ "Invalid value, allowed values: "
<> intercalate ", " (concatMap fst tbl)
langTbl :: [([String], ParserProxy -> MainProgram)]
langTbl = [
(ncpp, \(ParserProxy p) -> runProgram cpp p)
, (ncs , \(ParserProxy p) -> runProgram csharp p)
, (npy , \(ParserProxy p) -> runProgram python p)
, (njs , \(ParserProxy p) -> runProgram js p)
]
where
ncpp = ["cpp", "c++"]
ncs = ["c#", "csharp", "cs"]
npy = ["python", "py"]
njs = ["js", "javascript"]
data ParserProxy = forall p.
( Parser p
, ParserWriter p 'CPP
, ParserWriter p 'CSharp
, ParserWriter p 'Python
, ParserWriter p 'JS
) => ParserProxy { unPP :: Proxy p }
parsTbl :: [([String], ParserProxy)]
parsTbl = [
(["recursive", "rec"], ParserProxy recursiveParser)
, (["ll1"] , ParserProxy llParser )
, (["lr0"] , ParserProxy lr0Parser )
, (["lr1"] , ParserProxy lr1Parser )
, (["slr"] , ParserProxy slrParser )
, (["lalr"] , ParserProxy lalrParser )
]
parser :: OA.Parser (IO ())
parser = (option (enumReader langTbl)
( short 'l'
<> long "lang"
<> help "Target language, default cpp"
<> metavar (intercalate "|" (concatMap fst langTbl))
<> value (\(ParserProxy p) -> runProgram cpp p))
<*> option (enumReader parsTbl)
( short 'p'
<> long "parser"
<> help "Parser method, default lalr"
<> metavar (intercalate "|" (concatMap fst parsTbl))
<> value (ParserProxy lalrParser)))
<*> switch
( long "debug-lexer"
<> help "Output lexer finite automata graphs in GraphViz format")
<*> strOption
( short 'n'
<> long "name"
<> help "Parser class name, default \"Parser\""
<> metavar "NAME"
<> value "Parser")
<*> strOption
( short 'b'
<> long "basename"
<> help "Parser output file base name, default \"parser\""
<> metavar "FILENAME"
<> value "parser")
<*> strArgument
( help "Grammar input file"
<> metavar "GRAMMARFILE" )
versionFlag :: OA.Parser (IO () -> IO ())
versionFlag = infoOption
( showVersion version )
( short 'v'
<> long "version"
<> help "Show version")
main :: IO ()
main = join $ execParser opts
where
opts = info (parser <**> helper <**> versionFlag)
( fullDesc
<> progDesc "ALPACA generates mostly human-readable, if somewhat\
\ inefficient, simple parsers in multiple target languages."
<> header "ALPACA - Anemic Lexer and PArser Creation Algorithm" )
wrap :: T.Text -> MyMonadT IO [(FilePath, Text)] -> MyMonadT IO ()
wrap n m =
censor (map (("Warning in "<>n<>": ")<>)) (writeFiles =<< m)
`catchError`
(tell . map (("Error in "<>n<>": ")<>))
writeFiles :: [(FilePath, Text)] -> MyMonadT IO ()
writeFiles = mapM_ (lift . uncurry writeFile')
where
writeFile' :: FilePath -> Text -> IO ()
writeFile' fp = BS.writeFile fp . encodeUtf8