Skip to content

Commit f94c4c0

Browse files
committed
Change syntax to C-style braces
Lines are no longer newline-sensitive; they are terminated with semicolon. Blocks now are within braces rather than having an end keyword terminator.
1 parent 653e890 commit f94c4c0

2 files changed

Lines changed: 73 additions & 82 deletions

File tree

src/Compiler/QbScript/Parser.hs

Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,18 @@ ident = alpha ++ ['0'..'9'] ++ "_"
2525
identChar :: Parser Char
2626
identChar = oneOf ident
2727

28-
spaceChar' :: Parser ()
29-
spaceChar' = void (char ' ' <|> char '\t')
30-
3128
opChar :: Parser Char
3229
opChar = oneOf ".:+-*/=<>&|^"
3330

3431
spaceConsumer :: Parser ()
35-
spaceConsumer = L.space spaceChar'
32+
spaceConsumer = L.space (() <$ spaceChar)
3633
(L.skipLineComment "//")
3734
(L.skipBlockComment "/*" "*/")
3835

39-
blockKeywords :: [String]
40-
blockKeywords = [ "if", "else", "elseif", "endif", "begin", "repeat", "script", "endscript"
41-
, "switch", "case", "default", "endswitch" ]
42-
43-
blockKeyword :: Parser String
44-
blockKeyword = choice $ fmap (try . symbol) blockKeywords
45-
4636
reservedWords :: [String]
47-
reservedWords = blockKeywords ++ [ "break", "random", "random2"
48-
, "randomrange", "randomrange2", "randompermute", "randomshuffle", "not"
37+
reservedWords = [ "if", "else", "elseif", "repeat", "script", "switch", "case"
38+
, "default", "break", "random", "random2", "randomrange"
39+
, "randomrange2", "randompermute", "randomshuffle", "not"
4940
, "useheap" ]
5041

5142
lexeme :: Parser a -> Parser a
@@ -129,9 +120,8 @@ passthrough = void (symbol "<...>")
129120
-- * Parser
130121

131122
qbScript :: Parser QbScript
132-
qbScript = QbScript <$> (rword "script" *> parens (optional struct) <* newline)
133-
<*> many instruction
134-
<* rword "endscript"
123+
qbScript = QbScript <$> (rword "script" *> parens (optional struct))
124+
<*> braces (many instruction)
135125

136126
-- * Literals
137127

@@ -165,12 +155,10 @@ qbKey = QbCrc <$> checksum
165155
<|> QbName <$> identifier
166156

167157
dict :: Parser Dict
168-
dict = braces $ optional newline *>
169-
(Dict <$> dict')
158+
dict = Dict <$> braces dict'
170159
where
171160
dict' :: Parser [(Maybe QbKey, Expr)]
172-
dict' = entry `sepBy` (comma <* optional newline)
173-
<* optional newline
161+
dict' = entry `sepBy` comma
174162

175163
entry :: Parser (Maybe QbKey, Expr)
176164
entry = do
@@ -179,15 +167,12 @@ dict = braces $ optional newline *>
179167
return (k,v)
180168

181169
array :: Parser Array
182-
array = Array <$> brackets (optional newline *> expr `sepBy` (comma <* optional newline)
183-
<* optional newline)
170+
array = Array <$> brackets (expr `sepBy` comma)
184171

185172
-- ** Structs
186173

187174
struct :: Parser Struct
188-
struct = Struct <$> braces (optional newline
189-
*> structItem `sepBy` try (semicolon <* optional newline <* notFollowedBy (symbol "}"))
190-
<* semicolon <* optional newline)
175+
struct = Struct <$> braces (structItem `endBy` semicolon)
191176

192177
structItem :: Parser StructItem
193178
structItem = do
@@ -231,66 +216,70 @@ qbValue QbTStringQs = QbStringQs <$> qbKey
231216

232217

233218
qbArray :: QbType -> Parser QbArray
234-
qbArray t = brackets $ QbArr t <$> qbValue t `sepBy` (comma <* optional newline)
219+
qbArray t = brackets $ QbArr t <$> qbValue t `sepBy` comma
235220

236221
-- * Instructions
237222

238-
lineTerm :: Parser ()
239-
lineTerm = newline <|> eof
240-
241223
instructions :: Parser [Instruction]
242224
instructions = many instruction
243225

244226
instruction :: Parser Instruction
245227
instruction = choice (fmap try
246-
[ Assign <$> name <*> (equals *> expr)
228+
[ Assign <$> name <*> (equals *> expr) <* semicolon
247229
, ifelse
248230
, repeat
249231
, switch
250-
, Break <$ rword "break"
251-
, Return <$> (rword "return" *> optional (parens argument <|> argument))
252-
, BareExpr <$> expr
253-
]) <* lineTerm
232+
, Break <$ rword "break" <* semicolon
233+
, Return <$> (rword "return" *> optional (parens argument <|> argument)) <* semicolon
234+
, BareExpr <$> expr <* semicolon
235+
])
254236

255237
ifelse :: Parser Instruction
256238
ifelse = IfElse <$> if'
257239
<*> many elseif
258240
<*> else'
259-
<* rword "endif"
260241

261242
if' :: Parser (Expr, [Instruction])
262-
if' = (,) <$> (rword "if" *> expr <* newline)
263-
<*> instructions
243+
if' = (,) <$> (rword "if" *> parenExpr)
244+
<*> braces instructions
264245

265246
elseif :: Parser (Expr, [Instruction])
266-
elseif = (,) <$> (rword "elseif" *> expr <* newline)
267-
<*> instructions
247+
elseif = (,) <$> (rword "elseif" *> parenExpr)
248+
<*> braces instructions
268249

269250
else' :: Parser [Instruction]
270-
else' = rword "else" *> newline *> instructions
251+
else' = (rword "else" *> braces instructions)
271252
<|> pure []
272253

273254
repeat :: Parser Instruction
274-
repeat = flip Repeat <$> between (rword "begin" <* newline) (rword "repeat") instructions
275-
<*> optional (parens expr)
255+
repeat = Repeat <$> (rword "repeat" *> optional parenExpr)
256+
<*> braces instructions
276257

277258
switch :: Parser Instruction
278-
switch = Switch <$> (rword "switch" *> expr <* newline)
279-
<*> many case'
280-
<*> (rword "default:" *> newline *> instructions <|> pure [])
281-
<* rword "endswitch"
282-
283-
case' :: Parser (SmallLit, [Instruction])
284-
case' = (,) <$> (rword "case" *> (smallLit <* colon <* newline))
285-
<*> instructions
259+
switch = do
260+
rword "switch"
261+
sw <- parenExpr
262+
(c, d) <- braces $ do
263+
c' <- many case'
264+
d' <- def
265+
pure (c', d')
266+
pure $ Switch sw c d
267+
where
268+
case' = (,) <$> (rword "case" *> smallLit <* colon)
269+
<*> instructions
270+
def = (rword "default" *> colon *> instructions)
271+
<|> pure []
286272

287273
-- * Expressions
288274
expr :: Parser Expr
289275
expr = makeExprParser term opTable
290276

277+
parenExpr :: Parser Expr
278+
parenExpr = Paren <$> parens expr
279+
291280
term :: Parser Expr
292281
term = choice (fmap try
293-
[ Paren <$> parens expr
282+
[ parenExpr
294283
, MethodCall <$> (name <* colon) <*> qbKey <*> parens (try argument `sepBy` comma)
295284
, BareCall <$> qbKey <*> parens (try argument `sepBy` comma)
296285
, ELit <$> lit

test/Compiler/QbScript/Parser/Tests.hs

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -112,57 +112,57 @@ instructionTests =
112112
describe "instruction" $ do
113113
it "can parse an assignment by name" $ property $
114114
\(Ident xs) -> fmap toLower xs `notElem` reservedWords
115-
==> parse instruction "" (fromString $ xs ++ " = 1.0") `shouldParse`
115+
==> parse instruction "" (fromString $ xs ++ " = 1.0;") `shouldParse`
116116
Assign (NonLocal $ QbName xs) (ELit (LitF 1))
117117
it "can parse an assignment by checksum" $ property $
118-
\c@(Checksum x) -> parse instruction "" (fromString $ "%$" ++ show c ++ " = 1.0") `shouldParse`
118+
\c@(Checksum x) -> parse instruction "" (fromString $ "%$" ++ show c ++ " = 1.0;") `shouldParse`
119119
Assign (Local $ QbCrc x) (ELit (LitF 1))
120120
it "can parse an if with no else branches" $
121-
parse instruction "" "if 1.0\n doSomething()\nendif" `shouldParse`
122-
IfElse (ELit (LitF 1), [BareExpr $ BareCall (QbName "doSomething") []]) [] []
121+
parse instruction "" "if (1.0) {\n doSomething();\n}" `shouldParse`
122+
IfElse (Paren (ELit (LitF 1)), [BareExpr $ BareCall (QbName "doSomething") []]) [] []
123123
it "can parse an if/elseif" $
124-
parse instruction "" "if 1.0\n doSomething()\nelseif 2.0\n doNothing()\nendif"
125-
`shouldParse` IfElse (ELit (LitF 1), [BareExpr $ BareCall (QbName "doSomething") []])
126-
[(ELit (LitF 2), [BareExpr $ BareCall (QbName "doNothing") []])]
124+
parse instruction "" "if (1.0) {\n doSomething();\n} elseif (2.0) {\n doNothing();\n}"
125+
`shouldParse` IfElse (Paren (ELit (LitF 1)), [BareExpr $ BareCall (QbName "doSomething") []])
126+
[(Paren (ELit (LitF 2)), [BareExpr $ BareCall (QbName "doNothing") []])]
127127
[]
128128
it "can parse an if/else" $
129-
parse instruction "" "if 1.0\n doSomething()\nelse\n doNothing()\nendif"
130-
`shouldParse` IfElse (ELit (LitF 1), [BareExpr $ BareCall (QbName "doSomething") []])
129+
parse instruction "" "if (1.0) {\n doSomething();\n} else {\n doNothing();\n}"
130+
`shouldParse` IfElse (Paren (ELit (LitF 1)), [BareExpr $ BareCall (QbName "doSomething") []])
131131
[]
132132
[BareExpr $ BareCall (QbName "doNothing") []]
133133
it "can parse an if/elseif/else" $
134-
parse instruction "" "if 1.0\n doSomething()\nelseif 2.0\n doNothing()\nelse\n doNothing()\nendif"
135-
`shouldParse` IfElse (ELit (LitF 1), [BareExpr $ BareCall (QbName "doSomething") []])
136-
[(ELit (LitF 2), [BareExpr $ BareCall (QbName "doNothing") []])]
134+
parse instruction "" "if (1.0) {\n doSomething();\n} elseif (2.0) {\n doNothing();\n} else{ \n doNothing();\n}"
135+
`shouldParse` IfElse (Paren (ELit (LitF 1)), [BareExpr $ BareCall (QbName "doSomething") []])
136+
[(Paren (ELit (LitF 2)), [BareExpr $ BareCall (QbName "doNothing") []])]
137137
[BareExpr $ BareCall (QbName "doNothing") []]
138138
it "can parse a begin/repeat" $
139-
parse instruction "" "begin\n doSomething()\nrepeat (4)" `shouldParse`
140-
Repeat (Just . ELit . SmallLit . LitN $ 4) [BareExpr $ BareCall (QbName "doSomething") []]
139+
parse instruction "" "repeat (4) {\n doSomething();\n}" `shouldParse`
140+
Repeat (Just . Paren . ELit . SmallLit . LitN $ 4) [BareExpr $ BareCall (QbName "doSomething") []]
141141
it "can parse an infinite begin/repeat" $
142-
parse instruction "" "begin\n doSomething()\nrepeat" `shouldParse`
142+
parse instruction "" "repeat {\n doSomething();\n}" `shouldParse`
143143
Repeat Nothing [BareExpr $ BareCall (QbName "doSomething") []]
144144
it "can parse a switch without default" $
145-
parse instruction "" "switch %i\ncase 1:\n doSomething()\n break\nendswitch" `shouldParse`
146-
Switch (ELit . SmallLit . LitKey . Local . QbName $ "i")
145+
parse instruction "" "switch (%i) {\ncase 1:\n doSomething();\n break;\n}" `shouldParse`
146+
Switch (Paren . ELit . SmallLit . LitKey . Local . QbName $ "i")
147147
[(LitN 1, [BareExpr $ BareCall (QbName "doSomething") [], Break] )]
148148
[]
149149
it "can parse a switch with default" $
150-
parse instruction "" "switch %i\ncase 1:\n doSomething()\n break\ndefault:\n doNothing()\nendswitch" `shouldParse`
151-
Switch (ELit . SmallLit . LitKey . Local . QbName $ "i")
150+
parse instruction "" "switch (%i) {\ncase 1:\n doSomething();\n break;\ndefault:\n doNothing();\n}" `shouldParse`
151+
Switch (Paren . ELit . SmallLit . LitKey . Local . QbName $ "i")
152152
[(LitN 1, [BareExpr $ BareCall (QbName "doSomething") [], Break] )]
153153
[BareExpr $ BareCall (QbName "doNothing") []]
154154
it "can parse a break" $
155-
parse instruction "" "break" `shouldParse` Break
155+
parse instruction "" "break;" `shouldParse` Break
156156
it "can parse a no-value return" $
157-
parse instruction "" "return" `shouldParse` Return Nothing
157+
parse instruction "" "return;" `shouldParse` Return Nothing
158158
it "can parse a non-keyword return" $
159-
parse instruction "" "return 3" `shouldParse` Return (Just (Nothing, ELit (SmallLit (LitN 3))))
159+
parse instruction "" "return 3;" `shouldParse` Return (Just (Nothing, ELit (SmallLit (LitN 3))))
160160
it "can parse a keyword return" $
161-
parse instruction "" "return (x=2.0)" `shouldParse` Return (Just (Just $ QbName "x", ELit (LitF 2)))
161+
parse instruction "" "return (x=2.0);" `shouldParse` Return (Just (Just $ QbName "x", ELit (LitF 2)))
162162
it "can parse a keyword return by crc" $
163-
parse instruction "" "return ($1234abcd=2.0)" `shouldParse` Return (Just (Just $ QbCrc 0x1234abcd, ELit (LitF 2)))
163+
parse instruction "" "return ($1234abcd=2.0);" `shouldParse` Return (Just (Just $ QbCrc 0x1234abcd, ELit (LitF 2)))
164164
it "can parse a bare call expression" $
165-
parse instruction "" "doSomething()"
165+
parse instruction "" "doSomething();"
166166
`shouldParse` BareExpr (BareCall (QbName "doSomething") [])
167167

168168
termTests :: Spec
@@ -244,15 +244,15 @@ structTests :: Spec
244244
structTests =
245245
describe "struct" $ do
246246
it "should parse a 1-item struct" $ do
247-
parse struct "" "{\n\tqbkey x = $00000000;\n}" `shouldParse`
247+
parse struct "" "{qbkey x = $00000000;}" `shouldParse`
248248
Struct [StructItem QbTKey (QbName "x") (QbKey $ QbCrc 0)]
249-
parse struct "" "{\n\tqbkeyref x = $00000000;\n}" `shouldParse`
249+
parse struct "" "{qbkeyref x = $00000000;}" `shouldParse`
250250
Struct [StructItem QbTKeyRef (QbName "x") (QbKeyRef $ QbCrc 0)]
251251
it "can parse arrays of all types" $ do
252-
parse struct "" "{\n\tarray<int> _ = [1,2,3];\n}" `shouldParse`
252+
parse struct "" "{array<int> _ = [1,2,3];}" `shouldParse`
253253
Struct [StructItem (QbTArray QbTInteger) (QbCrc 0) (QbArray . QbArr QbTInteger
254254
$ [QbInteger 1, QbInteger 2, QbInteger 3])]
255-
parse struct "" "{\n\tarray<float> _ = [1.0, 2.0, 3.0];\n}" `shouldParse`
255+
parse struct "" "{array<float> _ = [1.0, 2.0, 3.0];}" `shouldParse`
256256
Struct [StructItem (QbTArray QbTFloat) (QbCrc 0) (QbArray . QbArr QbTFloat
257257
$ [QbFloat 1, QbFloat 2, QbFloat 3])]
258258
parse struct "" "{\n\tarray<string> _ = ['a', 'b'];\n}" `shouldParse`
@@ -283,6 +283,8 @@ qbScriptTests :: Spec
283283
qbScriptTests =
284284
describe "qbScript" $ do
285285
it "should parse an empty script" $
286-
parse qbScript "" "script()\nendscript" `shouldParse` QbScript Nothing []
286+
parse qbScript "" "script () {\n}" `shouldParse` QbScript Nothing []
287287
-- TODO: sample scripts
288-
return ()
288+
it "should parse repeat inside if" $
289+
parse qbScript "" "script() { if(<...>) { repeat { break; }}}" `shouldParse`
290+
QbScript Nothing [IfElse (Paren . ELit $ LitPassthrough, [Repeat Nothing [Break]]) [] []]

0 commit comments

Comments
 (0)