| 161 | | ruleSubHead :: RuleParser (Bool, SubType, String) |
| 162 | | ruleSubHead = rule "subroutine head" $ do |
| 163 | | isMulti <- option False $ do { symbol "multi" ; return True } |
| 164 | | -- You're allowed to omit the "sub": |
| 165 | | -- multi sub foo (...) {...} # legal |
| 166 | | -- sub foo (...) {...} # legal, too |
| 167 | | let implicitSub | isMulti == True = return SubRoutine |
| 168 | | | otherwise = pzero |
| 169 | | styp <- choice |
| 170 | | [ do symbol "sub" |
| 171 | | return SubRoutine |
| 172 | | , do symbol "coro" |
| 173 | | return SubCoroutine |
| 174 | | , do (symbol "submethod" <|> symbol "method") |
| 175 | | return SubMethod |
| 176 | | , do symbol "macro" |
| 177 | | return SubMacro |
| 178 | | ] <|> implicitSub |
| 179 | | name <- ruleSubName |
| 180 | | return (isMulti, styp, name) |
| 181 | | |
| 182 | | -- | Scope, context, isMulti, styp, name |
| 183 | | type SubDescription = (Scope, String, Bool, SubType, String) |
| 184 | | |
| 185 | | ruleSubScopedWithContext :: RuleParser SubDescription |
| 186 | | ruleSubScopedWithContext = tryRule "scoped subroutine with context" $ do |
| 187 | | scope <- ruleScope |
| 188 | | cxt <- identifier |
| 189 | | (isMulti, styp, name) <- ruleSubHead |
| 190 | | return (scope, cxt, isMulti, styp, name) |
| 191 | | |
| 192 | | ruleSubScoped :: RuleParser SubDescription |
| 193 | | ruleSubScoped = tryRule "scoped subroutine" $ do |
| 194 | | scope <- ruleScope |
| 195 | | (isMulti, styp, name) <- ruleSubHead |
| 196 | | return (scope, "Any", isMulti, styp, name) |
| 197 | | |
| 198 | | ruleSubGlobal :: RuleParser SubDescription |
| 199 | | ruleSubGlobal = tryRule "global subroutine" $ do |
| 200 | | (isMulti, styp, name) <- ruleSubHead |
| 201 | | return (SGlobal, "Any", isMulti, styp, name) |
| 202 | | |
| 276 | | |
| 277 | | ruleSubDeclaration :: RuleParser Exp |
| 278 | | ruleSubDeclaration = rule "subroutine declaration" $ do |
| 279 | | namePos <- getPosition |
| 280 | | (scope, typ, isMulti, styp, name) <- choice |
| 281 | | [ ruleSubScopedWithContext |
| 282 | | , ruleSubScoped |
| 283 | | , ruleSubGlobal |
| 284 | | ] |
| 285 | | optional $ do { symbol "handles"; ruleExpression } |
| 286 | | typ' <- option typ $ try $ ruleBareTrait "returns" |
| 287 | | formal <- option Nothing $ ruleSubParameters ParensMandatory |
| 288 | | typ'' <- option typ' $ try $ ruleBareTrait "returns" |
| 289 | | traits <- many $ ruleTrait |
| 290 | | |
| 291 | | -- XXX - We have the prototype now; install it immediately? |
| 292 | | |
| 293 | | -- bodyPos <- getPosition |
| 294 | | body <- ruleBlockWithParams (maybe [] id formal) |
| 295 | | let (fun, names, params) = doExtract styp formal body |
| 296 | | -- Check for placeholder vs formal parameters |
| 297 | | when (isJust formal && (not.null) names) $ |
| 298 | | fail "Cannot mix placeholder variables with formal parameters" |
| 299 | | env <- ask |
| 300 | | let sub = VCode $ MkCode |
| 301 | | { isMulti = isMulti |
| 302 | | , subName = nameQualified |
| 303 | | , subEnv = Just env |
| 304 | | , subType = if "primitive" `elem` traits |
| 305 | | then SubPrim else styp |
| 306 | | , subAssoc = "pre" |
| 307 | | , subReturns = mkType typ'' |
| 308 | | , subLValue = "rw" `elem` traits |
| 309 | | , subParams = self ++ paramsFor styp formal params |
| 310 | | , subBindings = [] |
| 311 | | , subSlurpLimit = [] |
| 312 | | , subBody = fun |
| 313 | | , subCont = Nothing |
| 314 | | } |
| 315 | | pkg = envPackage env |
| 316 | | nameQualified | ':' `elem` name = name |
| 317 | | | scope <= SMy = name |
| 318 | | | isGlobal = name |
| 319 | | | isBuiltin = (head name:'*':tail name) |
| 320 | | | otherwise = (head name:pkg) ++ "::" ++ tail name |
| 321 | | self :: [Param] |
| 322 | | self | styp > SubMethod = [] |
| 323 | | | (prm:_) <- params, isInvocant prm = [] |
| 324 | | | otherwise = [selfParam $ envPackage env] |
| 325 | | mkExp n = Syn ":=" [Var n, Syn "sub" [Val sub]] |
| 326 | | mkSym n = Sym scope (mkMulti n) (mkExp n) |
| 327 | | mkSymGlobal globalName lexicalName = Stmts |
| 328 | | (Sym SMy (mkMulti lexicalName) (mkExp lexicalName)) |
| 329 | | (Sym SGlobal (mkMulti globalName) (Syn ":=" [Var globalName, Var lexicalName])) |
| 330 | | -- Horrible hack! Sym "&&" is the multi form. |
| 331 | | mkMulti | isMulti = ('&':) |
| 332 | | | otherwise = id |
| 333 | | isGlobal = '*' `elem` name |
| 334 | | isBuiltin = ("builtin" `elem` traits) |
| 335 | | isExported = ("export" `elem` traits) |
| 336 | | isScopeGlobal = case scope of |
| 337 | | SGlobal -> True |
| 338 | | SOur -> True |
| 339 | | _ -> False |
| 340 | | expMakeSym |
| 341 | | | isScopeGlobal = mkSymGlobal nameQualified name |
| 342 | | | otherwise = mkSym nameQualified |
| 343 | | expImmediate |
| 344 | | | isExported = Stmts expMakeSym $ |
| 345 | | (App (Var "&push") |
| 346 | | (Just (Syn "{}" [Var ("%" ++ pkg ++ "::EXPORTS"), Val $ VStr name])) |
| 347 | | [Var name]) |
| 348 | | | otherwise = expMakeSym |
| 349 | | |
| 350 | | -- XXX this belongs in semantic analysis, not in the parser |
| 351 | | -- also, maybe we should only warn when you try to export an |
| 352 | | -- operator that is "standard" |
| 353 | | -- XXX I can't figure out how to do this without trace |
| 354 | | when (isExported && isOperatorName name) $ |
| 355 | | trace |
| 356 | | ("You probably don't want to export an operator name; instead\n\ |
| 357 | | define a new variant on the new operator (eg. multi sub *infix:<+>): " |
| 358 | | ++ show name ++ " at " ++ show namePos) |
| 359 | | (return ()) |
| 360 | | |
| 361 | | -- Don't add the sub if it's unsafe and we're in safemode. |
| 362 | | if "unsafe" `elem` traits && safeMode then return emptyExp else do |
| 363 | | lexDiff <- unsafeEvalLexDiff expImmediate |
| 364 | | addBlockPad SMy lexDiff |
| 365 | | clearDynParsers |
| 366 | | return (Var name) |
| 1185 | | retBlock typ formal lvalue body |
| 1186 | | |
| 1187 | | retBlock :: SubType -> Maybe [Param] -> Bool -> Exp -> RuleParser Exp |
| 1188 | | retBlock SubBlock Nothing _ exp | Just hashExp <- extractHash (unwrap exp) = return $ Syn "\\{}" [hashExp] |
| 1189 | | retBlock typ formal lvalue body = retVerbatimBlock typ formal lvalue body |
| 1190 | | |
| 1191 | | retVerbatimBlock :: SubType -> Maybe [Param] -> Bool -> Exp -> RuleParser Exp |
| 1192 | | retVerbatimBlock styp formal lvalue body = expRule $ do |
| | 1052 | rv <- retBlock typ maybeName formal traits body |
| | 1053 | case maybeName of |
| | 1054 | Just name -> registerSubName isMulti SOur name traits rv |
| | 1055 | _ -> return rv |
| | 1056 | |
| | 1057 | registerSubName :: Bool -> Scope -> Var -> Traits -> Exp -> RuleParser Exp |
| | 1058 | registerSubName isMulti scope name traits exp = do |
| | 1059 | -- Don't add the sub if it's unsafe and we're in safemode. |
| | 1060 | if "unsafe" `elem` traits && safeMode then return emptyExp else do |
| | 1061 | lexDiff <- unsafeEvalLexDiff $ |
| | 1062 | Sym scope ((if isMulti then ('&':) else id) name) emptyExp |
| | 1063 | addBlockPad scope lexDiff |
| | 1064 | rv <- unsafeEvalExp $! Syn ":=" [Var name, exp] |
| | 1065 | pkg <- asks envPackage |
| | 1066 | when (scope == SOur || scope == SGlobal) $ do |
| | 1067 | let qualifiedName = (head name:pkg) ++ (':':':':tail name) |
| | 1068 | unsafeEvalExp $! Syn ":=" [Var qualifiedName, rv] |
| | 1069 | return () |
| | 1070 | when ("export" `elem` traits) $ do |
| | 1071 | when (isOperatorName name) $ do |
| | 1072 | namePos <- getPosition |
| | 1073 | trace ("You probably don't want to export an operator name; instead\n\ |
| | 1074 | define a new variant on the new operator (eg. multi sub *infix:<+>): " |
| | 1075 | ++ show name ++ " at " ++ show namePos) |
| | 1076 | (return ()) |
| | 1077 | unsafeEvalExp $! |
| | 1078 | App (Var "&push") |
| | 1079 | (Just (Syn "{}" [Var ("%" ++ pkg ++ "::EXPORTS"), Val $ VStr name])) |
| | 1080 | [rv] |
| | 1081 | return () |
| | 1082 | -- Now handle exports and push the sub into namedness |
| | 1083 | return rv |
| | 1084 | |
| | 1085 | retBlock :: SubType -> Maybe Var -> Maybe [Param] -> Traits -> Exp -> RuleParser Exp |
| | 1086 | retBlock SubBlock _ Nothing _ exp | Just hashExp <- extractHash (unwrap exp) = return $ Syn "\\{}" [hashExp] |
| | 1087 | retBlock typ maybeName formal traits body = retVerbatimBlock typ maybeName formal traits body |
| | 1088 | |
| | 1089 | retVerbatimBlock :: SubType -> Maybe Var -> Maybe [Param] -> Traits -> Exp -> RuleParser Exp |
| | 1090 | retVerbatimBlock styp maybeName formal traits body = expRule $ do |
| 1214 | | ruleBlockFormalStandard :: RuleParser (SubType, Maybe [Param], Bool) |
| 1215 | | ruleBlockFormalStandard = rule "standard block parameters" $ do |
| 1216 | | styp <- choice |
| 1217 | | [ do { symbol "sub"; return SubRoutine } |
| 1218 | | , do { symbol "coro"; return SubCoroutine } |
| 1219 | | , do { symbol "macro"; return SubMacro } |
| 1220 | | ] |
| | 1123 | type Traits = [String] |
| | 1124 | |
| | 1125 | type IsMulti = Bool |
| | 1126 | type MaybeName = Maybe Var |
| | 1127 | type SubDescription = (IsMulti, SubType, MaybeName, Maybe [Param], Traits) |
| | 1128 | |
| | 1129 | ruleSubHead :: RuleParser SubDescription |
| | 1130 | ruleSubHead = rule "subroutine head" $ do |
| | 1131 | isMulti <- option False $ do { symbol "multi" ; return True } |
| | 1132 | -- You're allowed to omit the "sub": |
| | 1133 | -- multi sub foo (...) {...} # legal |
| | 1134 | -- sub foo (...) {...} # legal, too |
| | 1135 | let implicitSub | isMulti == True = return SubRoutine |
| | 1136 | | otherwise = pzero |
| | 1137 | styp <- choice |
| | 1138 | [ do symbol "sub" |
| | 1139 | return SubRoutine |
| | 1140 | , do symbol "coro" |
| | 1141 | return SubCoroutine |
| | 1142 | , do (symbol "submethod" <|> symbol "method") |
| | 1143 | return SubMethod |
| | 1144 | , do symbol "macro" |
| | 1145 | return SubMacro |
| | 1146 | ] <|> implicitSub |
| | 1147 | name <- option Nothing $ fmap Just (lexeme ruleSubName) |
| 1274 | | (cxtNames, exp) <- oneDecl <|> manyDecl |
| 1275 | | let makeBinding (nam, cxt) |
| 1276 | | | typ == anyType = mkSym |
| 1277 | | | otherwise = mkSym . bindSym |
| 1278 | | where |
| 1279 | | mkSym = Sym scope nam |
| 1280 | | bindSym = Stmts (Syn "=" [Var nam, Val (VType typ)]) |
| 1281 | | typ = typeOfCxt cxt |
| 1282 | | lexDiff <- unsafeEvalLexDiff $ combine (map makeBinding cxtNames) emptyExp |
| 1283 | | -- Now hoist the lexDiff to the current block |
| 1284 | | addBlockPad scope lexDiff |
| 1285 | | return exp |
| | 1213 | (ruleSubHead >>= ruleScopedRoutine scope) <|> do |
| | 1214 | (cxtNames, exp) <- oneDecl <|> manyDecl |
| | 1215 | let makeBinding (name, cxt) |
| | 1216 | | ('$':_) <- name, typ /= anyType = mkSym . bindSym |
| | 1217 | | otherwise = mkSym |
| | 1218 | where |
| | 1219 | mkSym = Sym scope name |
| | 1220 | bindSym = Stmts (Syn "=" [Var name, Val (VType typ)]) |
| | 1221 | typ = typeOfCxt cxt |
| | 1222 | lexDiff <- unsafeEvalLexDiff $ combine (map makeBinding cxtNames) emptyExp |
| | 1223 | -- Now hoist the lexDiff to the current block |
| | 1224 | addBlockPad scope lexDiff |
| | 1225 | return exp |