public
Description: Haskell implemented JavaScript interpreter
Homepage:
Clone URL: git://github.com/motemen/jusk.git
jusk / Parser.hs
100644 634 lines (545 sloc) 21.235 kb
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
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
{-
Parser.hs
構文パーザ
http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/11_Expressions.html
http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/12_Statements.html
-}
 
module Parser (module Parser, Text.ParserCombinators.Parsec.ParseError) where
import Text.ParserCombinators.Parsec hiding(Parser)
import Monad
import List
import Data.Char (isDigit,digitToInt)
 
import DataTypes
import ParserUtil
 
parse :: String -> Either ParseError JavaScriptProgram
parse = runLex program
 
-- Literals {{{
-- http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/7_Lexical_Conventions.html
--- Identifiers
identifier :: Parser Expression
identifier = liftM Identifier identifierString
 
-- Literals
literal :: Parser Expression
literal = nullLiteral
      <|> booleanLiteral
      <|> numericLiteral
      <|> stringLiteral
      <|> regularExpressionLiteral
 
--- Null Literals
nullLiteral :: Parser Expression
nullLiteral = do reserved "null"
                 return $ Literal Null
 
--- Boolean Literals
booleanLiteral :: Parser Expression
booleanLiteral = (reserved "true" >> (return $ Literal $ Boolean True))
             <|> (reserved "false" >> (return $ Literal $ Boolean False))
             <?> "boolean"
 
--- Numeric Literals
numericLiteral :: Parser Expression
numericLiteral =
    do num <- naturalOrFloat
       return $ Literal
              $ Number
              $ case num of
                     Left n -> Integer n
                     Right n -> Double n
    <?> "number"
 
--- String Literals
-- some code from libraries/parsec/Text/ParserCombinators/Parsec/Expression.hs
stringLiteral :: Parser Expression
stringLiteral = (do charOrExprs <- (stringCharacters '"' <|> stringCharacters '\'')
                    whiteSpace
                    return $ (+++) $ foldr join [] charOrExprs)
            <?> "string"
    where join (Right c) ((Literal (String cs)):xs) =
              [Literal $ String $ c:cs] ++ xs
          join (Right c) xs =
              [Literal $ String [c]] ++ xs
          join (Left e) xs =
              [e] ++ xs
          (+++) [] = Literal $ String ""
          (+++) [x] = x
          (+++) (x:xs) = Operator "+" [x, (+++) xs]
 
stringCharacters :: Char -> Parser [Either Expression Char]
stringCharacters q = between (char q) (char q) (many $ stringChar q)
 
stringChar :: Char -> Parser (Either Expression Char)
stringChar q = (char '\\' >> escapeSequence)
           <|> (liftM Right $ noneOf (q:"\r\n\f"))
 
escapeSequence :: Parser (Either Expression Char)
escapeSequence = liftM Left stringInterpolateSequence
             <|> liftM Right characterEscapeSequence
             <|> liftM Right (do { char '0'; notFollowedBy $ satisfy isDigit; return '\0' })
             <|> liftM Right octEscapeSequence -- Not specified in ECMA-3
             <|> liftM Right hexEscapeSequence
-- <|> unicodeEscapeSequence
 
characterEscapeSequence :: Parser Char
characterEscapeSequence = singleEscapeCharacter
                      <|> noneOf "xu0123456789\r\n\f"
 
singleEscapeCharacter :: Parser Char
singleEscapeCharacter = choice $ zipWith escaped "'\"\\bfnrtv" "'\"\\\b\f\n\r\t\v"
                      where escaped c code = do { char c; return code }
 
octEscapeSequence :: Parser Char
octEscapeSequence = do digits <- try (times 3 digit) <|> try (times 2 digit) <|> times 1 digit
                       return $ toEnum $ foldr (\b a -> a * 8 + (digitToInt b)) 0 digits
 
hexEscapeSequence :: Parser Char
hexEscapeSequence = do char 'x'
                       digits <- times 2 hexDigit
                       return $ toEnum $ foldr (\b a -> a * 16 + (digitToInt b)) 0 digits
 
stringInterpolateSequence :: Parser Expression
stringInterpolateSequence =
    do char '{'
       expr <- expression AllowIn
       char '}'
       return expr
 
-- Regular Expression Literals
regularExpressionLiteral :: Parser Expression
regularExpressionLiteral =
    do char '/'
       pattern <- regularExpressionBody
       char '/'
       flags <- regularExpressionFlags
       whiteSpace
       return $ RegExpLiteral pattern flags
 
regularExpressionBody :: Parser String
regularExpressionBody =
    do c <- regularExpressionFirstChar
       cs <- many regularExpressionChar
       return $ concat $ (c:cs)
 
regularExpressionFirstChar :: Parser [Char]
regularExpressionFirstChar =
    backSlashSequence <|> (liftM return $ noneOf "\n\r\f*/")
 
regularExpressionChar :: Parser [Char]
regularExpressionChar =
    backSlashSequence <|> (liftM return $ noneOf "\n\r\f/")
    
backSlashSequence :: Parser [Char]
backSlashSequence =
    do char '\\'
       (char '/' >> return "/")
        <|> (do { choice $ zipWith escaped "fbntr" ["\f", "\b", "\n", "\t", "\r"] })
        <|> (do { c <- noneOf "\n\r\f"; return ['\\', c] })
    where escaped c code = do { char c; return code }
 
regularExpressionFlags :: Parser [Char]
regularExpressionFlags =
    many $ letter
-- }}}
 
