Skip to content

Commit 3a537bc

Browse files
authored
Allow YAML tags to be specified / captured via metadata (#77)
* Scalar event to carry emit metadata * Extend branch table to accept metadata * Initial globals for forcing render meta * WIP: globals to eval render meta and pass to emit * Now outputs tags from metadata. * Preserve metadata on natives through compilation
1 parent b0538cc commit 3a537bc

File tree

18 files changed

+372
-124
lines changed

18 files changed

+372
-124
lines changed

src/Eucalypt/Render/Json.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ putBSFragment e@E.OutputMappingStart = do
161161
pushContext InObject
162162

163163
putBSFragment e@E.OutputMappingEnd = setLast e >> popContext >> putText "}"
164-
putBSFragment e@(E.OutputScalar n) = putScalar e (formatScalar n)
164+
putBSFragment e@(E.OutputScalar _ n) = putScalar e (formatScalar n)
165165
putBSFragment e@E.OutputNull = putScalar e "null"
166166
putBSFragment _ = putText ""
167167

src/Eucalypt/Render/Text.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ formatScalar (NativeDict d) =
4545

4646

4747
toFragment :: E.Event -> Maybe Builder
48-
toFragment (E.OutputScalar n) = Just $ formatScalar n
48+
toFragment (E.OutputScalar _ n) = Just $ formatScalar n
4949
toFragment _ = Nothing
5050

5151
pipeline :: Monad m => ConduitT E.Event Void m BS.ByteString

src/Eucalypt/Render/Yaml.hs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,44 +23,56 @@ import Data.Text (pack)
2323
import Data.Text.Encoding (encodeUtf8)
2424
import qualified Text.Libyaml as L
2525

26+
import Eucalypt.Stg.Event (RenderMetadata(..))
2627
import qualified Eucalypt.Stg.Event as E
2728
import Eucalypt.Stg.Syn (Native(..))
2829

29-
-- STG implementation
30+
tag :: RenderMetadata -> L.Tag -> L.Tag
31+
tag RenderMetadata {metaTag = Nothing} def = def
32+
tag RenderMetadata {metaTag = (Just t)} _ = L.UriTag t
3033

31-
renderValue :: Native -> [L.Event]
32-
renderValue (NativeNumber n) =
34+
style :: RenderMetadata -> L.Style -> L.Style
35+
style RenderMetadata {metaTag = Nothing} def = def
36+
style RenderMetadata {metaTag = _} _ = L.Plain
37+
38+
-- | Render a native value as a YAML scalar
39+
renderValue :: Native -> E.RenderMetadata -> [L.Event]
40+
renderValue (NativeNumber n) rm =
3341
case floatingOrInteger n of
34-
Left r -> [L.EventScalar (encodeUtf8 $ pack $ show r) L.FloatTag L.PlainNoTag Nothing]
35-
Right i -> [L.EventScalar (encodeUtf8 $ pack $ show i) L.IntTag L.PlainNoTag Nothing]
36-
renderValue (NativeSymbol s) =
37-
[L.EventScalar (encodeUtf8 $ pack s) L.StrTag L.PlainNoTag Nothing]
38-
renderValue (NativeString s) =
39-
[L.EventScalar (encodeUtf8 $ pack s) L.NoTag (style s) Nothing]
42+
Left r -> [L.EventScalar (encodeUtf8 $ pack $ show r) (tag rm L.FloatTag) (style rm L.PlainNoTag) Nothing]
43+
Right i -> [L.EventScalar (encodeUtf8 $ pack $ show i) (tag rm L.IntTag) (style rm L.PlainNoTag) Nothing]
44+
renderValue (NativeSymbol s) rm =
45+
[L.EventScalar (encodeUtf8 $ pack s) (tag rm L.StrTag) (style rm L.PlainNoTag) Nothing]
46+
renderValue (NativeString s) rm =
47+
[L.EventScalar (encodeUtf8 $ pack s) (tag rm L.NoTag) (textStyle s) Nothing]
4048
where
41-
style "" = L.DoubleQuoted
42-
style "*" = L.DoubleQuoted
43-
style "/" = L.DoubleQuoted
44-
style str | length str > 60 = L.Literal
45-
style _ = L.PlainNoTag
46-
renderValue (NativeBool b) =
49+
textStyle "" = L.DoubleQuoted
50+
textStyle "*" = L.DoubleQuoted
51+
textStyle "/" = L.DoubleQuoted
52+
textStyle str
53+
| length str > 60 = L.Literal
54+
textStyle _ = style rm L.PlainNoTag
55+
renderValue (NativeBool b) rm =
4756
[L.EventScalar
4857
(encodeUtf8 $
4958
pack $
5059
if b
5160
then "true"
5261
else "false")
53-
L.BoolTag
54-
L.PlainNoTag
62+
(tag rm L.BoolTag)
63+
(style rm L.PlainNoTag)
5564
Nothing]
56-
renderValue (NativeSet s) =
65+
renderValue (NativeSet s) _ =
5766
[L.EventSequenceStart Nothing] ++
58-
concatMap renderValue (toList s) ++ [L.EventSequenceEnd]
59-
renderValue (NativeDict d) =
67+
concatMap (`renderValue` RenderMetadata {metaTag = Nothing}) (toList s) ++
68+
[L.EventSequenceEnd]
69+
renderValue (NativeDict d) _ =
6070
[L.EventMappingStart Nothing] ++
6171
concatMap kv (MS.assocs d) ++ [L.EventMappingEnd]
6272
where
63-
kv (k, v) = renderValue k ++ renderValue v
73+
kv (k, v) =
74+
renderValue k RenderMetadata {metaTag = Nothing} ++
75+
renderValue v RenderMetadata {metaTag = Nothing}
6476

6577
toYamlEvents :: E.Event -> [L.Event]
6678
toYamlEvents e =
@@ -69,7 +81,7 @@ toYamlEvents e =
6981
E.OutputStreamEnd -> [L.EventStreamEnd]
7082
E.OutputDocumentStart -> [L.EventDocumentStart]
7183
E.OutputDocumentEnd -> [L.EventDocumentEnd]
72-
E.OutputScalar n -> renderValue n
84+
E.OutputScalar rm n -> renderValue n rm
7385
E.OutputNull -> [L.EventScalar (encodeUtf8 $ pack "null") L.NullTag L.PlainNoTag Nothing]
7486
E.OutputSequenceStart -> [L.EventSequenceStart Nothing]
7587
E.OutputSequenceEnd -> [L.EventSequenceEnd]

src/Eucalypt/Source/YamlSource.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ expressionFromString s =
112112
Left err -> throwM err
113113
Right expr -> return $ desugar expr
114114

115+
tagMeta :: String -> CoreExpr
116+
tagMeta tag = anon S.block [anon element "tag" $ anon S.str tag]
117+
115118
-- | Active translation scheme
116119
--
117120
-- @!eu@ tag causes expression parse, blocks become let expressions.
@@ -126,7 +129,7 @@ instance YamlTranslator ActiveTranslator where
126129
UriTag u ->
127130
if u == "!eu"
128131
then expressionFromString s
129-
else return $ anon S.str s
132+
else return $ anon S.withMeta (tagMeta u) $ anon S.str s
130133
_ -> return $ anon S.str s
131134
where
132135
s = (unpack . decodeUtf8) text

src/Eucalypt/Stg/Compiler.hs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import qualified Data.Vector as V
2323
import Eucalypt.Core.Syn as C
2424
import Eucalypt.Stg.GlobalInfo
2525
import Eucalypt.Stg.Globals
26+
import Eucalypt.Stg.Intrinsics (intrinsicIndex)
2627
import Eucalypt.Stg.Syn
2728
import Eucalypt.Stg.Tags
2829

@@ -120,9 +121,16 @@ compile _ context _metaref (C.CoreVar _ v) = Atom $ context v
120121
compile _ _ _ (C.CoreBuiltin _ n) = App (Ref (Global n)) mempty
121122

122123
-- | Compile primitive to STG native.
123-
compile _ _ _ (C.CorePrim _ p) = case convert p of
124-
Just n -> Atom (Literal n)
125-
Nothing -> Atom (Global "NULL")
124+
compile _ _context metaref (C.CorePrim _ p) =
125+
case convert p of
126+
Just n -> annotated n
127+
Nothing -> Atom (Global "NULL")
128+
where
129+
annotated n =
130+
maybe
131+
(Atom (Literal n))
132+
(\r -> appbif_ (intrinsicIndex "WITHMETA") [r, Literal n])
133+
metaref
126134

127135
-- | Block literals
128136
compile envSize context _metaref (C.CoreBlock _ content) = let_ [c] b

src/Eucalypt/Stg/Error.hs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ data StgError
4242
| IntrinsicExpectedNativeList
4343
| IntrinsicExpectedStringList
4444
| IntrinsicExpectedEvaluatedList !StgSyn
45+
| IntrinsicExpectedBlockFoundBlackHole
46+
| IntrinsicExpectedBlockFoundPartialApplication
47+
| IntrinsicExpectedBlockFoundNative !Native
48+
| IntrinsicExpectedEvaluatedBlock !StgSyn
49+
| IntrinsicExpectedBlock !StgSyn
4550
| InvalidRegex !String
4651
| UnknownGlobal !String
4752
| DictKeyNotFound !Native
@@ -91,11 +96,23 @@ instance Reportable StgException where
9196
bug "Expected a list, found a black hole."
9297
IntrinsicExpectedListFoundPartialApplication ->
9398
bug "Expected a list, found a partial application."
99+
IntrinsicExpectedBlockFoundBlackHole ->
100+
bug "Expected a block, found a black hole."
101+
IntrinsicExpectedBlockFoundPartialApplication ->
102+
bug "Expected a block, found a partial application."
94103
IntrinsicExpectedNativeList -> err "Expected list of native values."
95104
IntrinsicExpectedStringList -> err "Expected list of strings."
96105
IntrinsicExpectedEvaluatedList expr ->
97106
bug "Expected evaluated list, found unevaluated thunks." P.$$
98107
prettify expr
108+
IntrinsicExpectedBlock expr ->
109+
bug "Expected block, found something else." P.$$
110+
prettify expr
111+
IntrinsicExpectedEvaluatedBlock expr ->
112+
bug "Expected evaluated block, found unevaluated thunks." P.$$
113+
prettify expr
114+
IntrinsicExpectedBlockFoundNative n ->
115+
err "Expected a block but found native value: " P.$$ prettify n
99116
(InvalidRegex s) ->
100117
err "Regular expression was not valid:" P.$$
101118
P.nest 2 (P.text "-" P.<+> P.text s)

src/Eucalypt/Stg/Eval.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ selectBranch (BranchTable bs _ _) t = Map.lookup t bs
8585
-- | Match a native branch table alternative, return the next
8686
-- expression to eval
8787
selectNativeBranch :: BranchTable -> Native -> Maybe StgSyn
88-
selectNativeBranch (BranchTable _ bs _) n = HM.lookup n bs
88+
selectNativeBranch (BranchTable _ bs _) n = snd <$> HM.lookup n bs
8989

9090
-- | Halt the machine
9191
terminate :: MachineState -> MachineState

src/Eucalypt/Stg/Event.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ module Eucalypt.Stg.Event where
1313
import Eucalypt.Stg.Syn
1414
import Data.ByteString as BS
1515

16+
newtype RenderMetadata = RenderMetadata
17+
{ metaTag :: Maybe String
18+
} deriving (Show, Eq)
19+
1620
-- | Various events that can be emitted by the machine, including YAML
1721
-- / JSON output rendering and debug tracing.
1822
data Event
1923
= OutputStreamStart
2024
| OutputStreamEnd
2125
| OutputDocumentStart
2226
| OutputDocumentEnd
23-
| OutputScalar !Native
27+
| OutputScalar RenderMetadata !Native
2428
| OutputNull
2529
| OutputSequenceStart
2630
| OutputSequenceEnd

src/Eucalypt/Stg/Globals/Emit.hs

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,18 @@ globals =
2626
, GlobalInfo "Emit.startList" startList [NonStrict, NonStrict]
2727
, GlobalInfo "Emit.continueList" continueList [NonStrict]
2828
, GlobalInfo "Emit.wrapBlock" wrapBlock [NonStrict]
29+
, GlobalInfo "Emit.forceExportMetadata" forceExportMetadata [NonStrict]
30+
, GlobalInfo "Emit.forceExportMetadataKVList" forceExportMetadataKVList [NonStrict]
31+
, GlobalInfo "Emit.forceKVNatPair" forceKVNatPair [NonStrict]
32+
, GlobalInfo "Emit.isRenderMetadataKey" isRenderMetadataKey [Strict]
2933
, GlobalInfo "RENDER" euRender [NonStrict]
3034
, GlobalInfo "NULL" euNull []
3135
]
3236

