git.iko.soy/rowtype-yoga/purescm
on bytevector help sign in
Implement initial code generation for effect (#107)
commitf1324ccecb20cc3400a0e3ddadb3486908bc1977 authorJustin Garcia <purefunctor@pm.me> dateSat 1 Apr 2023, 17:14 (4 years ago) parenteee842c
10 files changed+150−21
src/PureScript/Backend/Chez/Convert.purs+44−7 src/PureScript/Backend/Chez/Syntax.purs+8−3 test-snapshots/spago.yaml+8−2 test-snapshots/src/snapshots-input/Snapshot.Effect.purs+18 test-snapshots/src/snapshots-input/Snapshot.Let.purs+13 test-snapshots/src/snapshots-output/Snapshot.ConstructorAccessor.ss+1−1 test-snapshots/src/snapshots-output/Snapshot.Effect.ss+18 test-snapshots/src/snapshots-output/Snapshot.Let.ss+13 test/Main.purs+7−1 test/Utils.purs+20−7
src/PureScript/Backend/Chez/Convert.purs+44−7
@@ -18,7 +18,7 @@ import PureScript.Backend.Chez.Syntax (ChezDefinition(..), ChezExport(..), ChezE
1818import PureScript.Backend.Chez.Syntax as S
1919import PureScript.Backend.Optimizer.Convert (BackendModule, BackendBindingGroup)
2020import PureScript.Backend.Optimizer.CoreFn (Ident(..), Literal(..), ModuleName(..))
21−import PureScript.Backend.Optimizer.Semantics (NeutralExpr(..))
21+import PureScript.Backend.Optimizer.Semantics (NeutralExpr)
2222import PureScript.Backend.Optimizer.Syntax (BackendAccessor(..), BackendOperator(..), BackendOperator1(..), BackendOperator2(..), BackendOperatorNum(..), BackendOperatorOrd(..), BackendSyntax(..), Pair(..))
2323import Safe.Coerce (coerce)
2424
@@ -130,7 +130,7 @@ codegenTopLevelBinding codegenEnv (Tuple (Ident i) n) =
130130 [ DefineValue i $ codegenExpr codegenEnv n ]
131131
132132codegenExpr :: CodegenEnv -> NeutralExpr -> ChezExpr
133−codegenExpr codegenEnv@{ currentModule } (NeutralExpr s) = case s of
133+codegenExpr codegenEnv@{ currentModule } s = case unwrap s of
134134 Var qi ->
135135 S.Identifier $ S.resolve currentModule qi
136136 Local i l ->
@@ -177,8 +177,8 @@ codegenExpr codegenEnv@{ currentModule } (NeutralExpr s) = case s of
177177
178178 LetRec _ _ _ ->
179179 S.Identifier "let-rec"
180− Let i l v e ->
181− S.chezLet (S.toChezIdent i l) (codegenExpr codegenEnv v) (codegenExpr codegenEnv e)
180+ Let _ _ _ _ ->
181+ codegenChain pureBlockMode codegenEnv s
182182 Branch b o -> do
183183 let
184184 goPair :: Pair NeutralExpr -> { c :: _, e :: _ }
@@ -186,11 +186,11 @@ codegenExpr codegenEnv@{ currentModule } (NeutralExpr s) = case s of
186186 S.chezCond (goPair <$> b) (codegenExpr codegenEnv <$> o)
187187
188188 EffectBind _ _ _ _ ->
189− S.Identifier "effect-bind"
189+ codegenEffectChain codegenEnv s
190190 EffectPure _ ->
191− S.Identifier "effect-pure"
191+ codegenEffectChain codegenEnv s
192192 EffectDefer _ ->
193− S.Identifier "effect-defer"
193+ codegenEffectChain codegenEnv s
194194
195195 PrimOp o ->
196196 codegenPrimOp codegenEnv o
@@ -225,6 +225,43 @@ codegenLiteral codegenEnv = case _ of
225225 LitArray a -> S.vector $ codegenExpr codegenEnv <$> a
226226 LitRecord r -> S.record $ (map $ codegenExpr codegenEnv) <$> r
227227
228+type BlockMode = { effect :: Boolean }
229+
230+pureBlockMode :: BlockMode
231+pureBlockMode = { effect: false }
232+
233+effectBlockMode :: BlockMode
234+effectBlockMode = { effect: true }
235+
236+codegenPureChain :: CodegenEnv -> NeutralExpr -> ChezExpr
237+codegenPureChain = codegenChain pureBlockMode
238+
239+codegenEffectChain :: CodegenEnv -> NeutralExpr -> ChezExpr
240+codegenEffectChain codegenEnv = S.chezThunk <<< codegenChain effectBlockMode codegenEnv
241+
242+codegenChain :: BlockMode -> CodegenEnv -> NeutralExpr -> ChezExpr
243+codegenChain blockMode codegenEnv = go []
244+ where
245+ go :: Array _ -> NeutralExpr -> ChezExpr
246+ go bindings expression = case unwrap expression of
247+ Let i l v e' ->
248+ go (Array.cons { i, l, v: codegenExpr codegenEnv v } bindings) e'
249+ EffectBind i l v e' | blockMode.effect ->
250+ go (Array.cons { i, l, v: S.chezUnthunk $ codegenExpr codegenEnv v } bindings) e'
251+ EffectPure e' | blockMode.effect ->
252+ go bindings e'
253+ EffectDefer e' | blockMode.effect ->
254+ go bindings e'
255+ _ | Array.null bindings ->
256+ codegenExpr codegenEnv expression
257+ _ ->
258+ S.List $
259+ [ S.Identifier "scm:let*"
260+ , S.List $ Array.reverse bindings <#> \binding ->
261+ S.List [ S.Identifier $ S.toChezIdent binding.i binding.l, binding.v ]
262+ , codegenExpr codegenEnv expression
263+ ]
264+
228265codegenPrimOp :: CodegenEnv -> BackendOperator NeutralExpr -> ChezExpr
229266codegenPrimOp codegenEnv@{ currentModule } = case _ of
230267 Op1 o x -> do
src/PureScript/Backend/Chez/Syntax.purs+8−3
@@ -342,7 +342,9 @@ printRecordDefinition ident name constructor predicate fields =
342342
343343toChezIdent :: Maybe Ident -> Level -> Prim.String
344344toChezIdent i (Level l) = case i of
345− Just (Ident i') -> i' <> show l
345+ Just (Ident i') -> case i' of
346+ "$__unused" -> "_"
347+ _ -> i' <> show l
346348 Nothing -> "_" <> show l
347349
348350--
@@ -367,8 +369,11 @@ chezCurriedApplication f s = NonEmptyArray.foldl1 app $ NonEmptyArray.cons f s
367369chezCurriedFunction :: NonEmptyArray Prim.String -> ChezExpr -> ChezExpr
368370chezCurriedFunction a e = Array.foldr lambda e $ NonEmptyArray.toArray a
369371
370−chezLet :: Prim.String -> ChezExpr -> ChezExpr -> ChezExpr
371−chezLet i v e = List [ Identifier $ scmPrefixed "letrec*", List [ List [ Identifier i, v ] ], e ]
372+chezThunk :: ChezExpr -> ChezExpr
373+chezThunk e = List [ Identifier $ scmPrefixed "lambda", List [], e ]
374+
375+chezUnthunk :: ChezExpr -> ChezExpr
376+chezUnthunk e = List [ e ]
372377
373378--
374379
test-snapshots/spago.yaml+8−2
@@ -1,9 +1,10 @@
11package:
22 name: purescm-snapshots
33 dependencies:
4+ - effect
5+ - functions
46 - partial
57 - prelude
6− - functions
78workspace:
89 package_set:
910 registry: 19.1.0
@@ -19,5 +20,10 @@ workspace:
1920 functions:
2021 git: https://github.com/purescm/purescript-functions.git
2122 ref: a43d127384ab568575fb9ee9e4a7a1844b21d39b
22− dependencies:
23+ dependencies:
24+ - prelude
25+ effect:
26+ git: https://github.com/purescm/purescript-effect.git
27+ ref: 8aee2c2fbf5425bf8fa4537a5ad2070170622acf
28+ dependencies:
2329 - prelude
test-snapshots/src/snapshots-input/Snapshot.Effect.pursnew file+18
@@ -0,0 +1,18 @@
1+-- @inline Snapshot.Effect.dontInlineMe never
2+module Snapshot.Effect where
3+
4+import Prelude
5+
6+import Effect (Effect)
7+
8+dontInlineMe :: forall a. a -> Effect Unit
9+dontInlineMe _ = pure unit
10+
11+main :: Effect Unit
12+main = do
13+ dontInlineMe "a"
14+ dontInlineMe "b"
15+ value <- dontInlineMe "c"
16+ dontInlineMe "d"
17+ dontInlineMe "e"
18+ pure value
test-snapshots/src/snapshots-input/Snapshot.Let.pursnew file+13
@@ -0,0 +1,13 @@
1+module Snapshot.Let where
2+
3+import Prelude
4+
5+letChain :: Int -> Int
6+letChain x =
7+ let
8+ a = x + x
9+ b = a + a
10+ c = b + b
11+ d = c * c
12+ in
13+ a + b + c + d
test-snapshots/src/snapshots-output/Snapshot.ConstructorAccessor.ss+1−1
@@ -76,7 +76,7 @@
7676 (Foo 1))
7777
7878 (scm:define test5
79− (scm:lambda ($__unused0)
79+ (scm:lambda (_)
8080 (scm:lambda (v1)
8181 (scm:cond ((First? v1) (First-value0 v1)) (scm:else (scm:raise (scm:condition (scm:make-error) (scm:make-message-condition "Failed pattern match"))))))))
8282
test-snapshots/src/snapshots-output/Snapshot.Effect.ssnew file+18
@@ -0,0 +1,18 @@
1+#!r6rs
2+#!chezscheme
3+(library
4+ (Snapshot.Effect lib)
5+ (export
6+ dontInlineMe
7+ main)
8+ (import
9+ (prefix (chezscheme) scm:)
10+ (prefix (_Chez_Runtime lib) rt:)
11+ (prefix (Data.Unit lib) Data.Unit.))
12+
13+ (scm:define dontInlineMe
14+ (scm:lambda (v0)
15+ (scm:lambda () Data.Unit.unit)))
16+
17+ (scm:define main
18+ (scm:let* ((_0 (dontInlineMe "a"))) (scm:lambda () (scm:let* ((_ (_0)) (_ ((dontInlineMe "b"))) (value3 ((dontInlineMe "c"))) (_ ((dontInlineMe "d"))) (_ ((dontInlineMe "e")))) value3)))))
test-snapshots/src/snapshots-output/Snapshot.Let.ssnew file+13
@@ -0,0 +1,13 @@
1+#!r6rs
2+#!chezscheme
3+(library
4+ (Snapshot.Let lib)
5+ (export
6+ letChain)
7+ (import
8+ (prefix (chezscheme) scm:)
9+ (prefix (_Chez_Runtime lib) rt:))
10+
11+ (scm:define letChain
12+ (scm:lambda (x0)
13+ (scm:let* ((a1 (scm:fx+ x0 x0)) (b2 (scm:fx+ a1 a1)) (c3 (scm:fx+ b2 b2))) (scm:fx+ (scm:fx+ (scm:fx+ a1 b2) c3) (scm:fx* c3 c3))))))
test/Main.purs+7−1
@@ -151,7 +151,13 @@ runSnapshotTests { accept, filter } = do
151151 snapshotFilePath = Path.concat [ snapshotsOut, name <> schemeExt ]
152152 runAcceptedTest = do
153153 schemeFile <- liftEffect $ Path.resolve [ testOut, name ] $ moduleLib <> schemeExt
154− result <- loadModuleMain testOut schemeBin hasMain schemeFile
154+ result <- loadModuleMain
155+ { libdir: testOut
156+ , scheme: schemeBin
157+ , hasMain
158+ , modulePath: schemeFile
159+ , moduleName: name
160+ }
155161 case result of
156162 Left err | matchesFail err.message failsWith -> do
157163 Console.log $ withGraphics (foreground Red) "✗" <> " " <> name <> " failed."
test/Utils.purs+20−7
@@ -28,7 +28,6 @@ import Data.Set.NonEmpty as NonEmptySet
2828import Data.Tuple (Tuple(..))
2929import Effect.Aff (Aff, effectCanceler, error, makeAff, throwError)
3030import Effect.Class (liftEffect)
31−import Effect.Console (log)
3231import Node.Buffer (Buffer, freeze)
3332import Node.Buffer.Immutable as ImmutableBuffer
3433import Node.ChildProcess (ExecResult, Exit(..), defaultExecOptions, defaultSpawnOptions, inherit)
@@ -76,12 +75,26 @@ bufferToUTF8 = liftEffect <<< map (ImmutableBuffer.toString UTF8) <<< freeze
7675mkdirp :: FilePath -> Aff Unit
7776mkdirp path = FS.mkdir' path { recursive: true, mode: mkPerms Perms.all Perms.all Perms.all }
7877
79−-- | Note: this code needs to be updated to run Chez Scheme scripts
80−loadModuleMain :: FilePath -> String -> Boolean -> FilePath -> Aff (Either ExecaError ExecaSuccess)
81−loadModuleMain libDir schemeBin hasMain path = do
82− when hasMain do
83− liftEffect $ log "Running a snaphot as a top-level program is not yet implemented."
84− spawned <- execa schemeBin [ "--libdirs", libDir <> ":", "--script", path ] identity
78+loadModuleMain
79+ :: { libdir :: FilePath
80+ , scheme :: String
81+ , hasMain :: Boolean
82+ , moduleName :: String
83+ , modulePath :: String
84+ }
85+ -> Aff (Either ExecaError ExecaSuccess)
86+loadModuleMain options = do
87+ let
88+ arguments :: Array String
89+ arguments = [ "-q", "--libdirs", options.libdir <> ":" ] <>
90+ if not options.hasMain then
91+ [ "--script", options.modulePath ]
92+ else
93+ []
94+ spawned <- execa options.scheme arguments identity
95+ when options.hasMain do
96+ spawned.stdin.writeUtf8End $ Array.fold
97+ [ "(import (", options.moduleName, " lib)) (main)" ]
8598 spawned.result
8699
87100copyFile :: FilePath -> FilePath -> Aff Unit