-- Expressions {{{
-- http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/11_Expressions.html
 
--- Primary Expressions
primaryExpression :: Parser Expression
primaryExpression = (keyword "this")
                <|> literal
                <|> identifier
                <|> arrayLiteral
                <|> objectLiteral
                <|> parens (expression AllowIn)
                <?> ""
 
--- Array Literals
arrayLiteral :: Parser Expression
arrayLiteral =
    (squares $ do el <- elisionOpt
                  es <- option [] elementList
                  return $ ArrayLiteral $ el ++ es)
     <?> "array"
 
elementList :: Parser [Expression]
elementList =
    do e <- assignmentExpression AllowIn
       es <- option [] elementList1
       return $ (e:es)
 
elementList1 :: Parser [Expression]
elementList1 =
    do comma
       el <- elisionOpt
       (try $ do es <- elementList
                 return $ el ++ es)
        <|> return el
 
elisionOpt :: Parser [Expression]
elisionOpt = many comma >>= return . map (const $ Literal Undefined)
 
--- Object Literals
objectLiteral :: Parser Expression
objectLiteral = braces (objectLiteralPair `sepBy` comma) >>= return . ObjectLiteral
            <?> "object literal"
 
objectLiteralPair :: Parser (Expression, Expression)
objectLiteralPair =
    do name <- stringLiteral <|> numericLiteral <|> (liftM (Literal . String) identifierString)
       colon
       expr <- assignmentExpression AllowIn
       return (name, expr)
 
--- Left-Hand-Side Expressions
arguments :: Parser Expression
arguments =
    liftM (Operator "()") (parens $ (assignmentExpression AllowIn) `sepBy` comma)
 
leftHandSideExpression :: Parser Expression
leftHandSideExpression =
    do news <- many $ reserved "new"
       expr <- do e <- functionExpression <|> primaryExpression
                  ps <- many propOperator
                  return $ foldl pushArg e ps
       args <- many arguments
       ps <- many $ propOperator <|> arguments
       let n = min (length news) (length args)
           newExpr = foldl (flip ($)) expr $ map applyNewOp (take n args)
           newExpr' = iterate applyNoArgNewOp newExpr !! ((length news - length args) `max` 0)
       return $ foldl pushArg newExpr' (drop n args ++ ps)
    where propOperator = (liftM applyBracketOp $ squares $ expression AllowIn)
                     <|> (reservedOp "." >> liftM (applyBracketOp . Literal . String) identifierString)
          applyBracketOp e = Operator "[]" [e]
          applyNoArgNewOp e = Operator "new" [e]
          applyNewOp (Operator _ args) e = Operator "new" (e:args)
 
parenExpression :: Parser Expression
parenExpression = parens (assignmentExpression AllowIn)
 
parenListExpression :: Parser Expression
parenListExpression = liftM List (parens $ (assignmentExpression AllowIn) `sepBy` comma)
 
--- Postfix Expressions
postfixExpression :: Parser Expression
postfixExpression =
    do e <- leftHandSideExpression
       option e (postfixOp "++" e <|> postfixOp "--" e)
    where postfixOp op e =
              do noLineTerminatorHere
                 reservedOp op
                 return $ Operator ('_':op) [e]
 
--- Unary Operators
operatorWithArg1 :: String -> Parser Expression -> Parser Expression
operatorWithArg1 op p =
    do reservedOp op
       arg <- p
       return $ Operator op [arg]
 
chainOperator :: [String] -> Parser Expression -> Parser Expression
chainOperator ops p =
    (do a <- p
        e <- many $ do Punctuator op <- choice $ map operator ops
                       a <- p
                       return $ Operator op [a]
        return $ foldl pushArg a e)
     <?> ""
 
unaryExpression :: Parser Expression
unaryExpression = postfixExpression
              <|> operatorWithArg1 "delete" postfixExpression
              <|> operatorWithArg1 "void" unaryExpression
              <|> operatorWithArg1 "typeof" unaryExpression
              <|> operatorWithArg1 "++" postfixExpression
              <|> operatorWithArg1 "--" postfixExpression
              <|> operatorWithArg1 "+" unaryExpression
              <|> operatorWithArg1 "-" unaryExpression
              <|> operatorWithArg1 "~" unaryExpression
              <|> operatorWithArg1 "!" unaryExpression
 
--- Multiplicative Operators
multiplicativeExpression :: Parser Expression
multiplicativeExpression = chainOperator ["*", "/", "%"] unaryExpression
 
--- Additive Operators
additiveExpression :: Parser Expression
additiveExpression = chainOperator ["+", "-"] multiplicativeExpression
 
--- Bitwise Shift Operators
shiftExpression :: Parser Expression
shiftExpression = chainOperator ["<<", ">>", ">>>"] additiveExpression
 
--- Relational Operators
relationalExpression :: ParserParameter -> Parser Expression
relationalExpression AllowIn = chainOperator ["<", ">", "<=", ">=", "instanceof", "in"] shiftExpression
relationalExpression NoIn = chainOperator ["<", ">", "<=", ">=", "instanceof"] shiftExpression
 
--- Equality Operators
equalityExpression :: ParserParameter -> Parser Expression
equalityExpression p = chainOperator ["==", "!=", "===", "!=="] (relationalExpression p)
 
--- Binary Bitwise Operators
bitwiseAndExpression, bitwiseXorExpression, bitwiseOrExpression :: ParserParameter -> Parser Expression
bitwiseAndExpression p = chainOperator ["&"] (equalityExpression p)
bitwiseXorExpression p = chainOperator ["^"] (bitwiseAndExpression p)
bitwiseOrExpression p = chainOperator ["|"] (bitwiseXorExpression p)
 
--- Binary Logical Operators
logicalAndExpression, logicalXorExpression, logicalOrExpression :: ParserParameter -> Parser Expression
logicalAndExpression p = chainOperator ["&&"] (bitwiseOrExpression p)
logicalXorExpression p = chainOperator ["^^"] (logicalAndExpression p)
logicalOrExpression p = chainOperator ["||"] (logicalAndExpression p)
 
--- Conditional Operators
conditionalExpression :: ParserParameter -> Parser Expression
conditionalExpression p =
    do c <- logicalOrExpression p
       (do reservedOp "?"
           t <- assignmentExpression p
           reservedOp ":"
           e <- assignmentExpression p
           return $ Operator "?:" [c, t, e])
           `ifFail` c
 
nonAssignmentExpression :: ParserParameter -> Parser Expression
nonAssignmentExpression p =
    do c <- logicalOrExpression p
       (do reservedOp "?"
           t <- nonAssignmentExpression p
           reservedOp ":"
           e <- nonAssignmentExpression p
           return $ Operator "?:" [c, t, e])
           `ifFail` c
 
--- Assignment Operators
assignmentExpression :: ParserParameter -> Parser Expression
assignmentExpression p =
    do expr <- conditionalExpression p
       if isLeftHandSideExpr expr
          then (do reservedOp "="
                   right <- assignmentExpression p
                   return $ Let expr right)
                <|> (do op <- compoundAssignment
                        right <- assignmentExpression p
                        return $ Let expr $ Operator (init op) [expr, right])
                <|> return expr
          else return expr
    where isLeftHandSideExpr (Operator op _) = op `elem` ["[]", "()", "new"]
          isLeftHandSideExpr _ = True
 
compoundAssignment, logicalAssignment :: Parser String
compoundAssignment = choice $ map opString ["*=", "/=", "%=", "+=", "-=", "<<=", ">>=", ">>>=", "&=", "^=", "|="]
logicalAssignment = choice $ map opString ["&&=", "^^=", "||="] -- Not defined
 
opString :: String -> Parser String
opString op = do reservedOp op
                 return op
 
--- Comma Operator
expression :: ParserParameter -> Parser Expression
expression p = liftM toList $ (assignmentExpression p) `sepBy1` comma
             where toList [x] = x
                   toList xs = List xs
-- }}}
 
