git.iko.soy/rowtype-yoga/purescm
on bytevector help sign in
Implement basic library conversion
commitcc36d6d1dd8190d8ca93804e77dc1033eabe2ba0 authorpurefunctor <purefunctor@pm.me> dateFri 10 Mar 2023, 22:31 (4 years ago) parent419dff2
4 files changed+234−4
spago.dhall+21−1 src/Main.purs+37−3 src/PureScript/Backend/Chez/Convert.purs+115 src/PureScript/Backend/Chez/Syntax.purs+61
spago.dhall+21−1
@@ -1,5 +1,25 @@
11{ name = "purs-backend-chez"
2−, dependencies = [ "backend-optimizer", "console", "effect", "prelude" ]
2+, dependencies =
3+ [ "aff"
4+ , "argonaut"
5+ , "arrays"
6+ , "backend-optimizer"
7+ , "bifunctors"
8+ , "console"
9+ , "control"
10+ , "debug"
11+ , "dodo-printer"
12+ , "effect"
13+ , "either"
14+ , "maybe"
15+ , "newtype"
16+ , "partial"
17+ , "prelude"
18+ , "profunctor"
19+ , "safe-coerce"
20+ , "strings"
21+ , "tuples"
22+ ]
323, packages = ./packages.dhall
424, sources = [ "src/**/*.purs", "test/**/*.purs" ]
525}
src/Main.purs+37−3
@@ -2,9 +2,43 @@ module Main where
22
33import Prelude
44
5+import Control.Alternative (empty)
6+import Data.Argonaut as Json
7+import Data.Bifunctor (lmap)
8+import Data.Either (Either(..))
9+import Dodo (plainText)
10+import Dodo as Dodo
511import Effect (Effect)
6−import Effect.Console (log)
12+import Effect.Aff (Aff, launchAff_)
13+import Effect.Class.Console (error, log)
14+import PureScript.Backend.Chez.Convert (codegenModule)
15+import PureScript.Backend.Chez.Syntax as S
16+import PureScript.Backend.Optimizer.Builder (buildModules)
17+import PureScript.Backend.Optimizer.CoreFn (Ann, Module)
18+import PureScript.Backend.Optimizer.CoreFn.Json (decodeModule)
19+
20+simpleCoreFn :: String
21+simpleCoreFn =
22+ """
23+{"builtWith":"0.15.7","comments":[],"decls":[{"annotation":{"meta":null,"sourceSpan":{"end":[3,24],"start":[3,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[3,24],"start":[3,17]}},"type":"Literal","value":{"literalType":"StringLiteral","value":"hello"}},"identifier":"literalString"},{"annotation":{"meta":null,"sourceSpan":{"end":[9,15],"start":[9,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[9,15],"start":[9,14]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"identifier":"literalInt"},{"annotation":{"meta":null,"sourceSpan":{"end":[7,19],"start":[7,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[7,19],"start":[7,16]}},"type":"Literal","value":{"literalType":"NumberLiteral","value":0.1}},"identifier":"literalFloat"},{"annotation":{"meta":null,"sourceSpan":{"end":[5,18],"start":[5,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[5,18],"start":[5,15]}},"type":"Literal","value":{"literalType":"CharLiteral","value":"a"}},"identifier":"literalChar"},{"annotation":{"meta":null,"sourceSpan":{"end":[11,23],"start":[11,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[11,23],"start":[11,18]}},"type":"Literal","value":{"literalType":"BooleanLiteral","value":false}},"identifier":"literalBoolean"}],"exports":["literalString","literalChar","literalFloat","literalInt","literalBoolean"],"foreign":[],"imports":[{"annotation":{"meta":null,"sourceSpan":{"end":[11,23],"start":[1,1]}},"moduleName":["Prim"]}],"moduleName":["Simple"],"modulePath":"src/Simple.purs","reExports":{},"sourceSpan":{"end":[11,23],"start":[1,1]}}
24+"""
25+
26+parseCoreFnModule :: String -> Aff (Either String (Module Ann))
27+parseCoreFnModule contents = pure $ lmap Json.printJsonDecodeError <<< decodeModule =<<
28+ Json.jsonParser contents
729
830main :: Effect Unit
9−main = do
10− log "🍝"
31+main = launchAff_ do
32+ coreFnModule <- parseCoreFnModule simpleCoreFn
33+ case coreFnModule of
34+ Left e -> error e
35+ Right m -> do
36+ pure m # buildModules
37+ { directives: empty
38+ , foreignSemantics: empty
39+ , onCodegenModule: \_ _ backendModule -> do
40+ log $ Dodo.print plainText Dodo.twoSpaces $ S.printChezExpr $ codegenModule
41+ backendModule
42+ pure unit
43+ , onPrepareModule: \_ coreFnM -> pure coreFnM
44+ }
src/PureScript/Backend/Chez/Convert.pursnew file+115
@@ -0,0 +1,115 @@
1+module PureScript.Backend.Chez.Convert where
2+
3+import Prelude
4+
5+import Data.Argonaut as Json
6+import Data.Array as A
7+import Data.Array as Array
8+import Data.Array.NonEmpty as NEA
9+import Data.Array.NonEmpty as NonEmptyArray
10+import Data.Maybe (Maybe(..))
11+import Data.Newtype (wrap)
12+import Data.Profunctor.Strong (second)
13+import Data.String.CodeUnits as CodeUnits
14+import Data.Tuple (Tuple, uncurry)
15+import Partial.Unsafe (unsafeCrashWith)
16+import PureScript.Backend.Chez.Syntax (ChezExpr)
17+import PureScript.Backend.Chez.Syntax as S
18+import PureScript.Backend.Optimizer.Convert (BackendModule, BackendBindingGroup)
19+import PureScript.Backend.Optimizer.CoreFn (Ident(..), Literal(..), ModuleName(..), Qualified(..))
20+import PureScript.Backend.Optimizer.Semantics (NeutralExpr(..))
21+import PureScript.Backend.Optimizer.Syntax (BackendSyntax(..))
22+import Safe.Coerce (coerce)
23+
24+codegenModule :: BackendModule -> ChezExpr
25+codegenModule { name, bindings, exports } =
26+ let
27+ name' :: String
28+ name' = coerce name
29+
30+ exports' :: Array String
31+ exports' = coerce $ A.fromFoldable exports
32+
33+ bindings' :: Array ChezExpr
34+ bindings' = Array.concat $ codegenTopLevelBindingGroup <$> bindings
35+ in
36+ S.library name' exports' bindings'
37+
38+codegenTopLevelBindingGroup :: BackendBindingGroup Ident NeutralExpr -> Array ChezExpr
39+codegenTopLevelBindingGroup { recursive, bindings }
40+ | recursive, Just bindings' <- NonEmptyArray.fromArray bindings = unsafeCrashWith "undefined"
41+ | otherwise = codegenBindings bindings
42+
43+codegenBindings :: Array (Tuple Ident NeutralExpr) -> Array ChezExpr
44+codegenBindings = map (coerce $ uncurry S.define) <<< map (second go)
45+ where
46+ go :: NeutralExpr -> ChezExpr
47+ go (NeutralExpr s) = case s of
48+ Var (Qualified (Just (ModuleName mn)) (Ident v)) ->
49+ S.Identifier $ mn <> "." <> v
50+ Var (Qualified _ (Ident v)) ->
51+ S.Identifier v
52+ Local _ _ ->
53+ S.Identifier "local-variable"
54+
55+ Lit l ->
56+ codegenLiteral l
57+
58+ App f p ->
59+ NEA.foldl1 S.app $ NEA.cons (go f) (go <$> p)
60+ Abs _ _ ->
61+ S.Identifier "abs"
62+
63+ UncurriedApp _ _ ->
64+ S.Identifier "uncurried-app"
65+ UncurriedAbs _ _ ->
66+ S.Identifier "uncurried-abs"
67+
68+ UncurriedEffectApp _ _ ->
69+ S.Identifier "uncurried-effect-app"
70+ UncurriedEffectAbs _ _ ->
71+ S.Identifier "uncurried-effect-ab"
72+
73+ Accessor _ _ ->
74+ S.Identifier "object-accessor"
75+ Update _ _ ->
76+ S.Identifier "object-update"
77+
78+ CtorSaturated _ _ _ _ _ ->
79+ S.Identifier "ctor-saturated"
80+ CtorDef _ _ _ _ ->
81+ S.Identifier "ctor-def"
82+
83+ LetRec _ _ _ ->
84+ S.Identifier "let-rec"
85+ Let _ _ _ _ ->
86+ S.Identifier "let"
87+ Branch _ _ ->
88+ S.Identifier "branch"
89+
90+ EffectBind _ _ _ _ ->
91+ S.Identifier "effect-bind"
92+ EffectPure _ ->
93+ S.Identifier "effect-pure"
94+ EffectDefer _ ->
95+ S.Identifier "effect-defer"
96+
97+ PrimOp _ ->
98+ S.Identifier "prim-op"
99+ PrimEffect _ ->
100+ S.Identifier "prim-effect"
101+ PrimUndefined ->
102+ S.Identifier "prim-undefined"
103+
104+ Fail _ ->
105+ S.Identifier "fail"
106+
107+codegenLiteral :: forall a. Literal a -> ChezExpr
108+codegenLiteral = case _ of
109+ LitInt i -> S.Integer $ wrap $ show i
110+ LitNumber n -> S.Float $ wrap $ show n
111+ LitString s -> S.String $ Json.stringify $ Json.fromString s
112+ LitChar c -> S.Identifier $ "#\\" <> CodeUnits.singleton c
113+ LitBoolean b -> S.Identifier $ if b then "#t" else "#f"
114+ LitArray _ -> S.Identifier "literal-array"
115+ LitRecord _ -> S.Identifier "literal-object"
src/PureScript/Backend/Chez/Syntax.pursnew file+61
@@ -0,0 +1,61 @@
1+module PureScript.Backend.Chez.Syntax where
2+
3+import Prelude
4+
5+import Data.Newtype (class Newtype)
6+import Dodo as Dodo
7+import Prim as Prim
8+
9+newtype LiteralDigit = LiteralDigit Prim.String
10+
11+derive instance Newtype LiteralDigit _
12+derive newtype instance Eq LiteralDigit
13+derive newtype instance Ord LiteralDigit
14+
15+data ChezExpr
16+ = Integer LiteralDigit
17+ | Float LiteralDigit
18+ | String Prim.String
19+ | Boolean Prim.Boolean
20+ | Identifier Prim.String
21+ | List (Prim.Array ChezExpr)
22+
23+printChezExpr :: forall a. ChezExpr -> Dodo.Doc a
24+printChezExpr e = case e of
25+ Integer (LiteralDigit x) -> Dodo.text x
26+ Float (LiteralDigit x) -> Dodo.text x
27+ String x -> Dodo.text x
28+ Boolean x -> Dodo.text $ if x then "#t" else "#f"
29+ Identifier x -> Dodo.text x
30+ List xs ->
31+ let
32+ xs' = printChezExpr <$> xs
33+ in
34+ Dodo.text "(" <> Dodo.words xs' <> Dodo.text ")"
35+
36+app :: ChezExpr -> ChezExpr -> ChezExpr
37+app f x = List [ f, x ]
38+
39+define :: Prim.String -> ChezExpr -> ChezExpr
40+define i e = List [ Identifier "scm:define", Identifier i, e ]
41+
42+library :: Prim.String -> Prim.Array Prim.String -> Prim.Array ChezExpr -> ChezExpr
43+library moduleName exports bindings =
44+ List $
45+ [ Identifier "library"
46+ , Identifier $ "(" <> moduleName <> " lib" <> ")"
47+ , List $
48+ [ Identifier "export"
49+ ] <> (Identifier <$> exports)
50+ , List
51+ [ Identifier "import"
52+ , List
53+ [ Identifier "prefix"
54+ , List
55+ [ Identifier "chezscheme"
56+ ]
57+ , Identifier "scm:"
58+
59+ ]
60+ ]
61+ ] <> bindings