| 1 | {-# OPTIONS_GHC -cpp -fglasgow-exts -funbox-strict-fields -fno-full-laziness -fno-cse -fallow-overlapping-instances #-} |
|---|
| 2 | |
|---|
| 3 | {-| |
|---|
| 4 | Higher-level parser for building ASTs. |
|---|
| 5 | |
|---|
| 6 | > I sang of leaves, of leaves of gold, and leaves of gold there grew: |
|---|
| 7 | > Of wind I sang, a wind there came and in the branches blew. |
|---|
| 8 | > Beyond the Sun, beyond the Moon, the foam was on the Sea, |
|---|
| 9 | > And by the strand of Ilmarin there grew a golden Tree... |
|---|
| 10 | -} |
|---|
| 11 | |
|---|
| 12 | module Pugs.Parser ( |
|---|
| 13 | ruleBlockBody, |
|---|
| 14 | possiblyExit, |
|---|
| 15 | module Pugs.Lexer, |
|---|
| 16 | module Pugs.Parser.Types, |
|---|
| 17 | module Pugs.Parser.Unsafe, |
|---|
| 18 | module Pugs.Parser.Operator, |
|---|
| 19 | |
|---|
| 20 | -- Circularity: Used in Pugs.Parser.Operator |
|---|
| 21 | parseTerm, parseNoParenArgList, ruleSubName, ruleSigil, |
|---|
| 22 | |
|---|
| 23 | -- Circularity: Used in Pugs.Parser.Literal |
|---|
| 24 | ruleExpression, retInterpolatedBlock, |
|---|
| 25 | ruleArraySubscript, ruleHashSubscript, ruleCodeSubscript, |
|---|
| 26 | ruleInvocationParens, verbatimVarNameString, ruleVerbatimBlock, retVerbatimBlock, |
|---|
| 27 | ruleBlockLiteral, ruleDoBlock, regularVarName, regularVarNameForSigil, ruleNamedMethodCall, |
|---|
| 28 | |
|---|
| 29 | genParamEntries |
|---|
| 30 | ) where |
|---|
| 31 | import Pugs.Internals |
|---|
| 32 | import Pugs.AST |
|---|
| 33 | import qualified Pugs.Exp as Exp |
|---|
| 34 | import Pugs.Types |
|---|
| 35 | import Pugs.Version (versnum) |
|---|
| 36 | import Pugs.Lexer |
|---|
| 37 | import Pugs.Rule |
|---|
| 38 | |
|---|
| 39 | import Pugs.Parser.Types |
|---|
| 40 | import Pugs.Parser.Unsafe |
|---|
| 41 | import Pugs.Parser.Export |
|---|
| 42 | import Pugs.Parser.Operator |
|---|
| 43 | import Pugs.Parser.Doc |
|---|
| 44 | import Pugs.Parser.Literal |
|---|
| 45 | import Pugs.Parser.Util |
|---|
| 46 | import qualified Data.Map as Map |
|---|
| 47 | import qualified Data.Set as Set |
|---|
| 48 | |
|---|
| 49 | -- Lexical units -------------------------------------------------- |
|---|
| 50 | |
|---|
| 51 | ruleBlock :: RuleParser BlockInfo |
|---|
| 52 | ruleBlock = do |
|---|
| 53 | lvl <- gets s_bracketLevel |
|---|
| 54 | case lvl of |
|---|
| 55 | StatementBracket -> ruleBlock' |
|---|
| 56 | _ -> lexeme ruleVerbatimBlock |
|---|
| 57 | where |
|---|
| 58 | ruleBlock' = do |
|---|
| 59 | rv <- ruleVerbatimBlock |
|---|
| 60 | -- Implementation of 'line-ending } terminates statement' |
|---|
| 61 | -- See L<S04/Statement-ending blocks>. |
|---|
| 62 | -- We are now at end of closing '}'. Mark the position... |
|---|
| 63 | prevPos <- getPosition |
|---|
| 64 | -- Skip whitespaces. If we go into another line, |
|---|
| 65 | -- then it's statement-level break |
|---|
| 66 | whiteSpace |
|---|
| 67 | currPos <- getPosition |
|---|
| 68 | if sourceLine prevPos == sourceLine currPos then return rv else do |
|---|
| 69 | -- Manually insert a ';' symbol here! |
|---|
| 70 | insertIntoPosition ";" |
|---|
| 71 | -- Register that this is an eol-block, thus can't be hash composers |
|---|
| 72 | retBlockWith (Ann (Prag [MkPrag "eol-block" 0])) rv |
|---|
| 73 | |
|---|
| 74 | ruleVerbatimBlock :: RuleParser BlockInfo |
|---|
| 75 | ruleVerbatimBlock = verbatimRule "block" $ do |
|---|
| 76 | block <- verbatimBraces ruleBlockBody |
|---|
| 77 | retBlockWith (Syn "block" . (:[])) block |
|---|
| 78 | |
|---|
| 79 | ruleEmptyExp :: RuleParser Exp |
|---|
| 80 | ruleEmptyExp = (<?> "") . expRule $ do |
|---|
| 81 | symbol ";" |
|---|
| 82 | return emptyExp |
|---|
| 83 | |
|---|
| 84 | ruleBlockBody :: RuleParser BlockInfo |
|---|
| 85 | ruleBlockBody = localBlock $ do |
|---|
| 86 | whiteSpace |
|---|
| 87 | ver <- option "6" (try (symbol "use" >> rulePerlVersion)) |
|---|
| 88 | case ver of |
|---|
| 89 | ('5':_) -> do |
|---|
| 90 | let chunk = many1 (noneOf "{}") <|> verbatimBraces block |
|---|
| 91 | block = fmap (\x -> ('{':x) ++ "}") body |
|---|
| 92 | body = fmap concat (many chunk) |
|---|
| 93 | p5code <- body |
|---|
| 94 | return (Syn "block-perl5" [Val (VStr p5code)]) |
|---|
| 95 | _ -> do |
|---|
| 96 | pre <- many ruleEmptyExp |
|---|
| 97 | body <- option emptyExp ruleStatementList |
|---|
| 98 | post <- many ruleEmptyExp |
|---|
| 99 | whiteSpace |
|---|
| 100 | return $ foldl1 mergeStmts (pre ++ [body] ++ post) |
|---|
| 101 | |
|---|
| 102 | {-| |
|---|
| 103 | Match a single statement (not including any terminating semicolon). A |
|---|
| 104 | statement consists of a single 'ruleExpression', followed by an optional |
|---|
| 105 | statement-modifier (e.g. @if $foo@ or @for \@baz@). |
|---|
| 106 | |
|---|
| 107 | One of the sub-rules used by 'ruleStatementList'. |
|---|
| 108 | -} |
|---|
| 109 | ruleStatement :: RuleParser Exp |
|---|
| 110 | ruleStatement = do |
|---|
| 111 | exp <- ruleExpression |
|---|
| 112 | f <- ruleStatementModifier <?> "" |
|---|
| 113 | f exp |
|---|
| 114 | |
|---|
| 115 | ruleStatementModifier :: RuleParser (Exp -> RuleParser Exp) |
|---|
| 116 | ruleStatementModifier = verbatimRule "statement modifier" . option return $ choice |
|---|
| 117 | [ s_postConditional |
|---|
| 118 | , s_postLoop |
|---|
| 119 | , s_postIterate |
|---|
| 120 | ] |
|---|
| 121 | |
|---|
| 122 | ruleStatementList :: RuleParser Exp |
|---|
| 123 | ruleStatementList = rule "statements" . |
|---|
| 124 | enterBracketLevel StatementBracket . |
|---|
| 125 | sepLoop $ do |
|---|
| 126 | optional $ try (ruleVerbatimIdentifier >> char ':' >> mandatoryWhiteSpace) |
|---|
| 127 | choice |
|---|
| 128 | [ noSep ruleDocBlock |
|---|
| 129 | , nonSep ruleBlockDeclaration |
|---|
| 130 | , semiSep ruleDeclaration |
|---|
| 131 | , nonSep ruleConstruct |
|---|
| 132 | , semiSep ruleStatement |
|---|
| 133 | ] |
|---|
| 134 | where |
|---|
| 135 | nonSep = doSep many -- must be followed by 0+ semicolons |
|---|
| 136 | semiSep = doSep many1 -- must be followed by 1+ semicolons |
|---|
| 137 | noSep r = fmap (\x -> (x, False)) r |
|---|
| 138 | doSep sepCount r = do |
|---|
| 139 | exp <- r |
|---|
| 140 | terminate <- option True $ do |
|---|
| 141 | (sepCount (symbol ";") <?> "") |
|---|
| 142 | return False |
|---|
| 143 | return (exp, terminate) |
|---|
| 144 | sepLoop rule = do |
|---|
| 145 | whiteSpace |
|---|
| 146 | (eof >> return Noop) <|> do |
|---|
| 147 | (exp, terminate) <- rule |
|---|
| 148 | if terminate then return exp else do |
|---|
| 149 | rest <- option Noop (sepLoop rule) |
|---|
| 150 | return $ exp `mergeStmts` rest |
|---|
| 151 | |
|---|
| 152 | -- Declarations ------------------------------------------------ |
|---|
| 153 | |
|---|
| 154 | ruleBlockDeclaration :: RuleParser Exp |
|---|
| 155 | ruleBlockDeclaration = rule "block declaration" $ choice |
|---|
| 156 | [ ruleRuleDeclaration |
|---|
| 157 | , ruleSubDeclaration |
|---|
| 158 | , ruleClosureTrait False |
|---|
| 159 | , rulePackageBlockDeclaration |
|---|
| 160 | ] |
|---|
| 161 | |
|---|
| 162 | ruleDeclaration :: RuleParser Exp |
|---|
| 163 | ruleDeclaration = rule "declaration" $ choice |
|---|
| 164 | [ rulePackageDeclaration |
|---|
| 165 | , ruleMemberDeclaration |
|---|
| 166 | , ruleTraitDeclaration |
|---|
| 167 | , ruleUseDeclaration |
|---|
| 168 | , ruleNoDeclaration |
|---|
| 169 | , ruleInlineDeclaration |
|---|
| 170 | , ruleRequireDeclaration |
|---|
| 171 | , ruleTrustsDeclaration |
|---|
| 172 | ] |
|---|
| 173 | |
|---|
| 174 | data SubModifier = ImplicitNil | ImplicitMulti | ImplicitProto deriving (Eq) |
|---|
| 175 | |
|---|
| 176 | ruleSubHead :: RuleParser (SubModifier, SubType, String) |
|---|
| 177 | ruleSubHead = rule "subroutine head" $ do |
|---|
| 178 | prefix <- choice |
|---|
| 179 | [ symbol "multi" >> return ImplicitMulti |
|---|
| 180 | , symbol "proto" >> return ImplicitProto |
|---|
| 181 | , return ImplicitNil |
|---|
| 182 | ] |
|---|
| 183 | |
|---|
| 184 | -- You're allowed to omit the "sub": |
|---|
| 185 | -- multi sub foo (...) {...} # legal |
|---|
| 186 | -- sub foo (...) {...} # legal, too |
|---|
| 187 | let implicitSub | ImplicitNil <- prefix = pzero |
|---|
| 188 | | otherwise = return SubRoutine |
|---|
| 189 | styp <- choice |
|---|
| 190 | [ do symbol "sub" |
|---|
| 191 | return SubRoutine |
|---|
| 192 | , do symbol "coro" |
|---|
| 193 | return SubCoroutine |
|---|
| 194 | , do (symbol "submethod" <|> symbol "method") |
|---|
| 195 | return SubMethod |
|---|
| 196 | , do symbol "macro" |
|---|
| 197 | return SubMacro |
|---|
| 198 | , do symbol "subset" |
|---|
| 199 | return _SubSet_ |
|---|
| 200 | ] <|> implicitSub |
|---|
| 201 | name <- ruleSubName |
|---|
| 202 | return (prefix, styp, name) |
|---|
| 203 | |
|---|
| 204 | -- XXX - Kluged up way to mark a subset parsed as a sub |
|---|
| 205 | _SubSet_ :: SubType |
|---|
| 206 | _SubSet_ = SubPrim |
|---|
| 207 | |
|---|
| 208 | -- | Scope, context, isMulti, styp, name |
|---|
| 209 | type SubDescription = (Scope, String, SubModifier, SubType, String) |
|---|
| 210 | |
|---|
| 211 | ruleSubScopedWithContext :: RuleParser SubDescription |
|---|
| 212 | ruleSubScopedWithContext = tryRule "scoped subroutine with context" $ do |
|---|
| 213 | scope <- ruleScope |
|---|
| 214 | cxt <- identifier |
|---|
| 215 | (isMulti, styp, name) <- ruleSubHead |
|---|
| 216 | return (scope, cxt, isMulti, styp, name) |
|---|
| 217 | |
|---|
| 218 | ruleSubScoped :: RuleParser SubDescription |
|---|
| 219 | ruleSubScoped = tryRule "scoped subroutine" $ do |
|---|
| 220 | scope <- ruleScope |
|---|
| 221 | (isMulti, styp, name) <- ruleSubHead |
|---|
| 222 | return (scope, "Any", isMulti, styp, name) |
|---|
| 223 | |
|---|
| 224 | ruleSubGlobal :: RuleParser SubDescription |
|---|
| 225 | ruleSubGlobal = tryRule "global subroutine" $ do |
|---|
| 226 | (isMulti, styp, name) <- ruleSubHead |
|---|
| 227 | return (SOur, "Any", isMulti, styp, name) |
|---|
| 228 | |
|---|
| 229 | ruleRuleDeclaration :: RuleParser Exp |
|---|
| 230 | ruleRuleDeclaration = rule "rule declaration" $ do |
|---|
| 231 | (mod, withAdvs, name) <- try $ do |
|---|
| 232 | mod <- symbol "proto" <|> symbol "multi" <|> return "" |
|---|
| 233 | advs <- ruleRegexDeclarator |
|---|
| 234 | fmap ((,,) mod advs) identifier |
|---|
| 235 | optional (ruleSubParameters ParensMandatory) |
|---|
| 236 | adverbs <- fmap withAdvs ruleQuoteAdverbs |
|---|
| 237 | skipMany (symbol "is" >> regularAdverbPair) -- XXX - context |
|---|
| 238 | if mod == "proto" then return emptyExp else do |
|---|
| 239 | ch <- char '{' |
|---|
| 240 | expr <- rxLiteralAny adverbs ch (balancedDelim ch) |
|---|
| 241 | unsafeEvalExp (_Sym SOur ('<':'*':name) mempty (Syn "rx" [expr, adverbs]) Noop) |
|---|
| 242 | insertIntoPosition $ "method " ++ name ++ " ($_) { $_ ~~ m/<" ++ name ++ ">/ };" |
|---|
| 243 | return emptyExp |
|---|
| 244 | |
|---|
| 245 | rulePackageBlockDeclaration :: RuleParser Exp |
|---|
| 246 | rulePackageBlockDeclaration = rule "package block declaration" $ do |
|---|
| 247 | -- scope <- option Nothing $ fmap Just ruleScope |
|---|
| 248 | rv <- try $ do |
|---|
| 249 | optional ruleScope -- XXX - not handled yet |
|---|
| 250 | rv <- rulePackageHead |
|---|
| 251 | lookAhead (char '{') |
|---|
| 252 | return rv |
|---|
| 253 | case rv of |
|---|
| 254 | Right (_, kind, pkgVal, env) -> do |
|---|
| 255 | block <- verbatimBraces ruleBlockBody |
|---|
| 256 | env' <- ask |
|---|
| 257 | putRuleEnv env'{ envPackage = envPackage env } |
|---|
| 258 | retInterpolatedBlock =<< retBlockWith (\body -> Syn "namespace" [kind, pkgVal, body]) block |
|---|
| 259 | Left err -> fail err |
|---|
| 260 | |
|---|
| 261 | rulePackageDeclaration :: RuleParser Exp |
|---|
| 262 | rulePackageDeclaration = rule "package declaration" $ do |
|---|
| 263 | -- scope <- option Nothing $ fmap Just ruleScope |
|---|
| 264 | rv <- try $ do |
|---|
| 265 | optional ruleScope -- XXX - not handled yet |
|---|
| 266 | rulePackageHead |
|---|
| 267 | case rv of |
|---|
| 268 | Right (_, kind, pkgVal, _) -> return $ Syn "package" [kind, pkgVal] |
|---|
| 269 | Left err -> fail err |
|---|
| 270 | |
|---|
| 271 | rulePackageHead :: RuleParser (Either String (String, Exp, Exp, Env)) |
|---|
| 272 | rulePackageHead = do |
|---|
| 273 | scope <- option Nothing $ fmap Just ruleScope |
|---|
| 274 | sym <- choice $ map symbol (words "package module class role grammar") |
|---|
| 275 | name <- ruleQualifiedIdentifier |
|---|
| 276 | optional ruleVersionPart -- v |
|---|
| 277 | optional ruleAuthorPart -- a |
|---|
| 278 | whiteSpace |
|---|
| 279 | env <- ask |
|---|
| 280 | newName <- case scope of |
|---|
| 281 | Just SOur -> return $ cast (envPackage env) ++ "::" ++ name |
|---|
| 282 | Nothing -> return name |
|---|
| 283 | _ -> fail "I only know about package- and global-scoped classes. Sorry." |
|---|
| 284 | traits <- many $ ruleTrait ["is", "does"] |
|---|
| 285 | let pkgClass = case sym of |
|---|
| 286 | "package" -> "Package" |
|---|
| 287 | "module" -> "Module" |
|---|
| 288 | "class" -> "Class" |
|---|
| 289 | "role" -> "Class" -- XXX - Wrong - need metamodel |
|---|
| 290 | "grammar" -> "Grammar" |
|---|
| 291 | _ -> fail "bug" |
|---|
| 292 | mixinRoles = nub ([ cls | ("does", cls) <- traits]) |
|---|
| 293 | parentClasses = nub ("Object":[ cls | ("is", cls) <- traits, cls /= "also" ]) |
|---|
| 294 | case () of |
|---|
| 295 | _ | elem name parentClasses -> do |
|---|
| 296 | return (Left $ "Circular class inheritance detected for " ++ sym ++ " '" ++ name ++ "'") |
|---|
| 297 | _ | elem name mixinRoles -> do |
|---|
| 298 | return (Left $ "Circular role composition detected for " ++ sym ++ " '" ++ name ++ "'") |
|---|
| 299 | _ -> do |
|---|
| 300 | unsafeEvalExp (newPackage pkgClass newName parentClasses mixinRoles) |
|---|
| 301 | modify $ \state -> state |
|---|
| 302 | { s_env = (s_env state) |
|---|
| 303 | { envPackage = cast newName |
|---|
| 304 | } |
|---|
| 305 | , s_dynParsers = MkDynParsersEmpty |
|---|
| 306 | } |
|---|
| 307 | let pkgVal = Val . VStr $ newName |
|---|
| 308 | kind = Val . VStr $ sym |
|---|
| 309 | return $ Right (newName, kind, pkgVal, env) |
|---|
| 310 | |
|---|
| 311 | ruleTraitsIsOnly :: RuleParser [String] |
|---|
| 312 | ruleTraitsIsOnly = fmap (map snd) . many $ ruleTrait ["is"] |
|---|
| 313 | |
|---|
| 314 | ruleSubDeclaration :: RuleParser Exp |
|---|
| 315 | ruleSubDeclaration = rule "subroutine declaration" $ do |
|---|
| 316 | -- namePos <- getPosition |
|---|
| 317 | (scope, typ, isMulti, styp, name) <- choice |
|---|
| 318 | [ ruleSubScopedWithContext |
|---|
| 319 | , ruleSubScoped |
|---|
| 320 | , ruleSubGlobal |
|---|
| 321 | ] |
|---|
| 322 | optional $ do { symbol "handles"; ruleExpression } |
|---|
| 323 | assoc <- option A_left . try $ do |
|---|
| 324 | symbol "is" |
|---|
| 325 | symbol "assoc" |
|---|
| 326 | lit <- parens qLiteral |
|---|
| 327 | case unwrap lit of |
|---|
| 328 | Val (VStr str) -> case str of |
|---|
| 329 | "left" -> return A_left |
|---|
| 330 | "right" -> return A_right |
|---|
| 331 | "non" -> return A_non |
|---|
| 332 | "chain" -> return A_chain |
|---|
| 333 | "list" -> return A_list |
|---|
| 334 | _ -> fail $ "Invalid associativity: " ++ str |
|---|
| 335 | _ -> fail $ "Invalid associativity: " ++ show lit |
|---|
| 336 | let returnsOrOf = try (ruleBareTrait "returns" <|> ruleBareTrait "of") |
|---|
| 337 | typ' <- option typ returnsOrOf |
|---|
| 338 | |
|---|
| 339 | -- Here if it's a subset-parsed-as-sub, escape now |
|---|
| 340 | if styp == _SubSet_ then skipMany (symbol "where" >> parseTerm) >> return emptyExp else do |
|---|
| 341 | |
|---|
| 342 | formal <- option Nothing $ ruleSubParameters ParensMandatory |
|---|
| 343 | typ'' <- option typ' returnsOrOf |
|---|
| 344 | traits <- ruleTraitsIsOnly |
|---|
| 345 | |
|---|
| 346 | env <- ask |
|---|
| 347 | let pkg = cast (envPackage env) |
|---|
| 348 | nameQualified | ':' `elem` name = name |
|---|
| 349 | | isGlobal = name |
|---|
| 350 | | isBuiltin = (head name:'*':tail name) |
|---|
| 351 | | otherwise = name |
|---|
| 352 | isGlobal = '*' `elem` name |
|---|
| 353 | isBuiltin = ("builtin" `elem` traits) |
|---|
| 354 | isExported = ("export" `elem` traits) |
|---|
| 355 | mkMulti | isMulti /= ImplicitNil = \x -> (cast x){ v_longname = _cast (cast signature) } |
|---|
| 356 | | otherwise = cast |
|---|
| 357 | mkSym sub n x = Sym scope (mkMulti n) mempty (Syn "sub" [Val sub]) x |
|---|
| 358 | var = mkMulti nameQualified |
|---|
| 359 | signature = self ++ paramsFor styp formal (maybe [] id formal) |
|---|
| 360 | self | styp > SubMethod = [] |
|---|
| 361 | | Just (prm:_) <- formal, isInvocant prm = [] |
|---|
| 362 | | otherwise = [selfParam . cast $ envPackage env] |
|---|
| 363 | |
|---|
| 364 | -- We have the prototype now; install it immediately! |
|---|
| 365 | -- fill in what we can about the sub before getting the block (below) |
|---|
| 366 | let sub@(VCode template) = VCode $ mkCode |
|---|
| 367 | { isMulti = isMulti /= ImplicitNil |
|---|
| 368 | , subName = cast nameQualified |
|---|
| 369 | , subOuterPads = envLexPads env |
|---|
| 370 | , subInnerPad = emptyPad |
|---|
| 371 | , subParams = signature |
|---|
| 372 | , subPackage = envPackage env |
|---|
| 373 | , subType = if "primitive" `elem` traits |
|---|
| 374 | then SubPrim else styp |
|---|
| 375 | , subAssoc = case v_categ var of |
|---|
| 376 | C_infix -> assoc |
|---|
| 377 | _ -> ANil |
|---|
| 378 | , subReturns = mkType typ'' |
|---|
| 379 | , subLValue = "rw" `elem` traits |
|---|
| 380 | , subBindings = [] |
|---|
| 381 | , subSlurpLimit = [] |
|---|
| 382 | , subCont = Nothing |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | -- Don't add the sub if it's unsafe and we're in safemode (XXX repeated below) |
|---|
| 386 | newPad <- if ("unsafe" `elem` traits && safeMode) then return (mkPad []) else do |
|---|
| 387 | -- This is ignored for multi-dispatch; see Pugs.Eval.Var comment "PROTO" |
|---|
| 388 | unsafeEvalLexDiff (mkSym sub nameQualified Noop) |
|---|
| 389 | `finallyM` clearDynParsers |
|---|
| 390 | |
|---|
| 391 | -- Generate init pad for each of our params, as well as for ourselves... |
|---|
| 392 | paramsPad <- genParamEntries styp signature |
|---|
| 393 | modify $ \s -> s{ s_protoPad = paramsPad } |
|---|
| 394 | block <- ruleBlock |
|---|
| 395 | |
|---|
| 396 | let (fun, names, _) = extractNamedPlaceholders styp formal (bi_body block) |
|---|
| 397 | |
|---|
| 398 | -- Check for placeholder vs formal parameters |
|---|
| 399 | when (isJust formal && (not.null) names) $ |
|---|
| 400 | fail "Cannot mix placeholder variables with formal parameters" |
|---|
| 401 | |
|---|
| 402 | env <- ask |
|---|
| 403 | |
|---|
| 404 | let template' = template |
|---|
| 405 | { subBody = case isMulti of |
|---|
| 406 | ImplicitProto -> fun -- XXX - Give Proto the tie-breaker status? |
|---|
| 407 | _ -> fun |
|---|
| 408 | , subOuterPads = envLexPads env |
|---|
| 409 | , subInnerPad = bi_pad block |
|---|
| 410 | , subTraitBlocks= bi_traits block (subTraitBlocks template) |
|---|
| 411 | } |
|---|
| 412 | sub = VCode template' |
|---|
| 413 | |
|---|
| 414 | -- Don't add the sub if it's unsafe and we're in safemode. |
|---|
| 415 | if "unsafe" `elem` traits && safeMode then return (Var var) else do |
|---|
| 416 | (`finallyM` clearDynParsers) $ if not (isLexicalVar var) |
|---|
| 417 | then do unsafeEvalExp $ mkSym sub nameQualified (Var var) |
|---|
| 418 | else do |
|---|
| 419 | let doExportCode = if not isExported then return (Var var) else do |
|---|
| 420 | -- we mustn't perform the export immediately upon parse, because |
|---|
| 421 | -- then only the first consumer of a module will see it. Instead, |
|---|
| 422 | -- make a note of this symbol being exportable, and defer the |
|---|
| 423 | -- actual symbol table manipulation to opEval. |
|---|
| 424 | -- %*INC<This::Package><exports><&this_sub> = expression-binding-&this_sub |
|---|
| 425 | -- ==> |
|---|
| 426 | -- %This::Package::EXPORTS<&this_sub> = expression-binding-&this_sub |
|---|
| 427 | let VCode cv = sub |
|---|
| 428 | (exportedSub, exportedName) |
|---|
| 429 | -- "method foo is export" is exported into "multi foo" here. |
|---|
| 430 | | SubMethod <- styp = (multiCode, cast var{ v_longname = _cast (cast multiSig) }) |
|---|
| 431 | | otherwise = (sub, cast var) |
|---|
| 432 | where |
|---|
| 433 | multiSig = map (\x -> x{ isInvocant = False }) (subParams cv) |
|---|
| 434 | multiCode = VCode cv |
|---|
| 435 | { isMulti = True |
|---|
| 436 | , subParams = multiSig |
|---|
| 437 | } |
|---|
| 438 | unsafeEvalExp $ Syn "=" |
|---|
| 439 | [ Syn "{}" [_Var ("%" ++ pkg ++ "::EXPORTS"), Val $ VStr exportedName] |
|---|
| 440 | , Val exportedSub |
|---|
| 441 | ] |
|---|
| 442 | return (Var var) |
|---|
| 443 | case lookupPad var newPad of |
|---|
| 444 | Just entry -> do |
|---|
| 445 | Val (VCode code) <- unsafeEvalExp (Syn "sub" [Val sub]) |
|---|
| 446 | let entry' = entry{ pe_proto = cv' } |
|---|
| 447 | cv' = MkRef (ICode code) |
|---|
| 448 | addBlockPad (adjustPad (const entry') var newPad) |
|---|
| 449 | result <- doExportCode |
|---|
| 450 | case entry' of |
|---|
| 451 | PEConstant{} -> return result |
|---|
| 452 | _ -> return $! unsafePerformSTM $! do |
|---|
| 453 | rv <- writePadEntry entry' cv' |
|---|
| 454 | return (rv `seq` result) |
|---|
| 455 | _ -> error "Impossible: Disappearing entry?" |
|---|
| 456 | |
|---|
| 457 | ruleSubNamePossiblyWithTwigil :: RuleParser String |
|---|
| 458 | ruleSubNamePossiblyWithTwigil = tryVerbatimRule "subroutine name" $ do |
|---|
| 459 | twigil <- ruleTwigil |
|---|
| 460 | name <- ruleOperatorName <|> ruleQualifiedIdentifier |
|---|
| 461 | return $ ('&':twigil) ++ name |
|---|
| 462 | |
|---|
| 463 | ruleSubName :: RuleParser String |
|---|
| 464 | ruleSubName = verbatimRule "subroutine name" $ do |
|---|
| 465 | twigil <- option "" (string "*") |
|---|
| 466 | name <- ruleOperatorName <|> ruleQualifiedIdentifier |
|---|
| 467 | return $ ('&':twigil) ++ name |
|---|
| 468 | |
|---|
| 469 | ruleOperatorName :: RuleParser String |
|---|
| 470 | ruleOperatorName = verbatimRule "operator name" $ do |
|---|
| 471 | categ <- choice (map (try . string) grammaticalCategories) `tryLookAhead` (oneOf "\xAB<{") |
|---|
| 472 | name <- do |
|---|
| 473 | -- char ':' |
|---|
| 474 | sub <- ruleHashSubscript |
|---|
| 475 | -- Not exactly un-evil |
|---|
| 476 | let (Syn "{}" [_, expr]) = sub (Val VUndef) |
|---|
| 477 | exprs = case expr of |
|---|
| 478 | Syn "," es -> es |
|---|
| 479 | e -> [e] |
|---|
| 480 | return $ unwords [ str | Val (VStr str) <- exprs ] |
|---|
| 481 | return $ categ ++ name |
|---|
| 482 | |
|---|
| 483 | |
|---|
| 484 | ruleSubParameters :: ParensOption -> RuleParser (Maybe [Param]) |
|---|
| 485 | ruleSubParameters wantParens = rule "subroutine parameters" $ do |
|---|
| 486 | rv <- ruleParamList wantParens (ruleFormalParam FormalsComplex) |
|---|
| 487 | case rv of |
|---|
| 488 | Just (invs:args:_) -> return . Just $ map setInv invs ++ args |
|---|
| 489 | _ -> return Nothing |
|---|
| 490 | where |
|---|
| 491 | setInv e = e { isInvocant = True } |
|---|
| 492 | |
|---|
| 493 | ruleFormalParam :: FormalsOption -> RuleParser Param |
|---|
| 494 | ruleFormalParam opt = rule "formal parameter" $ do |
|---|
| 495 | typ <- option "" $ ruleType |
|---|
| 496 | optional $ char '|' -- XXX hack to parse arglist (|$foo) |
|---|
| 497 | sigil1 <- option "" $ choice . map symbol $ words " : * " |
|---|
| 498 | name <- ruleParamName -- XXX support *[...] |
|---|
| 499 | sigil2 <- option "" $ choice . map symbol $ words " ? ! " |
|---|
| 500 | traits <- ruleTraitsIsOnly |
|---|
| 501 | -- sigil' is the canonical form of sigil1 and sigil2, e.g. |
|---|
| 502 | -- $foo is required --> !$foo |
|---|
| 503 | -- :$foo --> ?:$foo |
|---|
| 504 | -- :$foo! --> !:$foo |
|---|
| 505 | -- :$foo is required --> !:$foo |
|---|
| 506 | isDefaultSpecified <- option False $ do |
|---|
| 507 | lookAhead $ symbol "=" |
|---|
| 508 | return True |
|---|
| 509 | let isOptional = isDefaultSpecified |
|---|
| 510 | || sigil2 == "?" |
|---|
| 511 | || sigil1 == ":" && sigil2 /= "!" && "required" `notElem` traits |
|---|
| 512 | || "optional" `elem` traits |
|---|
| 513 | let sigil' = (if isOptional then '?' else '!'):sigil1 |
|---|
| 514 | defaultExp = case name of |
|---|
| 515 | ('@':_) -> Val (VList []) |
|---|
| 516 | ('%':_) -> Val (VList []) |
|---|
| 517 | _ -> Noop |
|---|
| 518 | |
|---|
| 519 | -- XXX - RIGHT HERE, add this one to CompPad? |
|---|
| 520 | modify $ \state -> state |
|---|
| 521 | { s_knownVars = Map.insert (cast name) (fromJust . envCompPad $ s_env state) (s_knownVars state) } |
|---|
| 522 | |
|---|
| 523 | rv <- case opt of |
|---|
| 524 | FormalsSimple -> option emptyExp $ do |
|---|
| 525 | pseudoAssignment (cxtOfSigilVar var) (Val (VType (if null typ then typeOfSigilVar var else mkType typ))) |
|---|
| 526 | where |
|---|
| 527 | var = cast name |
|---|
| 528 | FormalsComplex -> ruleParamDefault |
|---|
| 529 | when (opt == FormalsComplex) . optional $ do |
|---|
| 530 | symbol "-->" |
|---|
| 531 | ruleParamList ParensOptional $ choice |
|---|
| 532 | [ ruleType `sepBy1` symbol "of" |
|---|
| 533 | , ruleFormalParam FormalsComplex >> return [] |
|---|
| 534 | ] |
|---|
| 535 | let exp = case rv of |
|---|
| 536 | Noop -> defaultExp |
|---|
| 537 | _ -> rv |
|---|
| 538 | return $ foldr appTrait (buildParam typ sigil' name exp) traits |
|---|
| 539 | where |
|---|
| 540 | appTrait "rw" x = x { isWritable = True } |
|---|
| 541 | appTrait "copy" x = x { isLValue = False, isWritable = True } |
|---|
| 542 | appTrait "lazy" x = x { isLazy = True } |
|---|
| 543 | appTrait "context" x = x { paramName = (paramName x){ v_twigil = TImplicit } } |
|---|
| 544 | appTrait _ x = x -- error "unknown trait" |
|---|
| 545 | |
|---|
| 546 | ruleParamDefault :: RuleParser Exp |
|---|
| 547 | ruleParamDefault = rule "default value" $ option emptyExp $ do |
|---|
| 548 | symbol "=" |
|---|
| 549 | parseExpWithItemOps |
|---|
| 550 | |
|---|
| 551 | ruleTrustsDeclaration :: RuleParser Exp |
|---|
| 552 | ruleTrustsDeclaration = do |
|---|
| 553 | symbol "trusts" |
|---|
| 554 | lexeme ruleQualifiedIdentifier |
|---|
| 555 | return emptyExp |
|---|
| 556 | |
|---|
| 557 | ruleTraitDeclaration :: RuleParser Exp |
|---|
| 558 | ruleTraitDeclaration = try $ do |
|---|
| 559 | -- XXX horrible hack! "is eval(...), ..." should *not* be parsed as a trait |
|---|
| 560 | -- declaration. So we check whether we're really statement-level, i.e. |
|---|
| 561 | -- is eval(...) [eof] # trait |
|---|
| 562 | -- is eval(...); # trait |
|---|
| 563 | -- is eval(...) } # trait |
|---|
| 564 | -- is eval(...), ... # sub call |
|---|
| 565 | (aux, trait) <- ruleTrait ["is", "does"] |
|---|
| 566 | lookAhead (eof <|> (oneOf ";}" >> return ())) |
|---|
| 567 | pkg <- asks envPackage |
|---|
| 568 | let meta = _Var (':':'*':cast pkg) |
|---|
| 569 | expMeta = Syn "=" |
|---|
| 570 | [ Syn "{}" [meta, Val (VStr aux)] |
|---|
| 571 | , Syn "," [Syn "@{}" [Syn "{}" [meta, Val (VStr aux)]], Val (VStr trait)] |
|---|
| 572 | ] |
|---|
| 573 | addDoes | "does" <- aux = Stmts (App (_Var "&HOW::does") (Just meta) [Val (VStr trait)]) |
|---|
| 574 | | otherwise = id |
|---|
| 575 | unsafeEvalExp $ Syn "if" |
|---|
| 576 | [ meta |
|---|
| 577 | , addDoes expMeta |
|---|
| 578 | , App (_Var "&die") Nothing [Val (VStr $ "Can't add trait to non-class package: " ++ show pkg)] |
|---|
| 579 | ] |
|---|
| 580 | return Noop |
|---|
| 581 | |
|---|
| 582 | ruleMemberDeclaration :: RuleParser Exp |
|---|
| 583 | ruleMemberDeclaration = do |
|---|
| 584 | symbol "has" |
|---|
| 585 | typ <- option "" $ do |
|---|
| 586 | optional (string "::") |
|---|
| 587 | lexeme $ choice |
|---|
| 588 | [ ruleQualifiedIdentifier |
|---|
| 589 | -- ::?CLASS, ::?ROLE, etc. |
|---|
| 590 | , char '?' >> ruleQualifiedIdentifier >> fmap cast (asks envPackage) |
|---|
| 591 | ] |
|---|
| 592 | attr <- ruleVarName |
|---|
| 593 | (sigil:twigil:key) <- case attr of |
|---|
| 594 | (_:'.':_) -> return attr |
|---|
| 595 | (_:'!':_) -> return attr |
|---|
| 596 | (x:xs@(twigil:_)) |
|---|
| 597 | | (isAlpha twigil) || twigil == '_' |
|---|
| 598 | -> return (x:'!':xs) |
|---|
| 599 | _ -> fail $ "Invalid member variable name '" ++ attr ++ "'" |
|---|
| 600 | traits <- ruleTraitsIsOnly |
|---|
| 601 | optional $ do { symbol "handles"; ruleExpression } |
|---|
| 602 | def <- ruleParamDefault |
|---|
| 603 | env <- ask |
|---|
| 604 | let self = selfParam $ cast (envPackage env) |
|---|
| 605 | paramsPad <- genParamEntries SubMethod [self] |
|---|
| 606 | -- manufacture an accessor, and register this slot into metaobject |
|---|
| 607 | let sub = mkPrim |
|---|
| 608 | { isMulti = False |
|---|
| 609 | , subName = cast name |
|---|
| 610 | , subReturns = if null typ then typeOfSigil (cast sigil) else mkType typ |
|---|
| 611 | , subBody = fun |
|---|
| 612 | , subParams = [self] |
|---|
| 613 | , subInnerPad = paramsPad |
|---|
| 614 | , subLValue = "rw" `elem` traits |
|---|
| 615 | , subType = SubMethod |
|---|
| 616 | } |
|---|
| 617 | exp = Syn "sub" [Val $ VCode sub] |
|---|
| 618 | name | twigil == '.' = '&':(pkg ++ "::" ++ key) |
|---|
| 619 | | otherwise = '&':(pkg ++ "::" ++ (twigil:key)) |
|---|
| 620 | fun = Syn (sigil:"{}") [Ann (Cxt (cxtOfSigil $ cast sigil)) (Syn "{}" [_Var "$__SELF__", Val (VStr key)])] |
|---|
| 621 | pkg = cast (envPackage env) |
|---|
| 622 | metaObj = _Var (':':'*':pkg) |
|---|
| 623 | attrDef = Syn "{}" [Syn "{}" [metaObj, Val (VStr "attrs")], Val (VStr key)] |
|---|
| 624 | unsafeEvalExp (_Sym SOur name mempty exp (Syn "=" [attrDef, def])) |
|---|
| 625 | return emptyExp |
|---|
| 626 | |
|---|
| 627 | {-| |
|---|
| 628 | Match a @no@ declaration, i.e. the opposite of @use@ (see |
|---|
| 629 | 'ruleUseDeclaration'). |
|---|
| 630 | |
|---|
| 631 | Works by matching \'@no@\', then trying 'ruleNoVersion' and |
|---|
| 632 | @'ruleUsePackage' False@. |
|---|
| 633 | -} |
|---|
| 634 | ruleNoDeclaration :: RuleParser Exp |
|---|
| 635 | ruleNoDeclaration = rule "no declaration" $ do |
|---|
| 636 | symbol "no" |
|---|
| 637 | choice [ ruleNoVersion >> return emptyExp |
|---|
| 638 | , ruleUsePackage False |
|---|
| 639 | ] |
|---|
| 640 | return emptyExp |
|---|
| 641 | |
|---|
| 642 | {-| |
|---|
| 643 | Match a @use@ declaration. |
|---|
| 644 | |
|---|
| 645 | Works by matching \'@use@\', then trying 'ruleUseVersion' and |
|---|
| 646 | @'ruleUsePackage' True@. |
|---|
| 647 | -} |
|---|
| 648 | ruleUseDeclaration :: RuleParser Exp |
|---|
| 649 | ruleUseDeclaration = rule "use declaration" $ do |
|---|
| 650 | symbol "use" |
|---|
| 651 | choice [ try ruleUseVersion >> return emptyExp |
|---|
| 652 | , ruleUsePackage True |
|---|
| 653 | ] |
|---|
| 654 | |
|---|
| 655 | rulePerlVersion :: RuleParser String |
|---|
| 656 | rulePerlVersion = rule "perl version" $ do |
|---|
| 657 | optional (string "v" <|> string "Perl-") |
|---|
| 658 | version <- many1 (choice [ digit, char '.' ]) |
|---|
| 659 | optional ruleAuthorPart |
|---|
| 660 | {- |
|---|
| 661 | optional $ do |
|---|
| 662 | variant <- ruleAuthorPart |
|---|
| 663 | when (map toLower variant /= "pugs") $ do |
|---|
| 664 | pos <- getPosition |
|---|
| 665 | error $ "Perl implementation " ++ tail variant ++ " required--this is only Pugs v" ++ versnum ++ ", stopped at " ++ (show pos) |
|---|
| 666 | -} |
|---|
| 667 | return version |
|---|
| 668 | |
|---|
| 669 | {-| |
|---|
| 670 | Match a Perl version number (as part of a @use@ declaration), and abort if |
|---|
| 671 | the version needed is higher than our version. |
|---|
| 672 | -} |
|---|
| 673 | ruleUseVersion :: RuleParser () |
|---|
| 674 | ruleUseVersion = rule "use version" $ do |
|---|
| 675 | version <- rulePerlVersion |
|---|
| 676 | when (version > versnum) $ do |
|---|
| 677 | pos <- getPosition |
|---|
| 678 | error $ "Perl v" ++ version ++ " required--this is only v" ++ versnum ++ ", stopped at " ++ (show pos) |
|---|
| 679 | |
|---|
| 680 | {-| |
|---|
| 681 | Match a Perl version number (as part of a @no@ declaration), and abort if |
|---|
| 682 | the version needed is lower than our version. |
|---|
| 683 | -} |
|---|
| 684 | ruleNoVersion :: RuleParser () |
|---|
| 685 | ruleNoVersion = rule "no version" $ do |
|---|
| 686 | version <- rulePerlVersion |
|---|
| 687 | when (version <= versnum) $ do |
|---|
| 688 | pos <- getPosition |
|---|
| 689 | error $ "Perls since v" ++ version ++ " too modern--this is v" |
|---|
| 690 | ++ versnum ++ ", stopped at " ++ show pos |
|---|
| 691 | |
|---|
| 692 | {-| |
|---|
| 693 | Match the contents of a @use@ or @no@ declaration. |
|---|
| 694 | |
|---|
| 695 | Works by reading the (optional) \'lang\' prefix, then dispatching to either |
|---|
| 696 | 'ruleUseJSANModule' or 'ruleUsePerlPackage' as appropriate. |
|---|
| 697 | |
|---|
| 698 | It is assumed that the @use@ or @no@ has already been consumed by |
|---|
| 699 | 'ruleUseDeclaration' or 'ruleNoDeclaration'. |
|---|
| 700 | -} |
|---|
| 701 | ruleUsePackage :: Bool -- ^ @True@ for @use@; @False@ for @no@ |
|---|
| 702 | -> RuleParser Exp |
|---|
| 703 | ruleUsePackage use = rule "use package" $ do |
|---|
| 704 | lang <- ruleUsePackageLang |
|---|
| 705 | case lang of |
|---|
| 706 | "jsan" -> if use |
|---|
| 707 | then ruleUseJSANModule |
|---|
| 708 | else fail "can't 'no' a JSAN module" |
|---|
| 709 | "jsperl5" -> if use |
|---|
| 710 | then ruleUseJSPerl5Module |
|---|
| 711 | else fail "can't 'no' a Perl5 module" |
|---|
| 712 | "java" -> if use |
|---|
| 713 | then ruleUseJavaModule |
|---|
| 714 | else fail "can't 'no' a Java module" |
|---|
| 715 | _ -> ruleUsePerlPackage use lang |
|---|
| 716 | where |
|---|
| 717 | ruleUsePackageLang = option "perl6" $ try $ do |
|---|
| 718 | lang <- identifier |
|---|
| 719 | char ':' |
|---|
| 720 | notFollowedBy (char ':') |
|---|
| 721 | return lang |
|---|
| 722 | |
|---|
| 723 | {-| |
|---|
| 724 | Match the package-name and import-list part of a (non-JSAN) @use@ or @no@ |
|---|
| 725 | declaration, then perform the actual import. |
|---|
| 726 | |
|---|
| 727 | The parser itself does not yield a useful value; instead it modifies the |
|---|
| 728 | 'Pugs.AST.Internals.Env' part of the parser's state to reflect the results |
|---|
| 729 | of the declaration. (If the package is a Perl 5 package, the modification |
|---|
| 730 | will be different.) |
|---|
| 731 | |
|---|
| 732 | It is assumed that 'ruleUsePackage' has already consumed the \'lang\' prefix, |
|---|
| 733 | which is passed in as the second argument. |
|---|
| 734 | -} |
|---|
| 735 | ruleUsePerlPackage :: Bool -- ^ @True@ for @use@; @False@ for @no@ |
|---|
| 736 | -> String -- ^ \'lang\' prefix (e.g. \"@perl5@\" or |
|---|
| 737 | -- \"@perl6@\") |
|---|
| 738 | -> RuleParser Exp |
|---|
| 739 | ruleUsePerlPackage use lang = rule "use perl package" $ do |
|---|
| 740 | -- author and version get thrown away for now |
|---|
| 741 | (names, _, _) <- rulePackageFullName |
|---|
| 742 | let name = concat (intersperse "::" names) |
|---|
| 743 | ruleLoadPerlPackage name use lang |
|---|
| 744 | |
|---|
| 745 | ruleLoadPerlPackage :: String -> Bool -> String -> RuleParser Exp |
|---|
| 746 | ruleLoadPerlPackage pkg use lang = do |
|---|
| 747 | when use $ do -- for &no, don't load code |
|---|
| 748 | env <- ask |
|---|
| 749 | env' <- unsafeEvalEnv $ if lang == "perl6" |
|---|
| 750 | then (App (_Var "&use") Nothing [Val $ VStr pkg]) |
|---|
| 751 | else (App (_Var $ "&require_" ++ lang) Nothing [Val $ VStr pkg]) |
|---|
| 752 | modify $ \state -> state |
|---|
| 753 | { s_env = env |
|---|
| 754 | { envGlobal = envGlobal env' |
|---|
| 755 | } |
|---|
| 756 | , s_dynParsers = MkDynParsersEmpty |
|---|
| 757 | } |
|---|
| 758 | try (do { verbatimParens whiteSpace ; return emptyExp}) <|> do |
|---|
| 759 | imp <- option emptyExp ruleExpression |
|---|
| 760 | let sub = _Var $ ('&':pkg) ++ if use then "::import" else "::unimport" |
|---|
| 761 | |
|---|
| 762 | Val res <- unsafeEvalExp $ Syn "if" |
|---|
| 763 | [ sub |
|---|
| 764 | , App sub (Just $ Val $ VStr $ pkg) [imp] |
|---|
| 765 | , emptyExp |
|---|
| 766 | ] |
|---|
| 767 | |
|---|
| 768 | Val (VList exportList) <- res `seq` unsafeEvalExp $ case lang of |
|---|
| 769 | -- map { ~$_, ::Pkg.can($_) }, @importlist |
|---|
| 770 | "perl5" -> App (_Var "&map") Nothing [Syn "sub" |
|---|
| 771 | [ Val . VCode $ mkPrim |
|---|
| 772 | { subBody = Syn "," |
|---|
| 773 | [ App (_Var "&prefix:<~>") (Just $ Var varTopic) [] |
|---|
| 774 | , App (_Var "&can") (Just $ _Var (':':'*':pkg)) [Var varTopic] |
|---|
| 775 | ] |
|---|
| 776 | , subParams = [defaultScalarParam] |
|---|
| 777 | , subInnerPad = defaultScalarPad |
|---|
| 778 | } |
|---|
| 779 | ], imp ] |
|---|
| 780 | -- %Pkg::EXPORTS.kv |
|---|
| 781 | _ -> App (_Var "&kv") (Just $ _Var ('%':pkg ++ "::EXPORTS")) [] |
|---|
| 782 | |
|---|
| 783 | let hardcodedScopeFixme = SMy |
|---|
| 784 | doExportList [] = [] |
|---|
| 785 | doExportList [x] = error $ "doExportList [x]: " ++ show x |
|---|
| 786 | doExportList (VStr name:ex:xs) = |
|---|
| 787 | (exportSym hardcodedScopeFixme name ex : doExportList xs) |
|---|
| 788 | doExportList x = error $ "doExportList x: " ++ show x |
|---|
| 789 | sequence_ $ doExportList exportList |
|---|
| 790 | clearDynParsers |
|---|
| 791 | return emptyExp |
|---|
| 792 | |
|---|
| 793 | {-| |
|---|
| 794 | Match a JSAN module name, returning an appropriate |
|---|
| 795 | sub call 'Pugs.AST.Exp' that will load the module using subs defined in |
|---|
| 796 | @PIL2JS::Internals@. |
|---|
| 797 | |
|---|
| 798 | Used by 'ruleUsePackage' after matching \'@use jsan:@\'. |
|---|
| 799 | |
|---|
| 800 | More info about JSAN can be found at <http://www.openjsan.org/>. |
|---|
| 801 | -} |
|---|
| 802 | ruleUseJSANModule :: RuleParser Exp |
|---|
| 803 | ruleUseJSANModule = do |
|---|
| 804 | name <- fmap (Val . VStr) (ruleDotOrColonSeparatedModuleName ".") |
|---|
| 805 | choice |
|---|
| 806 | [ try $ do |
|---|
| 807 | verbatimParens whiteSpace |
|---|
| 808 | return $ App (_Var "&PIL2JS::Internals::use_jsan_module_noimp") Nothing [name] |
|---|
| 809 | , do |
|---|
| 810 | exp <- option emptyExp ruleExpression |
|---|
| 811 | let exp' | exp == emptyExp = [] |
|---|
| 812 | | otherwise = [exp] |
|---|
| 813 | return $ App (_Var "&PIL2JS::Internals::use_jsan_module_imp") Nothing $ name:exp' |
|---|
| 814 | ] |
|---|
| 815 | |
|---|
| 816 | ruleUseJavaModule :: RuleParser Exp |
|---|
| 817 | ruleUseJavaModule = do |
|---|
| 818 | name <- ruleDotOrColonSeparatedModuleName "::" |
|---|
| 819 | ruleLoadPerlPackage name True "java" |
|---|
| 820 | |
|---|
| 821 | ruleDotOrColonSeparatedModuleName :: String -> RuleParser String |
|---|
| 822 | ruleDotOrColonSeparatedModuleName sep = lexeme $ do |
|---|
| 823 | names <- ruleVerbatimIdentifier `sepBy1` (try (string "::" <|> string ".")) |
|---|
| 824 | optional ruleVersionPart |
|---|
| 825 | optional ruleAuthorPart |
|---|
| 826 | return . concat $ intersperse sep names |
|---|
| 827 | |
|---|
| 828 | {-| |
|---|
| 829 | Match a perl5 module for js backend, returning an appropriate |
|---|
| 830 | sub call 'Pugs.AST.Exp' that will load the module using subs defined in |
|---|
| 831 | @PIL2JS::Internals@. |
|---|
| 832 | |
|---|
| 833 | -} |
|---|
| 834 | ruleUseJSPerl5Module :: RuleParser Exp |
|---|
| 835 | ruleUseJSPerl5Module = do |
|---|
| 836 | name <- fmap (Val . VStr) (ruleDotOrColonSeparatedModuleName "::") |
|---|
| 837 | |
|---|
| 838 | choice |
|---|
| 839 | [ try $ do |
|---|
| 840 | verbatimParens whiteSpace |
|---|
| 841 | return $ App (_Var "&PIL2JS::Internals::use_perl5_module_noimp") Nothing [name] |
|---|
| 842 | , do |
|---|
| 843 | exp <- option emptyExp ruleExpression |
|---|
| 844 | let exp' | exp == emptyExp = [] |
|---|
| 845 | | otherwise = [exp] |
|---|
| 846 | return $ App (_Var "&PIL2JS::Internals::use_perl5_module_imp") Nothing $ name:exp' |
|---|
| 847 | ] |
|---|
| 848 | |
|---|
| 849 | {-| |
|---|
| 850 | Match a full package name, consisting of: |
|---|
| 851 | |
|---|
| 852 | * A short name, optionally delimited by double colons (@::@) |
|---|
| 853 | |
|---|
| 854 | * An optional version specification |
|---|
| 855 | |
|---|
| 856 | * An optional author specification (e.g. @cpan:JRANDOM@) |
|---|
| 857 | -} |
|---|
| 858 | rulePackageFullName :: RuleParser ( [String] |
|---|
| 859 | , (Maybe String) |
|---|
| 860 | , (Maybe String) |
|---|
| 861 | ) |
|---|
| 862 | rulePackageFullName = do |
|---|
| 863 | name <- ruleDelimitedIdentifier "::" |
|---|
| 864 | version <- option Nothing $ fmap Just ruleVersionPart |
|---|
| 865 | author <- option Nothing $ fmap Just ruleAuthorPart |
|---|
| 866 | whiteSpace |
|---|
| 867 | return (name, version, author) |
|---|
| 868 | |
|---|
| 869 | -- | The version part of a fully-qualified package name. |
|---|
| 870 | ruleVersionPart :: RuleParser String |
|---|
| 871 | ruleVersionPart = do -- version - XXX |
|---|
| 872 | char '-' |
|---|
| 873 | str <- many (choice [ digit, char '.', char '(', char ')' ]) |
|---|
| 874 | return ('-':str) |
|---|
| 875 | |
|---|
| 876 | -- | The author part of a fully-qualified package name. |
|---|
| 877 | ruleAuthorPart :: RuleParser String |
|---|
| 878 | ruleAuthorPart = do -- author - XXX |
|---|
| 879 | char '-' |
|---|
| 880 | -- this will break if you specify an author AND an imports list |
|---|
| 881 | str <- many1 (satisfy (/= ';')) |
|---|
| 882 | return ('-':str) |
|---|
| 883 | {- end of ruleUseDeclaration -} |
|---|
| 884 | |
|---|
| 885 | ruleInlineDeclaration :: RuleParser Exp |
|---|
| 886 | ruleInlineDeclaration = rule "inline declaration" $ do |
|---|
| 887 | symbol "inline" |
|---|
| 888 | args <- ruleExpression |
|---|
| 889 | case args of |
|---|
| 890 | App (Var var) Nothing exp | var == cast "&infix:=>" -> do |
|---|
| 891 | return $ Syn "inline" exp |
|---|
| 892 | _ -> fail "not yet parsed" |
|---|
| 893 | |
|---|
| 894 | {-| |
|---|
| 895 | Match a @require@ declaration, returning a sub call 'Pugs.AST.Exp' that will |
|---|
| 896 | load the package at runtime. |
|---|
| 897 | |
|---|
| 898 | (This should probably be merged with 'ruleUseDeclaration' & friends, if |
|---|
| 899 | anybody has some tuits.) |
|---|
| 900 | -} |
|---|
| 901 | ruleRequireDeclaration :: RuleParser Exp |
|---|
| 902 | ruleRequireDeclaration = tryRule "require declaration" $ do |
|---|
| 903 | symbol "require" |
|---|
| 904 | (names, _, _) <- rulePackageFullName |
|---|
| 905 | return $ App (_Var "&require") Nothing [Val . VStr $ concat (intersperse "::" names)] |
|---|
| 906 | |
|---|
| 907 | ruleDoBlock :: RuleParser Exp |
|---|
| 908 | ruleDoBlock = rule "do block" $ do |
|---|
| 909 | sym <- symbol "do" <|> symbol "gather" |
|---|
| 910 | tree <- choice |
|---|
| 911 | [ ruleDoOnceBlock |
|---|
| 912 | , ruleBlockDeclaration |
|---|
| 913 | , ruleDeclaration |
|---|
| 914 | , ruleConstruct |
|---|
| 915 | , ruleStatement |
|---|
| 916 | ] |
|---|
| 917 | return $ if sym == "gather" |
|---|
| 918 | then App (_Var "&gather") Nothing [Val . VCode $ mkSub { subBody = tree }] |
|---|
| 919 | else tree |
|---|
| 920 | where |
|---|
| 921 | ruleDoOnceBlock = do |
|---|
| 922 | rv <- ruleBareOrPointyBlockLiteralWithoutDefaultParams |
|---|
| 923 | notFollowedBy (ruleStatementModifier >> return ' ') |
|---|
| 924 | return $ Syn "postwhile" [Val (castV False), rv] |
|---|
| 925 | |
|---|
| 926 | ruleClosureTrait :: Bool -> RuleParser Exp |
|---|
| 927 | ruleClosureTrait rhs = tryRule "closure trait" $ do |
|---|
| 928 | let rhsTraits = words " BEGIN CHECK INIT START ENTER FIRST " |
|---|
| 929 | let names = words " BEGIN CHECK INIT END START ENTER LEAVE KEEP UNDO FIRST NEXT LAST PRE POST CATCH CONTROL" |
|---|
| 930 | name <- choice $ map symbol $ names |
|---|
| 931 | block <- ruleBlock |
|---|
| 932 | popClosureTrait |
|---|
| 933 | when (rhs && not (name `elem` rhsTraits)) $ |
|---|
| 934 | fail (name ++ " may only be used at statement level") |
|---|
| 935 | let (fun, params) = extractPlaceholderVars (bi_body block) Set.empty |
|---|
| 936 | -- Check for placeholder vs formal parameters |
|---|
| 937 | unless (Set.null $ Set.delete varTopic params) $ |
|---|
| 938 | fail "Closure traits take no formal parameters" |
|---|
| 939 | env <- ask |
|---|
| 940 | let code = mkSub |
|---|
| 941 | { subName = cast name |
|---|
| 942 | , subType = SubBlock |
|---|
| 943 | , subBody = fun |
|---|
| 944 | , subPackage = envPackage env |
|---|
| 945 | , subInnerPad = (bi_pad block) |
|---|
| 946 | , subOuterPads = (PCompiling (fromJust $ envCompPad env):envLexPads env) |
|---|
| 947 | } |
|---|
| 948 | case name of |
|---|
| 949 | "END" -> do |
|---|
| 950 | -- We unshift END blocks to @*END at compile-time. |
|---|
| 951 | -- They're then run at the end of runtime or at the end of the |
|---|
| 952 | -- whole program. |
|---|
| 953 | pkg <- asks envPackage |
|---|
| 954 | rv <- unsafeEvalExp $ |
|---|
| 955 | App (_Var "&unshift") |
|---|
| 956 | (Just (_Var (if pkg == mainPkg then "@Main::END" else "@*END"))) |
|---|
| 957 | [Val $ VCode code] |
|---|
| 958 | return (rv `seq` emptyExp) |
|---|
| 959 | "BEGIN" -> do |
|---|
| 960 | -- We have to exit if the user has written code like BEGIN { exit }. |
|---|
| 961 | val <- possiblyExit =<< unsafeEvalExp (checkForIOLeak code) |
|---|
| 962 | -- And install any pragmas they've requested. |
|---|
| 963 | env <- ask |
|---|
| 964 | let idat = unsafePerformSTM . readTVar $ envInitDat env |
|---|
| 965 | install $ initPragmas idat |
|---|
| 966 | clearDynParsers |
|---|
| 967 | return val |
|---|
| 968 | "CHECK" -> vcode2checkBlock $ VCode code |
|---|
| 969 | "INIT" -> vcode2initBlock $ VCode code |
|---|
| 970 | -- we need to clone this closure sometimes |
|---|
| 971 | "START" -> vcode2startBlock $ VCode code |
|---|
| 972 | _ -> do |
|---|
| 973 | addClosureTrait name code |
|---|
| 974 | return emptyExp --retBlock SubBlock Nothing False block |
|---|
| 975 | -- XXX Not the right thing to return |
|---|
| 976 | where |
|---|
| 977 | install [] = return $ () |
|---|
| 978 | install prag = do |
|---|
| 979 | env' <- ask |
|---|
| 980 | let env'' = envCaller env' -- not sure about this. |
|---|
| 981 | case env'' of |
|---|
| 982 | Just target -> do |
|---|
| 983 | putRuleEnv target { envPragmas = prag ++ envPragmas target } |
|---|
| 984 | _ -> fail "no caller env to install pragma in" |
|---|
| 985 | |
|---|
| 986 | {-| Match a @quasi { ... }@ quotation -} |
|---|
| 987 | ruleCodeQuotation :: RuleParser Exp |
|---|
| 988 | ruleCodeQuotation = rule "code quotation" $ do |
|---|
| 989 | -- XXX - This is entirely kluge; it drops traits in the body too |
|---|
| 990 | symbol "quasi" >> optional (symbol ":COMPILING") |
|---|
| 991 | block <- ruleBlockBody |
|---|
| 992 | return (Syn "quasi" [bi_body block]) |
|---|
| 993 | |
|---|
| 994 | -- | If we've executed code like @BEGIN { exit }@, we've to run all @\@*END@ |
|---|
| 995 | -- blocks and then exit. Returns the input expression if there's no need to |
|---|
| 996 | -- exit. |
|---|
| 997 | {-# NOINLINE possiblyExit #-} |
|---|
| 998 | possiblyExit :: Exp -> RuleParser Exp |
|---|
| 999 | possiblyExit (Val (VControl (ControlExit exit))) = do |
|---|
| 1000 | -- Run all @*END blocks... |
|---|
| 1001 | rv <- unsafeEvalExp $ Stmts (Syn "for" |
|---|
| 1002 | [ _Var "@Main::END" |
|---|
| 1003 | , Syn "sub" |
|---|
| 1004 | [ Val . VCode $ mkPrim |
|---|
| 1005 | { subBody = App (Var varTopic) Nothing [] |
|---|
| 1006 | , subParams = [defaultScalarParam] |
|---|
| 1007 | , subInnerPad = defaultScalarPad |
|---|
| 1008 | } |
|---|
| 1009 | ] |
|---|
| 1010 | ]) (_Var "@Main::END") |
|---|
| 1011 | -- ...and then exit. |
|---|
| 1012 | return $ unsafePerformIO $ exitWith (rv `seq` exit) |
|---|
| 1013 | possiblyExit x = return x |
|---|
| 1014 | |
|---|
| 1015 | vcode2memoized :: VCode -> RuleParser VCode |
|---|
| 1016 | vcode2memoized code = do |
|---|
| 1017 | -- Ok. Now the tricky thing. |
|---|
| 1018 | -- This is the general idea: |
|---|
| 1019 | -- START { 42 } is transformed into |
|---|
| 1020 | -- { |
|---|
| 1021 | -- # XXX these should not be $? vars!!! |
|---|
| 1022 | -- state $?START_RESULT; |
|---|
| 1023 | -- state $?START_RUN; |
|---|
| 1024 | -- $?START_RUN++ ?? $?START_RESULT !! $?START_RESULT = { 42 }(); |
|---|
| 1025 | -- } |
|---|
| 1026 | -- These are the two state variables we need. |
|---|
| 1027 | -- This will soon add our two state vars to our pad |
|---|
| 1028 | |
|---|
| 1029 | lexDiff <- unsafeEvalLexDiff $ |
|---|
| 1030 | (_Sym SState "$?START_RESULT" mempty emptyExp) . |
|---|
| 1031 | (_Sym SState "$?START_RUN" mempty emptyExp) $ emptyExp |
|---|
| 1032 | |
|---|
| 1033 | let body' = Syn "if" |
|---|
| 1034 | [ App (_Var "&postfix:++") Nothing [_Var "$?START_RUN"] |
|---|
| 1035 | , _Var "$?START_RESULT" |
|---|
| 1036 | , Syn "=" [_Var "$?START_RESULT", subBody code] |
|---|
| 1037 | ] -- { $?START_RUN++; $?START_RESULT = 42 }; |
|---|
| 1038 | |
|---|
| 1039 | return $ code |
|---|
| 1040 | { subBody = body' |
|---|
| 1041 | , subInnerPad = subInnerPad code `mappend` lexDiff |
|---|
| 1042 | } |
|---|
| 1043 | |
|---|
| 1044 | vcode2startBlock :: Val -> RuleParser Exp |
|---|
| 1045 | vcode2startBlock ~(VCode code) = do |
|---|
| 1046 | code' <- vcode2memoized code |
|---|
| 1047 | return $ App (Syn "sub" [Val (VCode code')]) Nothing [] |
|---|
| 1048 | |
|---|
| 1049 | vcode2initBlock :: Val -> RuleParser Exp |
|---|
| 1050 | vcode2initBlock ~(VCode code) = do |
|---|
| 1051 | code' <- vcode2memoized code |
|---|
| 1052 | Val res <- unsafeEvalExp $ |
|---|
| 1053 | App (_Var "&push") (Just $ _Var "@*INIT") [ Val (VCode code') ] |
|---|
| 1054 | return (res `seq` App (Val (VCode code')) Nothing []) |
|---|
| 1055 | |
|---|
| 1056 | vcode2checkBlock :: Val -> RuleParser Exp |
|---|
| 1057 | vcode2checkBlock ~(VCode code) = do |
|---|
| 1058 | code' <- vcode2memoized code |
|---|
| 1059 | Val res <- unsafeEvalExp $ |
|---|
| 1060 | App (_Var "&unshift") (Just $ _Var "@*CHECK") [ Val (VCode code') ] |
|---|
| 1061 | return (res `seq` App (Val (VCode code')) Nothing []) |
|---|
| 1062 | |
|---|
| 1063 | -- Constructs ------------------------------------------------ |
|---|
| 1064 | |
|---|
| 1065 | ruleConstruct :: RuleParser Exp |
|---|
| 1066 | ruleConstruct = rule "construct" $ choice |
|---|
| 1067 | [ ruleForConstruct |
|---|
| 1068 | , ruleLoopConstruct |
|---|
| 1069 | , ruleRepeatConstruct |
|---|
| 1070 | , ruleCondConstruct |
|---|
| 1071 | , ruleWhileUntilConstruct |
|---|
| 1072 | , ruleStandaloneBlock |
|---|
| 1073 | , ruleWhenConstruct |
|---|
| 1074 | , ruleMaybeConstruct |
|---|
| 1075 | , ruleDefaultConstruct |
|---|
| 1076 | , yadaLiteral |
|---|
| 1077 | ] |
|---|
| 1078 | |
|---|
| 1079 | ruleStandaloneBlock :: RuleParser Exp |
|---|
| 1080 | ruleStandaloneBlock = tryVerbatimRule "" $ do |
|---|
| 1081 | body <- ruleBlock |
|---|
| 1082 | whiteSpace |
|---|
| 1083 | lookAhead ((oneOf ";}" >> return ()) <|> eof) |
|---|
| 1084 | retVerbatimBlock SubBlock Nothing False body |
|---|
| 1085 | |
|---|
| 1086 | ruleForConstruct :: RuleParser Exp |
|---|
| 1087 | ruleForConstruct = rule "loop construct" $ do |
|---|
| 1088 | sym <- symbol "for" <|> symbol "given" |
|---|
| 1089 | cond <- ruleCondPart |
|---|
| 1090 | body <- enterBracketLevel ParensBracket $ ruleBlockLiteral |
|---|
| 1091 | return $ Syn sym [cond, body] |
|---|
| 1092 | |
|---|
| 1093 | ruleLoopConstruct :: RuleParser Exp |
|---|
| 1094 | ruleLoopConstruct = rule "loop construct" $ do |
|---|
| 1095 | symbol "loop" |
|---|
| 1096 | choice [ ruleSemiLoopConstruct, ruleBareLoopConstruct ] |
|---|
| 1097 | |
|---|
| 1098 | ruleSemiLoopConstruct :: RuleParser Exp |
|---|
| 1099 | ruleSemiLoopConstruct = rule "for-like loop construct" $ do |
|---|
| 1100 | conds <- parens $ do |
|---|
| 1101 | a <- option emptyExp ruleExpression |
|---|
| 1102 | symbol ";" |
|---|
| 1103 | b <- option emptyExp ruleExpression |
|---|
| 1104 | symbol ";" |
|---|
| 1105 | c <- option emptyExp ruleExpression |
|---|
| 1106 | return [a,b,c] |
|---|
| 1107 | block <- retBlockWithoutDefaultParams SubBlock Nothing False =<< ruleBlock |
|---|
| 1108 | return $ Syn "loop" (conds ++ [block]) |
|---|
| 1109 | |
|---|
| 1110 | ruleBareLoopConstruct :: RuleParser Exp |
|---|
| 1111 | ruleBareLoopConstruct = rule "for-like loop construct" $ do |
|---|
| 1112 | block <- ruleBareBlock |
|---|
| 1113 | return $ Syn "loop" [block] |
|---|
| 1114 | |
|---|
| 1115 | ruleBareBlock :: RuleParser Exp |
|---|
| 1116 | ruleBareBlock = do |
|---|
| 1117 | retBlockWithoutDefaultParams SubBlock Nothing False =<< ruleBlock |
|---|
| 1118 | |
|---|
| 1119 | ruleRepeatConstruct :: RuleParser Exp |
|---|
| 1120 | ruleRepeatConstruct = rule "postfix loop construct" $ do |
|---|
| 1121 | symbol "repeat" |
|---|
| 1122 | choice [ ruleRepeatPostConstruct, ruleRepeatPreConstruct ] |
|---|
| 1123 | |
|---|
| 1124 | ruleRepeatPostConstruct :: RuleParser Exp |
|---|
| 1125 | ruleRepeatPostConstruct = rule "repeat postfix construct" $ do |
|---|
| 1126 | block <- enterBracketLevel ParensBracket $ ruleBareOrPointyBlockLiteralWithoutDefaultParams |
|---|
| 1127 | name <- choice [ symbol "while", symbol "until" ] |
|---|
| 1128 | cond <- ruleExpression |
|---|
| 1129 | return $ Syn ("post" ++ name) [cond, block] |
|---|
| 1130 | |
|---|
| 1131 | ruleRepeatPreConstruct :: RuleParser Exp |
|---|
| 1132 | ruleRepeatPreConstruct = rule "repeat prefix construct" $ do |
|---|
| 1133 | name <- choice [ symbol "while", symbol "until" ] |
|---|
| 1134 | cond <- ruleCondPart |
|---|
| 1135 | block <- enterBracketLevel ParensBracket $ ruleBareOrPointyBlockLiteralWithoutDefaultParams |
|---|
| 1136 | return $ Syn ("post" ++ name) [ cond, block ] |
|---|
| 1137 | |
|---|
| 1138 | ruleCondConstruct :: RuleParser Exp |
|---|
| 1139 | ruleCondConstruct = rule "conditional construct" $ do |
|---|
| 1140 | csym <- choice [ symbol "if", symbol "unless" ] |
|---|
| 1141 | ruleCondBody $ csym |
|---|
| 1142 | |
|---|
| 1143 | ruleCondBody :: String -> RuleParser Exp |
|---|
| 1144 | ruleCondBody csym = rule "conditional expression" $ do |
|---|
| 1145 | cond <- ruleCondPart |
|---|
| 1146 | enterBracketLevel ParensBracket $ do |
|---|
| 1147 | body <- ruleBareOrPointyBlockLiteralWithoutDefaultParams |
|---|
| 1148 | bodyElse <- option emptyExp ruleElseConstruct |
|---|
| 1149 | case csym of |
|---|
| 1150 | "if" -> return $ Syn "cond" [cond, body, bodyElse] |
|---|
| 1151 | _ -> if bodyElse == emptyExp then |
|---|
| 1152 | return $ Syn "cond" [cond, bodyElse, body] |
|---|
| 1153 | else fail "no else after unless" |
|---|
| 1154 | |
|---|
| 1155 | |
|---|
| 1156 | ruleCondPart :: RuleParser Exp |
|---|
| 1157 | ruleCondPart = enterBracketLevel ConditionalBracket ruleExpression |
|---|
| 1158 | |
|---|
| 1159 | ruleMaybeConstruct :: RuleParser Exp |
|---|
| 1160 | ruleMaybeConstruct = rule "maybe construct" $ do |
|---|
| 1161 | symbol "maybe" |
|---|
| 1162 | blocks <- enterBracketLevel ParensBracket (ruleBareBlock `sepBy` symbol "maybe") |
|---|
| 1163 | return (Syn "maybe" blocks) |
|---|
| 1164 | |
|---|
| 1165 | ruleElseConstruct :: RuleParser Exp |
|---|
| 1166 | ruleElseConstruct = rule "else or elsif construct" $ |
|---|
| 1167 | do |
|---|
| 1168 | symbol "else" |
|---|
| 1169 | ruleBareOrPointyBlockLiteralWithoutDefaultParams |
|---|
| 1170 | <|> do |
|---|
| 1171 | symbol "elsif" |
|---|
| 1172 | body <- ruleCondBody "if" |
|---|
| 1173 | return (Syn "block" [body]) |
|---|
| 1174 | |
|---|
| 1175 | ruleWhileUntilConstruct :: RuleParser Exp |
|---|
| 1176 | ruleWhileUntilConstruct = rule "while/until construct" $ do |
|---|
| 1177 | sym <- choice [ symbol "while", symbol "until" ] |
|---|
| 1178 | cond <- ruleCondPart |
|---|
| 1179 | body <- enterBracketLevel ParensBracket $ ruleBareOrPointyBlockLiteralWithoutDefaultParams |
|---|
| 1180 | return $ Syn sym [ cond, body ] |
|---|
| 1181 | |
|---|
| 1182 | ruleWhenConstruct :: RuleParser Exp |
|---|
| 1183 | ruleWhenConstruct = rule "when construct" $ do |
|---|
| 1184 | sym <- symbol "when" |
|---|
| 1185 | match <- ruleCondPart |
|---|
| 1186 | body <- ruleBareBlock |
|---|
| 1187 | return $ Syn sym [ match, body ] |
|---|
| 1188 | |
|---|
| 1189 | -- XXX: make this translate into when true, when smartmatch |
|---|
| 1190 | -- against true works |
|---|
| 1191 | ruleDefaultConstruct :: RuleParser Exp |
|---|
| 1192 | ruleDefaultConstruct = rule "default construct" $ do |
|---|
| 1193 | sym <- symbol "default" |
|---|
| 1194 | body <- ruleBareBlock |
|---|
| 1195 | return $ Syn sym [ body ] |
|---|
| 1196 | |
|---|
| 1197 | -- Expressions ------------------------------------------------ |
|---|
| 1198 | |
|---|
| 1199 | ruleExpression :: RuleParser Exp |
|---|
| 1200 | ruleExpression = (<?> "expression") $ parseExpWithOps |
|---|
| 1201 | |
|---|
| 1202 | {-| |
|---|
| 1203 | Match a statement's /conditional/ statement-modifier, |
|---|
| 1204 | e.g. '@say \"hello\" if \$cheerful@' or '@die unless +\@arguments@'. |
|---|
| 1205 | |
|---|
| 1206 | Returns a function that will take the statement proper, and enclose it in an |
|---|
| 1207 | appropriate 'Pugs.AST.Internals.Syn' (either @\"if\"@ or @\"unless\"@). |
|---|
| 1208 | -} |
|---|
| 1209 | s_postConditional :: RuleParser (Exp -> RuleParser Exp) |
|---|
| 1210 | s_postConditional = rule "postfix conditional" $ do |
|---|
| 1211 | cond <- choice $ map symbol ["if", "unless"] |
|---|
| 1212 | exp <- ruleExpression |
|---|
| 1213 | return $ \body -> return $ case cond of |
|---|
| 1214 | "if" -> Syn "cond" [exp, Syn "block" [body], Syn "block" [emptyExp]] |
|---|
| 1215 | _ -> Syn "cond" [exp, Syn "block" [emptyExp], Syn "block" [body]] |
|---|
| 1216 | |
|---|
| 1217 | {-| |
|---|
| 1218 | Match a statement's /looping/ statement-modifier, |
|---|
| 1219 | e.g. '@procrastinate while $bored@' or '@eat until $full@'. |
|---|
| 1220 | |
|---|
| 1221 | Returns a function that will take the statement proper, and enclose it in an |
|---|
| 1222 | appropriate 'Pugs.AST.Internals.Syn' (either @\"while\"@ or @\"until\"@). |
|---|
| 1223 | -} |
|---|
| 1224 | s_postLoop :: RuleParser (Exp -> RuleParser Exp) |
|---|
| 1225 | s_postLoop = rule "postfix loop" $ do |
|---|
| 1226 | cond <- choice $ map symbol ["while", "until"] |
|---|
| 1227 | exp <- ruleExpression |
|---|
| 1228 | return $ \body -> do |
|---|