-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathParse.hs
116 lines (92 loc) · 3.71 KB
/
Parse.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
module Parse where
import qualified Data.Map as M
import Control.Applicative
import Control.Monad
import Data.Char
import Data.List
import Data.Maybe
import Text.Parsec hiding ((<|>), many, State)
import Text.Parsec.Expr
import Types
parseInput :: String -> String -> Parsec String () a -> ErrorM a
parseInput srcName input parser = case parse (parseWholeInput parser) srcName input of
Left err_ -> err ("Syntax error " ++ show err_)
Right expr -> pure expr
parseReplCmd = whitespace *> (parseLoad <|> (RStmt <$> parseStmt)) <* whitespace
parseLoad = RLoad <$> (tryString "load" /> some (satisfy (not . isSpace)))
parseStmts = anyWhitespace *> many (parseStmt <* anyWhitespace)
parseStmt :: Parsec String () Stmt
parseStmt = parseUnitDef <|> try parseDef <|> (SExpr <$> parseExpr)
parseUnitDef = do
utype <- (tryString "si-unit" *> pure USI) <|>
(tryString "bin-unit" *> pure UBin) <|>
(tryString "unit" *> pure UNormal)
names <- whitespace *> sepBy1 identifier (char '/')
whitespace
abbrs <- option [] $ try (char '(' /> sepBy1 identifier (char '/') </ char ')')
whitespace
value <- option Nothing $ Just <$> (char '=' /> parseExpr <* whitespace)
pure $ SUnitDef utype names abbrs value
parseDef = SDef <$> identifier <*> (whitespace *> char '=' /> parseExpr)
parseExpr :: Parsec String () Expr
parseExpr = buildExpressionParser opTableWithSpaces parseExpr'
parseExpr' = try parseApply <|> parseFn <|> parseExpr''
parseExpr'' = buildExpressionParser opTableNoSpaces parseSingleTokenExpr
parseSingleTokenExpr = parsePrefixOp <|> parseParens <|> parseNum <|> parseId
parseParens = char '(' /> parseExpr </ char ')'
parseId = EId <$> identifier
parseNum = ENum <$> (float <|> fromIntegral <$> integer) <*> pure M.empty
parseApply = EApply <$> parseExpr'' <*> some (try $ whitespace *> parseExpr'')
parsePrefixOp = EApply <$> (EId <$> prefixOperator) <*> ((:[]) <$> parseSingleTokenExpr)
parseFn = do
args <- tryString "\\" /> sepBy1 identifier (char ',' *> whitespace) <* whitespace
body <- tryString "->" /> parseExpr
pure $ EFn args body
operatorChars = "/<>?:\\|~!@#$%^&*+-="
reservedOps = ["#", "\\", "->"]
keywords = ["unit", "si-unit", "bin-unit", "exit", "load"]
--- Operators
ops = [
[("^", AssocRight)],
[("*", AssocLeft), ("/", AssocLeft)],
[("+", AssocLeft), ("-", AssocLeft)],
[("@", AssocLeft)]
]
opTableWithSpaces = map (map $ op True) ops
opTableNoSpaces = map (map $ op False) ops
op reqSpaces (str, assoc) = binop str reqSpaces assoc
binop str reqSpaces = Infix (try $ do
when reqSpaces someWhitespace
name <- operator str False
when reqSpaces someWhitespace
pure (\a b -> EApply (EId name) [a, b])
)
operator str rassoc = (do
str <- tryString str
if rassoc /= (last str == ':') || str `elem` reservedOps then mzero
else pure str
) <?> "operator"
prefixOperator = (do
val <- some $ satisfy (`elem` operatorChars)
if val `elem` reservedOps then mzero else pure val) <?> "operator"
--- Lower-level combinators
tryString = try . string
parseWholeInput parser = parser <* eof
infixl 4 </>, />, </
a /> b = a *> whitespace *> b
a </ b = a <* whitespace <* b
a </> b = a <*> (whitespace *> b)
whitespace = whitespace' " \t"
anyWhitespace = whitespace' " \t\n"
someWhitespace = whitespace1' " \t"
whitespace' chars = skipMany (void (oneOf chars) <|> comment)
whitespace1' chars = skipMany1 (void (oneOf chars) <|> comment)
comment = char '#' *> skipMany (noneOf "\n")
integer' = catMaybes <$> some ((Just <$> digit) <|> (char '_' *> pure Nothing))
integer = read <$> integer'
float = try $ do
a <- option "0" integer'
char '.'
b <- integer'
pure $ read (a ++ "." ++ b)
identifier = (:) <$> letter <*> many alphaNum