37+
38+
panic :: String -> StgSyn
39+
panic msg = appfn_ (Global "PANIC") [Literal $ NativeString msg]
40+
3341
-- | __NULL - for emitting JSON / YAML null
3442
euNull :: LambdaForm
3543
euNull = standardConstructor 0 stgUnit
@@ -46,6 +54,11 @@ emitSS = appbif_ (intrinsicIndex "EMIT[") []
4654
emitSE :: StgSyn
4755
emitSE = appbif_ (intrinsicIndex "EMIT]") []
4856

57+
-- | Emit a scalar.
58+
--
59+
-- The intrinsic requires that the value is already resolve to a
60+
-- native with metadata and that the metadata is already evaluated
61+
-- at all the relevant metadata keys (e.g. :export, :tag ...)
4962
emitScalar :: Ref -> StgSyn
5063
emitScalar n = appbif_ (intrinsicIndex "EMITx") [n]
5164

@@ -132,10 +145,13 @@ continueList =
132145
(Atom (Local 0))
133146
[ ( stgCons
134147
, ( 2
135-
, seq_ (appfn_ (Global "RENDER") [Local 1]) $ appfn_ (Global "Emit.continueList") [Local 2]))
148+
, seq_ (appfn_ (Global "RENDER") [Local 1]) $
149+
appfn_ (Global "Emit.continueList") [Local 2]))
136150
, (stgNil, (0, emitSE))
137-
]
138-
(emitScalar (Local 1))
151+
] $
152+
force_ (appfn_ (Global "META") [Local 1]) $
153+
force_ (appfn_ (Global "Emit.forceExportMetadata") [Local 2]) $
154+
emitScalar (Local 1) -- force is effectful
139155

