|
29 | 29 |
|
30 | 30 | module CCC.Compiler |
31 | 31 | ( compileNumExpr, |
32 | | - compileNumExprNaive, |
33 | 32 | compileEnvironment, |
34 | 33 | tryCompileVar, |
35 | | - compileNumericBindings |
| 34 | + compileNumericBindings, |
| 35 | + -- * Internal: shared with CCC.CompilerNaive |
| 36 | + RVal (..), |
| 37 | + IntArgs (..), |
| 38 | + SomeIntArgs (..), |
| 39 | + compileRecBuiltin, |
| 40 | + collectLams, |
| 41 | + mkIntArgs, |
| 42 | + buildRecFun, |
| 43 | + argProjections, |
| 44 | + tupleFromExprs, |
| 45 | + expectRInt, |
36 | 46 | ) where |
37 | 47 |
|
38 | 48 | import CCC.CatExpr (CatExpr (..)) |
@@ -246,21 +256,7 @@ compileRecExpr env outerLocal local = \case |
246 | 256 | applyRVal _ _ = Left "Cannot apply non-function value" |
247 | 257 |
|
248 | 258 | compileRecBuiltin :: String -> Maybe (RVal c) |
249 | | -compileRecBuiltin "+" = Just (rIntBin Add) |
250 | | -compileRecBuiltin "-" = Just (rIntBin Sub) |
251 | | -compileRecBuiltin "*" = Just (rIntBin Mul) |
252 | | -compileRecBuiltin "sub" = Just (rIntBin Sub) |
253 | | -compileRecBuiltin "sub1" = Just (rIntUnary (\x -> Comp Sub (fanC x (IntConst 1)))) |
254 | | -compileRecBuiltin "is0" = Just (rIntToSel (\x -> Comp Eql (fanC x (IntConst 0)))) |
255 | | -compileRecBuiltin "eql" = Just (rIntCmpSel Eql) |
256 | | -compileRecBuiltin "leq" = Just (rIntCmpSel Leq) |
257 | | -compileRecBuiltin "geq" = Just (rIntCmpSel Geq) |
258 | | --- Scott-encoded booleans: TRUE = Snd (select second), FALSE = Fst (select first) |
259 | | -compileRecBuiltin "true" = Just (rSelConst Snd) |
260 | | -compileRecBuiltin "false" = Just (rSelConst Fst) |
261 | | --- if selector thenVal elseVal = Apply ∘ ⟨selector, ⟨thenVal, elseVal⟩⟩ |
262 | | -compileRecBuiltin "if" = Just rIfFun |
263 | | -compileRecBuiltin _ = Nothing |
| 259 | +compileRecBuiltin name = builtinToRVal <$> lookupBuiltin name |
264 | 260 |
|
265 | 261 | rIntUnary :: (CatExpr c Integer -> CatExpr c Integer) -> RVal c |
266 | 262 | rIntUnary op = RFun $ \case |
@@ -306,6 +302,48 @@ rIfFun = RFun $ \case |
306 | 302 | _ -> Left "if: then branch must be integer" |
307 | 303 | _ -> Left "if: condition must be a Scott boolean (selector)" |
308 | 304 |
|
| 305 | +-- Builtin descriptor: captures the shape of each primitive operation once. |
| 306 | +-- Interpreted into both SVal (NBE) and RVal (direct) domains by |
| 307 | +-- builtinToSVal and builtinToRVal respectively. |
| 308 | +data Builtin where |
| 309 | + BinOp :: CatExpr (Integer, Integer) Integer -> Builtin |
| 310 | + UnaryOp :: (forall c. CatExpr c Integer -> CatExpr c Integer) -> Builtin |
| 311 | + Predicate :: (forall c. CatExpr c Integer -> CatExpr c (CatExpr (Integer, Integer) Integer)) -> Builtin |
| 312 | + CmpOp :: (forall b. CatExpr (Integer, Integer) (CatExpr (b, b) b)) -> Builtin |
| 313 | + SelConst :: CatExpr (Integer, Integer) Integer -> Builtin |
| 314 | + IfOp :: Builtin |
| 315 | + |
| 316 | +lookupBuiltin :: String -> Maybe Builtin |
| 317 | +lookupBuiltin "+" = Just (BinOp Add) |
| 318 | +lookupBuiltin "-" = Just (BinOp Sub) |
| 319 | +lookupBuiltin "*" = Just (BinOp Mul) |
| 320 | +lookupBuiltin "sub" = Just (BinOp Sub) |
| 321 | +lookupBuiltin "sub1" = Just (UnaryOp (\x -> Comp Sub (fanC x (IntConst 1)))) |
| 322 | +lookupBuiltin "is0" = Just (Predicate (\x -> Comp Eql (fanC x (IntConst 0)))) |
| 323 | +lookupBuiltin "eql" = Just (CmpOp Eql) |
| 324 | +lookupBuiltin "leq" = Just (CmpOp Leq) |
| 325 | +lookupBuiltin "geq" = Just (CmpOp Geq) |
| 326 | +lookupBuiltin "true" = Just (SelConst Snd) |
| 327 | +lookupBuiltin "false" = Just (SelConst Fst) |
| 328 | +lookupBuiltin "if" = Just IfOp |
| 329 | +lookupBuiltin _ = Nothing |
| 330 | + |
| 331 | +builtinToRVal :: Builtin -> RVal c |
| 332 | +builtinToRVal (BinOp op) = rIntBin op |
| 333 | +builtinToRVal (UnaryOp op) = rIntUnary op |
| 334 | +builtinToRVal (Predicate p) = rIntToSel p |
| 335 | +builtinToRVal (CmpOp op) = rIntCmpSel op |
| 336 | +builtinToRVal (SelConst sel) = rSelConst sel |
| 337 | +builtinToRVal IfOp = rIfFun |
| 338 | + |
| 339 | +builtinToSVal :: Builtin -> SVal |
| 340 | +builtinToSVal (BinOp op) = sIntBinOp op |
| 341 | +builtinToSVal (UnaryOp op) = sIntUnaryOp op |
| 342 | +builtinToSVal (Predicate p) = sIntToSel p |
| 343 | +builtinToSVal (CmpOp op) = sIntCmpSel op |
| 344 | +builtinToSVal (SelConst sel) = SSel (ClosedSel (Lift (const sel))) |
| 345 | +builtinToSVal IfOp = sIfFun |
| 346 | + |
309 | 347 | sValToRVal :: SVal -> Maybe (RVal c) |
310 | 348 | sValToRVal (SInt (Closed c)) = Just (RInt c) |
311 | 349 | sValToRVal (SSel (ClosedSel c)) = Just (RSel c) |
@@ -381,23 +419,12 @@ collectLams = go [] |
381 | 419 | go params expr = (params, expr) |
382 | 420 |
|
383 | 421 | compileBuiltin :: String -> Either String SVal |
384 | | -compileBuiltin "+" = Right (sIntBinOp Add) |
385 | | -compileBuiltin "-" = Right (sIntBinOp Sub) |
386 | | -compileBuiltin "*" = Right (sIntBinOp Mul) |
387 | | -compileBuiltin "sub" = Right (sIntBinOp Sub) |
388 | | -compileBuiltin "sub1" = Right (sIntUnaryOp (\x -> Comp Sub (fanC x (IntConst 1)))) |
389 | | -compileBuiltin "is0" = Right (sIntToSel (\x -> Comp Eql (fanC x (IntConst 0)))) |
390 | | -compileBuiltin "eql" = Right (sIntCmpSel Eql) |
391 | | -compileBuiltin "leq" = Right (sIntCmpSel Leq) |
392 | | -compileBuiltin "geq" = Right (sIntCmpSel Geq) |
393 | | -compileBuiltin "if" = Right sIfFun |
394 | | --- Scott-encoded booleans: TRUE = Snd (select second), FALSE = Fst (select first) |
395 | | -compileBuiltin "true" = Right (SSel (ClosedSel (Lift (const Snd)))) |
396 | | -compileBuiltin "false" = Right (SSel (ClosedSel (Lift (const Fst)))) |
397 | | -compileBuiltin "y" = Left "Recursion via fixpoint operators is structurally compiled" |
| 422 | +compileBuiltin name |
| 423 | + | Just b <- lookupBuiltin name = Right (builtinToSVal b) |
| 424 | +compileBuiltin "y" = Left "Recursion via fixpoint operators is structurally compiled" |
398 | 425 | compileBuiltin "fix" = Left "Recursion via fixpoint operators is structurally compiled" |
399 | | -compileBuiltin "/" = Left "Division is not currently supported in CatExpr compilation" |
400 | | -compileBuiltin name = Left ("Unbound variable: " ++ name) |
| 426 | +compileBuiltin "/" = Left "Division is not currently supported in CatExpr compilation" |
| 427 | +compileBuiltin name = Left ("Unbound variable: " ++ name) |
401 | 428 |
|
402 | 429 | sIntUnaryOp :: (forall z. CatExpr z Integer -> CatExpr z Integer) -> SVal |
403 | 430 | sIntUnaryOp op = SFun $ \case |
@@ -439,116 +466,6 @@ sIfFun = SFun $ \case |
439 | 466 | _ -> Left "if: then branch must be integer" |
440 | 467 | _ -> Left "if: condition must be a Scott boolean (selector)" |
441 | 468 |
|
442 | | --- --------------------------------------------------------------------------- |
443 | | --- Naive (explicit CatExpr) compilation — no NBE |
444 | | --- --------------------------------------------------------------------------- |
445 | | - |
446 | | --- | Naive CCC compilation: builds explicit CatExpr nodes directly. |
447 | | --- Unlike compileNumExpr (which uses NBE with Haskell closures for |
448 | | --- beta-reduction), this compiles into the RVal domain from a unit |
449 | | --- context, producing explicit Comp/fanC/Apply nodes at every step. |
450 | | --- |
451 | | --- For first-order integer programs (no higher-order function passing), |
452 | | --- the output is identical to the NBE version — NBE only gains an |
453 | | --- advantage for higher-order terms where it can beta-reduce at compile time. |
454 | | -compileNumExprNaive :: Environment -> Expr -> CatExpr () Integer |
455 | | -compileNumExprNaive env expr = |
456 | | - case compileNaive env [] expr of |
457 | | - Right (RInt e) -> e |
458 | | - Right _ -> error "Naive compilation: expected integer result" |
459 | | - Left err -> error ("Naive compilation failed: " ++ err) |
460 | | - |
461 | | --- Direct CatExpr compilation (no NBE). |
462 | | --- Uses the same RVal machinery as compileRecExpr but starts from any context. |
463 | | --- Builtins are RFun closures (primitives), user-level code builds explicit nodes. |
464 | | --- |
465 | | --- Key difference from compileExpr (NBE): |
466 | | --- NBE: Lam → Haskell closure; App → Haskell application (beta-reduces) |
467 | | --- Naive: Lam → RFun closure; App → RFun application (same node building) |
468 | | --- For first-order programs, both produce identical CatExpr output because |
469 | | --- builtins build the same Comp/fanC nodes. The difference appears only |
470 | | --- with higher-order terms where NBE can beta-reduce at compile time. |
471 | | -compileNaive :: forall c. Environment -> [(String, RVal c)] -> Expr -> Either String (RVal c) |
472 | | -compileNaive env local = \case |
473 | | - Int i -> Right (RInt (IntConst i)) |
474 | | - Var name -> |
475 | | - case lookup name local of |
476 | | - Just v -> Right v |
477 | | - Nothing -> compileNaiveVar name |
478 | | - App fixHead stepExpr |
479 | | - | isNaiveFixOp env local fixHead -> compileNaiveFix env stepExpr |
480 | | - Lam param body -> |
481 | | - Right (RFun (\arg -> compileNaive env ((param, arg) : local) body)) |
482 | | - App f x -> do |
483 | | - fVal <- compileNaive env local f |
484 | | - xVal <- compileNaive env local x |
485 | | - applyNaiveVal fVal xVal |
486 | | - where |
487 | | - compileNaiveVar name = |
488 | | - case lookup name env of |
489 | | - Just expr -> compileNaive env local expr |
490 | | - Nothing -> case compileRecBuiltin name of |
491 | | - Just v -> Right v |
492 | | - Nothing -> Left ("Unbound variable: " ++ name) |
493 | | - |
494 | | - applyNaiveVal (RFun fn) x = fn x |
495 | | - applyNaiveVal _ _ = Left "Cannot apply non-function value" |
496 | | - |
497 | | -isNaiveFixOp :: Environment -> [(String, RVal c)] -> Expr -> Bool |
498 | | -isNaiveFixOp env local = go [] |
499 | | - where |
500 | | - go _ (Int _) = False |
501 | | - go _ (Lam _ _) = False |
502 | | - go _ (App _ _) = False |
503 | | - go seen (Var name) |
504 | | - | name `elem` map fst local = False |
505 | | - | name == "y" || name == "fix" = True |
506 | | - | name `elem` seen = False |
507 | | - | otherwise = |
508 | | - case lookup name env of |
509 | | - Just expr -> go (name : seen) expr |
510 | | - Nothing -> False |
511 | | - |
512 | | -compileNaiveFix :: forall c. Environment -> Expr -> Either String (RVal c) |
513 | | -compileNaiveFix env = \case |
514 | | - Lam fName stepExpr -> |
515 | | - case collectLams stepExpr of |
516 | | - (params, body) -> |
517 | | - case mkIntArgs (length params) of |
518 | | - Just (SomeIntArgs args) -> compileNaiveFixGeneric env args fName params body |
519 | | - Nothing -> Left "fix expects at least one integer argument" |
520 | | - _ -> Left "fix expects a lambda step function" |
521 | | - |
522 | | -compileNaiveFixGeneric :: |
523 | | - forall c input. |
524 | | - Environment -> |
525 | | - IntArgs input -> |
526 | | - String -> |
527 | | - [String] -> |
528 | | - Expr -> |
529 | | - Either String (RVal c) |
530 | | -compileNaiveFixGeneric env args fName params body = buildCurried args [] |
531 | | - where |
532 | | - buildCurried :: IntArgs remaining -> [CatExpr c Integer] -> Either String (RVal c) |
533 | | - buildCurried OneArg acc = |
534 | | - Right $ RFun $ \case |
535 | | - RInt arg -> applyNaiveFix (acc ++ [arg]) |
536 | | - _ -> Left "fix expects Integer argument" |
537 | | - buildCurried (MoreArgs rest) acc = |
538 | | - Right $ RFun $ \case |
539 | | - RInt arg -> buildCurried rest (acc ++ [arg]) |
540 | | - _ -> Left "fix expects Integer argument" |
541 | | - |
542 | | - applyNaiveFix :: [CatExpr c Integer] -> Either String (RVal c) |
543 | | - applyNaiveFix actualArgs = do |
544 | | - paramTuple <- tupleFromExprs args actualArgs |
545 | | - recFun <- buildRecFun args |
546 | | - let fixLocal = |
547 | | - (fName, recFun) : |
548 | | - zipWith (\name proj -> (name, RInt proj)) params (argProjections args Snd) |
549 | | - stepBody <- compileNaive env fixLocal body >>= expectRInt "Recursive body must compile to Integer" |
550 | | - Right (RInt (Comp (Fix stepBody) paramTuple)) |
551 | | - |
552 | 469 | -- | Compile an expression, extracting environment variables to morphisms. |
553 | 470 | -- Returns a list of (name, morphism_string_representation) for inspection. |
554 | 471 | compileEnvironment :: Environment -> [(String, String)] |
|
0 commit comments