-- Statements {{{
-- http://www.mozilla.org/js/language/js20/core/statements.html
statement :: Parser Statement
statement = block
        <|> (variableStatement AllowIn)
        <|> emptyStatement
        <|> (try labelledStatement)
        <|> expressionStatement
        <|> ifStatement
        <|> iterationStatement
        <|> continueStatement
        <|> breakStatement
        <|> returnStatement
        <|> withStatement
        <|> switchStatement
        <|> throwStatement
        <|> tryStatement
 
semicolon :: Parser ()
semicolon = (semi >> return ())
        <|> ((symbol "}" <?> "") >> putBack "}")
        <|> lineTerminator
        <|> do pos <- getPosition
               st <- getState
               input <- getInput
               case () of
                    _ | null input -> do setInput ";"
                                         semi
                                         return ()
                    _ | pos == stLTPos st
                                   -> do setInput (';':input)
                                         semicolon
                    _ | otherwise -> fail ""
 
--- Block
block :: Parser Statement
block = liftM STBlock (braces statementListOpt) <?> "block"
 
statementListOpt :: Parser [Statement]
statementListOpt = many statement
 
--- Variable statement
variableStatement :: ParserParameter -> Parser Statement
variableStatement p =
    do reserved "var"
       bindings <- variableDeclarationList p
       semicolon
       return $ STVarDef bindings
    <?> "variable definition"
 
variableDeclarationList :: ParserParameter -> Parser [VariableBinding]
variableDeclarationList p =
    (variableDeclaration p) `sepBy` comma
 
variableDeclaration :: ParserParameter -> Parser VariableBinding
variableDeclaration p =
    do var <- identifierString
       init <- initialiserOpt p
       return (var, init)
 
initialiserOpt :: ParserParameter -> Parser (Maybe Expression)
initialiserOpt p =
    option Nothing
           (do reservedOp "="
               liftM Just $ assignmentExpression p)
 
--- Empty Statement
emptyStatement :: Parser Statement
emptyStatement =
    do semi
       return STEmpty
 
--- Expression Statement
expressionStatement :: Parser Statement
expressionStatement = ((reserved "function" <|> reservedOp "{") >> fail "")
                  <|> do expr <- expression AllowIn
                         semicolon
                         return $ STExpression expr
 
--- The if Statement
ifStatement :: Parser Statement
ifStatement =
    do reserved "if"
       condition <- parens $ expression AllowIn
       thenStatement <- statement
       (do reserved "else"
           elseStatement <- statement
           return $ STIf condition thenStatement (Just elseStatement))
           `ifFail` STIf condition thenStatement Nothing
 
--- Iteration Statements
iterationStatement :: Parser Statement
iterationStatement = doWhileStatement
                 <|> whileStatement
                 <|> forStatement
 
---- Do-While Statement
doWhileStatement :: Parser Statement
doWhileStatement =
    do reserved "do"
       block <- statement
       reserved "while"
       condition <- parens $ expression AllowIn
       semicolon
       return $ STDoWhile condition block
 
---- While Statement
whileStatement :: Parser Statement
whileStatement =
    do reserved "while"
       condition <- parens $ expression AllowIn
       block <- statement
       return $ STWhile condition block
 
--- For Statements
forStatement :: Parser Statement
forStatement =
    do reserved "for"
       symbol "("
       (try $ do init <- forInitializer
                 semi
                 cond <- expressionOpt
                 semi
                 updt <- expressionOpt
                 symbol ")"
                 block <- statement
                 return $ STFor init cond updt block)
           <|> (do binding <- (do reserved "var"
                                  liftM STVarDef $ variableDeclarationList NoIn)
                              <|> liftM STExpression leftHandSideExpression
                   reserved "in"
                   object <- expression AllowIn
                   symbol ")"
                   block <- statement
                   return $ STForIn binding object block)
 
forInitializer :: Parser Statement
forInitializer =
    option STEmpty
           ((do reserved "var"
                liftM STVarDef $ variableDeclarationList NoIn)
            <|> (liftM STExpression $ expression NoIn))
 
expressionOpt :: Parser Expression
expressionOpt = (expression AllowIn)
            <|> (return $ List [])
 
--- Switch Statement
--- With Statement
--- Continue and Break Statements
continueStatement :: Parser Statement
continueStatement =
    do reservedWithNoLT "continue"
       label <- option Nothing (liftM Just identifierString)
       semicolon
       return $ STContinue label
 
breakStatement :: Parser Statement
breakStatement =
    do reservedWithNoLT "break"
       label <- option Nothing (liftM Just identifierString)
       semicolon
       return $ STBreak label
 
--- Return Statement
returnStatement :: Parser Statement
returnStatement =
    do reservedWithNoLT "return"
       expr <- option Nothing (liftM Just (try $ expression AllowIn))
       semicolon
       return $ STReturn expr
 
-- Labelled Statements
labelledStatement :: Parser Statement
labelledStatement =
    do label <- identifierString
       colon
       st <- statement
       return $ STLabelled label st
 
-- The with Statement
withStatement :: Parser Statement
withStatement =
    do reserved "with"
       expr <- parens $ expression AllowIn
       st <- statement
       return $ STWith expr st
 
-- The switch Statement
switchStatement :: Parser Statement
switchStatement =
    do reserved "switch"
       expr <- parens $ expression AllowIn
       cases <- caseBlock
       return $ STSwitch expr cases
 
caseBlock :: Parser [(Maybe Expression, Statement)]
caseBlock =
    braces $ do clausesPre <- caseClausesOpt
                clausesDefault <- option mzero (liftM return defaultCase)
                clausesPost <- caseClausesOpt
                return $ clausesPre ++ clausesDefault ++ clausesPost
 
caseClausesOpt :: Parser [(Maybe Expression, Statement)]
caseClausesOpt =
    many $ do reserved "case"
              expr <- expression AllowIn
              colon
              ss <- statementListOpt
              return (Just expr, STBlock ss)
 
defaultCase :: Parser (Maybe Expression, Statement)
defaultCase =
    do reserved "default"
       colon
       ss <- statementListOpt
       return (Nothing, STBlock ss)
 
-- Throw Statement
throwStatement :: Parser Statement
throwStatement =
    do reservedWithNoLT "throw"
       expr <- expression AllowIn
       semicolon
       return $ STThrow expr
 
-- Try Statement
tryStatement :: Parser Statement
tryStatement =
    do reserved "try"
       try <- block
       (catch, finally) <- (do c <- catchClause
                               f <- option Nothing $ liftM Just finallyClause
                               return (Just c, f))
                               <|> (do f <- finallyClause
                                       return (Nothing, Just f))
       return $ STTry try catch finally
 
catchClause :: Parser (Parameter, Statement)
catchClause =
    do reserved "catch"
       param <- parens identifierString
       block <- block
       return (param, block)
 
finallyClause :: Parser Statement
finallyClause =
    do reserved "finally"
       block
 
--- Programs
program :: Parser JavaScriptProgram
program = many (functionDeclaration <|> statement)
-- }}}
 
-- Functions {{{
--- http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/13_Function_Definition.html
 
--- Function Expressions
functionDeclaration :: Parser Statement
functionDeclaration =
    do reserved "function"
       name <- identifierString
       function <- functionCommon
       return $ STFuncDef { funcDefName = name, funcDefFunc = function }
    <?> "function declaration"
 
functionExpression :: Parser Expression
functionExpression =
    do reserved "function"
       name <- option "anonymous" identifierString
       Function { funcParam = params, funcBody = body } <- functionCommon
       return $ Literal $ nullObject { objName = name, objObject = nullFunction { funcParam = params, funcBody = body } }
 
functionCommon :: Parser NativeObject
functionCommon =
    do params <- parens formalParameterListOpt
       body <- liftM STBlock $ braces program
       return $ nullFunction { funcParam = params, funcBody = body }
 
formalParameterListOpt :: Parser Parameters
formalParameterListOpt = identifierString `sepBy` comma
-- }}}