140156
-- | Emit.startList(l)
141157
startList :: LambdaForm
@@ -181,5 +197,71 @@ euRender =
181197
, (stgCons, (2, appfn_ (Global "Emit.startList") [Local 1, Local 2]))
182198
, (stgNil, (0, appfn_ (Global "Emit.emptyList") []))
183199
, (stgUnit, (0, emitNull))
184-
]
185-
(emitScalar (Local 1))
200+
] $
201+
force_ (appfn_ (Global "META") [Local 1]) $
202+
force_ (appfn_ (Global "Emit.forceExportMetadata") [Local 2]) $
203+
emitScalar (Local 1)
204+
205+
-- | Single argument is the metadata (not the annotated value)
206+
forceExportMetadata :: LambdaForm
207+
forceExportMetadata =
208+
lam_ 0 1 $
209+
ann_ "Emit.forceExportMetadata" 0 $
210+
let b = Local 0
211+
l = Local 1
212+
in case_
213+
(Atom b)
214+
[ ( stgBlock
215+
, ( 1
216+
, force_
217+
(appfn_ (Global "Emit.forceExportMetadataKVList") [l])
218+
(appcon_ stgUnit [])))
219+
]
220+
221+
222+
forceExportMetadataKVList :: LambdaForm
223+
forceExportMetadataKVList =
224+
lam_ 0 1 $
225+
ann_ "Emit.forceExportMetadataKVList" 0 $
226+
let l = Local 0
227+
h = Local 1
228+
t = Local 2
229+
in case_
230+
(Atom l)
231+
[ (stgNil, (0, Atom (Global "KNIL")))
232+
, ( stgCons
233+
, ( 2
234+
, force_ (appfn_ (Global "Emit.forceKVNatPair") [h]) $
235+
force_ (appfn_ (Global "Emit.forceExportMetadataKVList") [t]) $
236+
appcon_ stgUnit []))
237+
]
238+
239+
240+
isRenderMetadataKey :: LambdaForm
241+
isRenderMetadataKey =
242+
lam_ 0 1 $
243+
ann_ "Emit.isRenderMetadataKey" 0 $
244+
caselit_
245+
(Atom (Local 0))
246+
[ (NativeSymbol "export", Atom (Global "TRUE"))
247+
, (NativeSymbol "tag", Atom (Global "TRUE"))
248+
] $
249+
Just (Atom (Global "FALSE"))
250+
251+
252+
forceKVNatPair :: LambdaForm
253+
forceKVNatPair =
254+
lam_ 0 1 $
255+
ann_ "Emit.forceKVNatPair" 0 $
256+
let pr = Local 0
257+
prh = Local 1
258+
in casedef_
259+
(Atom pr)
260+
[ ( stgCons
261+
, ( 2
262+
, caselit_
263+
(appfn_ (Global "Emit.isRenderMetadataKey") [prh])
264+
[(NativeBool True, appfn_ (Global "seqNatList") [pr])]
265+
(Just (Atom pr))))
266+
]
267+
(panic "Invalid pair (not cons) while evaluating render metadata")

0 commit comments

Comments
 (0)