Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/Language/PureScript/Backend/Lua.hs
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,13 @@ fromIR foreigns topLevelNames modname ir = case ir of
Lua.functionCall (Lua.varName Fixture.objectUpdateName) [obj, vals]
-- See Note [Nullary functions and Prim.undefined]
IR.Abs _ann param expr → do
egoExp expr
bodygo expr
let luaParams = case param of
IR.ParamUnused _ann → []
IR.ParamNamed _ann name → [ParamNamed (fromName name)]
pure . Right $ Lua.functionDef luaParams [Lua.return e]
pure . Right $ case body of
Left chunk → Lua.functionDef luaParams chunk
Right e → Lua.functionDef luaParams [Lua.return e]
IR.App _ann expr arg → do
e ← goExp expr
Right . Lua.functionCall e <$> case arg of
Expand Down
11 changes: 1 addition & 10 deletions lib/Language/PureScript/Backend/Lua/Optimizer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ optimizeExpression = foldr (>>>) identity rewriteRulesInOrder

rewriteRulesInOrder ∷ [RewriteRule]
rewriteRulesInOrder =
[ removeScopeWhenInsideEmptyFunction
, reduceTableDefinitionAccessor
[ reduceTableDefinitionAccessor
, foldFieldProjectionThroughScopeCall
]

Expand All @@ -63,14 +62,6 @@ rewriteExpWithRule rule = everywhereExp rule identity
--------------------------------------------------------------------------------
-- Rewrite rules for expressions -----------------------------------------------

removeScopeWhenInsideEmptyFunction ∷ RewriteRule
removeScopeWhenInsideEmptyFunction = \case
Function
outerArgs
[Ann (Return (Ann (FunctionCall (Ann (Function [] body)) [])))] →
Function outerArgs body
e → e

{- | Rewrites '{ foo = 1, bar = 2 }.foo' to '1'.

IR-visible record literals are already folded by the IR optimizer
Expand Down
1 change: 1 addition & 0 deletions pslua.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ test-suite spec
Language.PureScript.Backend.Lua.Optimizer.Spec
Language.PureScript.Backend.Lua.Printer.Spec
Language.PureScript.Backend.Lua.Run.Spec
Language.PureScript.Backend.Lua.Spec
Language.PureScript.Backend.Output.Spec
Language.PureScript.PSString.Spec
Test.Hspec.Expectations.Pretty
Expand Down
25 changes: 0 additions & 25 deletions test/Language/PureScript/Backend/Lua/Optimizer/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,15 @@ import Language.PureScript.Backend.Lua.Name (name)
import Language.PureScript.Backend.Lua.Optimizer
( foldFieldProjectionThroughScopeCall
, reduceTableDefinitionAccessor
, removeScopeWhenInsideEmptyFunction
, rewriteExpWithRule
)
import Language.PureScript.Backend.Lua.Types (ParamF (..))
import Language.PureScript.Backend.Lua.Types qualified as Lua
import Test.Hspec (Spec, describe, it)
import Test.Hspec.Expectations.Pretty (assertEqual)
import Text.Pretty.Simple (pShow)

spec ∷ Spec
spec = describe "Lua AST Optimizer" do
describe "optimizes expressions" do
it "removes scope when inside an empty function" do
let original ∷ Lua.Exp =
Lua.functionDef
[ParamNamed [name|a|]]
[ Lua.return
( Lua.functionDef
[ParamNamed [name|b|]]
[Lua.return (Lua.scope [Lua.return (Lua.varName [name|c|])])]
)
]
expected ∷ Lua.Exp =
Lua.functionDef
[ParamNamed [name|a|]]
[ Lua.return
( Lua.functionDef
[ParamNamed [name|b|]]
[Lua.return (Lua.varName [name|c|])]
)
]
assertEqual (toString $ pShow original) expected $
rewriteExpWithRule removeScopeWhenInsideEmptyFunction original

describe "reduceTableDefinitionAccessor" do
it "folds a field access into an unambiguous name-value definition" do
let original ∷ Lua.Exp =
Expand Down
84 changes: 84 additions & 0 deletions test/Language/PureScript/Backend/Lua/Spec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
module Language.PureScript.Backend.Lua.Spec where

import Control.Monad.Oops (Variant)
import Control.Monad.Trans.Except (ExceptT, runExceptT)
import Data.Tagged (Tagged (..))
import Data.Text qualified as Text
import Language.PureScript.Backend.IR qualified as IR
import Language.PureScript.Backend.IR.Linker (UberModule (..))
import Language.PureScript.Backend.Lua qualified as Lua
import Language.PureScript.Backend.Lua.Printer qualified as Printer
import Language.PureScript.Backend.Lua.Types qualified as Lua.Types
import Language.PureScript.Backend.Types (AppOrModule (AsModule))
import Path.IO (getCurrentDir)
import Prettyprinter (defaultLayoutOptions, layoutPretty)
import Prettyprinter.Render.Text (renderStrict)
import Test.Hspec (Spec, describe, expectationFailure, it, shouldSatisfy)

spec ∷ Spec
spec = describe "Lua.fromUberModule" do
it "does not wrap Abs-over-Let body in a scope IIFE" do
rendered ← compileExportedExpr absWithLetBody
rendered `shouldSatisfy` (not . Text.isInfixOf "(function()")

it "does not wrap Abs-over-IfThenElse body in a scope IIFE" do
rendered ← compileExportedExpr absWithIfBody
rendered `shouldSatisfy` (not . Text.isInfixOf "(function()")

compileExportedExpr ∷ IR.Exp → IO Text
compileExportedExpr expr = do
foreignPath ← Tagged <$> getCurrentDir
let
moduleName = IR.ModuleName "Test.AbsScopeIife"
uberModule =
UberModule
{ uberModuleBindings = []
, uberModuleForeigns = []
, uberModuleExports = [(IR.Name "value", expr)]
}
result ←
runExceptT
( Lua.fromUberModule
foreignPath
(Tagged False)
(AsModule moduleName)
uberModule
∷ ExceptT (Variant '[Lua.Error]) IO Lua.Types.Chunk
)
case result of
Left _err → expectationFailure "Lua.fromUberModule failed" >> pure ""
Right chunk →
pure . renderStrict $
layoutPretty defaultLayoutOptions (Printer.printLuaChunk chunk)

absWithLetBody ∷ IR.Exp
absWithLetBody =
IR.Abs
IR.noAnn
(IR.ParamNamed IR.noAnn (IR.Name "x"))
( IR.Let
IR.noAnn
( IR.Standalone
( IR.noAnn
, IR.Name "y"
, IR.App
IR.noAnn
(IR.Ref IR.noAnn (IR.Local (IR.Name "f")))
(IR.Ref IR.noAnn (IR.Local (IR.Name "x")))
)
:| []
)
(IR.Ref IR.noAnn (IR.Local (IR.Name "y")))
)

absWithIfBody ∷ IR.Exp
absWithIfBody =
IR.Abs
IR.noAnn
(IR.ParamNamed IR.noAnn (IR.Name "x"))
( IR.IfThenElse
IR.noAnn
(IR.Ref IR.noAnn (IR.Local (IR.Name "p")))
(IR.Ref IR.noAnn (IR.Local (IR.Name "x")))
(IR.LiteralInt IR.noAnn 0)
)
2 changes: 2 additions & 0 deletions test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Language.PureScript.Backend.Lua.NestingCheck.Spec qualified as NestingChe
import Language.PureScript.Backend.Lua.Optimizer.Spec qualified as LuaOptimizer
import Language.PureScript.Backend.Lua.Printer.Spec qualified as Printer
import Language.PureScript.Backend.Lua.Run.Spec qualified as Run
import Language.PureScript.Backend.Lua.Spec qualified as Lua
import Language.PureScript.Backend.Output.Spec qualified as Output
import Language.PureScript.PSString.Spec qualified as PSString
import Test.Hspec (hspec)
Expand All @@ -34,6 +35,7 @@ main = hspec do
IRPass.spec
IRUniquify.spec
LuaOptimizer.spec
Lua.spec
Printer.spec
Run.spec
LuaLinkerForeign.spec
Expand Down
Loading