-
Notifications
You must be signed in to change notification settings - Fork 173
/
Copy pathEval.hs
1187 lines (1143 loc) · 52.8 KB
/
Eval.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{-# LANGUAGE LambdaCase #-}
module Eval where
import ColorText
import Commands
import Context
import Control.Applicative
import Control.Exception
import Control.Monad.State
import Data.Either (fromRight)
import Data.Foldable (foldlM, foldrM)
import Data.List (foldl', isSuffixOf)
import Data.List.Split (splitOn, splitWhen)
import Data.Maybe (fromJust, fromMaybe, isJust)
import Emit
import qualified Env as E
import EvalError
import Expand
import Forms
import Infer
import Info
import qualified Map
import qualified Meta
import Obj
import Parsing
import Path
import Primitives
import Project
import Qualify
import qualified Set
import System.Exit (ExitCode (..), exitSuccess, exitWith)
import System.Process (readProcessWithExitCode)
import qualified Text.Parsec as Parsec
import TypeError
import Types
import Util
import Prelude hiding (exp, mod)
-- TODO: Formalize "lookup order preference" a bit better and move into
-- the Context module.
data LookupPreference
= PreferDynamic
| PreferGlobal
| PreferLocal [SymPath]
deriving (Show)
data Resolver
= ResolveGlobal
| ResolveLocal
type Evaluator = [XObj] -> IO (Context, Either EvalError XObj)
-- Prefer dynamic bindings
evalDynamic :: Resolver -> Context -> XObj -> IO (Context, Either EvalError XObj)
evalDynamic resolver ctx xobj = eval ctx xobj PreferDynamic resolver
-- Prefer global bindings
evalStatic :: Resolver -> Context -> XObj -> IO (Context, Either EvalError XObj)
evalStatic resolver ctx xobj = eval ctx xobj PreferGlobal resolver
-- | Dynamic (REPL) evaluation of XObj:s (s-expressions)
-- Note: You might find a bunch of code of the following form both here and in
-- macroExpand:
--
-- pure (ctx, do res <- <something>
-- Right <something else with res>)
--
-- This might a little weird to you, and rightfully so. Through the nested do
-- we ensure that an evaluation is forced where it needs to be, since we depend
-- on the state here; eval is inherently stateful (because it carries around
-- the compiler’s context, which might change after each macro expansion), and
-- it gets real weird with laziness. (Note to the note: this code is mostly a
-- remnant of us using StateT, and might not be necessary anymore since we
-- switched to more explicit state-passing.)
eval :: Context -> XObj -> LookupPreference -> Resolver -> IO (Context, Either EvalError XObj)
eval ctx xobj@(XObj o info ty) preference resolver =
case o of
Lst body -> eval' body
Sym spath@(SymPath p n) _ ->
pure $
case resolver of
ResolveGlobal -> unwrapLookup ((tryAllLookups preference) >>= checkStatic)
ResolveLocal -> unwrapLookup (tryAllLookups preference)
where
checkStatic v@(_, Right (XObj (Lst ((XObj obj _ _) : _)) _ _)) =
if isResolvableStaticObj obj
then pure (ctx, Left (HasStaticCall xobj info))
else pure v
checkStatic v = pure v
-- all else failed, error.
unwrapLookup =
fromMaybe
(throwErr (SymbolNotFound spath) ctx info)
-- Try all lookups performs lookups for symbols based on a given
-- lookup preference.
tryAllLookups :: LookupPreference -> Maybe (Context, Either EvalError XObj)
tryAllLookups PreferDynamic = (getDynamic) <|> fullLookup
tryAllLookups PreferGlobal = (getGlobal spath) <|> fullLookup
tryAllLookups (PreferLocal shadows) = (if spath `elem` shadows then (getLocal n) else (getDynamic)) <|> fullLookup
fullLookup = (tryDynamicLookup <|> (if null p then tryInternalLookup spath <|> tryLookup spath else tryLookup spath))
getDynamic :: Maybe (Context, Either EvalError XObj)
getDynamic =
do
(Binder _ found) <- maybeId (E.findValueBinder (contextGlobalEnv ctx) (SymPath ("Dynamic" : p) n))
pure (ctx, Right (resolveDef found))
getGlobal :: SymPath -> Maybe (Context, Either EvalError XObj)
getGlobal path =
do
(Binder meta found) <- maybeId (E.findValueBinder (contextGlobalEnv ctx) path)
checkPrivate meta found
tryDynamicLookup :: Maybe (Context, Either EvalError XObj)
tryDynamicLookup =
do
(Binder meta found) <- maybeId (E.searchValueBinder (contextGlobalEnv ctx) (SymPath ("Dynamic" : p) n))
checkPrivate meta found
getLocal :: String -> Maybe (Context, Either EvalError XObj)
getLocal name =
do
internal <- contextInternalEnv ctx
(Binder _ found) <- maybeId (E.getValueBinder internal name)
pure (ctx, Right (resolveDef found))
-- TODO: Deprecate this function?
-- The behavior here is a bit nefarious since it relies on cached
-- environment parents (it calls `search` on the "internal" binder).
-- But for now, it seems to be needed for some cases.
tryInternalLookup :: SymPath -> Maybe (Context, Either EvalError XObj)
tryInternalLookup path =
--trace ("Looking for internally " ++ show path) -- ++ show (fmap (fmap E.binders . E.parent) (contextInternalEnv ctx)))
( contextInternalEnv ctx
>>= \e ->
maybeId (E.searchValueBinder e path)
>>= \(Binder meta found) -> checkPrivate meta found
)
tryLookup :: SymPath -> Maybe (Context, Either EvalError XObj)
tryLookup path =
( maybeId (E.searchValueBinder (contextGlobalEnv ctx) path)
>>= \(Binder meta found) -> checkPrivate meta found
)
<|> ( (maybeId (E.searchValueBinder (contextGlobalEnv ctx) (SymPath ((contextPath ctx) ++ p) n)))
>>= \(Binder meta found) -> checkPrivate meta found
)
<|> ( maybeId (lookupBinderInTypeEnv ctx path)
>>= \(Binder _ found) -> pure (ctx, Right (resolveDef found))
)
<|> ( foldl
(<|>)
Nothing
( map
( \(SymPath p' n') ->
maybeId (E.searchValueBinder (contextGlobalEnv ctx) (SymPath (p' ++ (n' : p)) n))
>>= \(Binder meta found) -> checkPrivate meta found
)
(Set.toList (envUseModules (contextGlobalEnv ctx)))
)
)
checkPrivate meta found =
pure $
if metaIsTrue meta "private"
then throwErr (PrivateBinding (getPath found)) ctx info
else (ctx, Right (resolveDef found))
Arr objs -> do
(newCtx, evaled) <- foldlM successiveEval (ctx, Right []) objs
pure
( newCtx,
do
ok <- evaled
Right (XObj (Arr ok) info ty)
)
StaticArr objs -> do
(newCtx, evaled) <- foldlM successiveEval (ctx, Right []) objs
pure
( newCtx,
do
ok <- evaled
Right (XObj (StaticArr ok) info ty)
)
_ -> do
(nctx, res) <- annotateWithinContext ctx xobj
pure $ case res of
Left e -> (nctx, Left e)
Right (val, _) -> (nctx, Right val)
where
resolveDef (XObj (Lst [XObj DefDynamic _ _, _, value]) _ _) = value
resolveDef (XObj (Lst [XObj LocalDef _ _, _, value]) _ _) = value
resolveDef x = x
eval' form =
case validate form of
Left e -> pure (evalError ctx (format e) (xobjInfo xobj))
Right form' ->
case form' of
(IfPat _ _ _ _) -> evaluateIf form'
(DefnPat _ _ _ _) -> specialCommandDefine ctx xobj
(DefPat _ _ _) -> specialCommandDefine ctx xobj
(ThePat _ _ _) -> evaluateThe form'
(LetPat _ _ _) -> evaluateLet form'
(FnPat _ _ _) -> evaluateFn form'
(AppPat (ClosurePat _ _ _) _) -> evaluateClosure form'
(AppPat (DynamicFnPat _ _ _) _) -> evaluateDynamicFn form'
(AppPat (MacroPat _ _ _) _) -> evaluateMacro form'
(AppPat (CommandPat _ _ _) _) -> evaluateCommand form'
(AppPat (PrimitivePat _ _ _) _) -> evaluatePrimitive form'
(WithPat _ sym@(SymPat path _) forms) -> specialCommandWith ctx sym path forms
(DoPat _ forms) -> evaluateSideEffects forms
(WhilePat _ cond body) -> specialCommandWhile ctx cond body
(SetPat _ iden value) -> specialCommandSet ctx (iden : [value])
-- This next match is a bit redundant looking at first glance, but
-- it is necessary to prevent hangs on input such as: `((def foo 2)
-- 4)`. Ideally, we could perform only *one* static check (the one
-- we do in eval). But the timing is wrong.
-- The `def` in the example above initially comes into the
-- evaluator as a *Sym*, **not** as a `Def` xobj. So, we need to
-- discriminate on the result of evaluating the symbol to eagerly
-- break the evaluation loop, otherwise we will proceed to evaluate
-- the def form, yielding Unit, and attempt to reevaluate unit
-- indefinitely on subsequent eval loops.
-- Importantly, the loop *is only broken on literal nested lists*.
-- That is, passing a *symbol* that, e.g. resolves to a defn list, won't
-- break our normal loop.
(AppPat self@(ListPat (x@(SymPat _ _) : _)) args) ->
do
(_, evald) <- eval ctx x preference ResolveGlobal
case evald of
Left err -> pure (evalError ctx (show err) (xobjInfo xobj))
Right x' -> case checkStatic' x' of
Right _ -> evaluateApp (self : args)
Left er -> pure (ctx, Left er)
(AppPat (ListPat _) _) -> evaluateApp form'
(AppPat (SymPat _ _) _) -> evaluateApp form'
(AppPat (XObj other _ _) _)
| isResolvableStaticObj other ->
pure (ctx, (Left (HasStaticCall xobj info)))
[] -> pure (ctx, dynamicNil)
_ -> pure (throwErr (UnknownForm xobj) ctx (xobjInfo xobj))
checkStatic' (XObj Def _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Defn _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Interface _ _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Instantiate _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Deftemplate _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (External _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj (Match _) _ _) = Left (HasStaticCall xobj info)
checkStatic' (XObj Ref _ _) = Left (HasStaticCall xobj info)
checkStatic' x' = Right x'
successiveEval (ctx', acc) x =
case acc of
Left _ -> pure (ctx', acc)
Right l -> do
(newCtx, evald) <- eval ctx' x preference resolver
pure $ case evald of
Right res -> (newCtx, Right (l ++ [res]))
Left err -> (newCtx, Left err)
evaluateIf :: Evaluator
evaluateIf (IfPat _ cond true false) = do
(newCtx, evd) <- eval ctx cond preference ResolveLocal
case evd of
Right cond' ->
case xobjObj cond' of
Bol b -> eval newCtx (if b then true else false) preference ResolveLocal
_ ->
pure (throwErr (IfContainsNonBool cond) ctx (xobjInfo cond))
Left e -> pure (newCtx, Left e)
evaluateIf _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateThe :: Evaluator
evaluateThe (ThePat the t value) = do
(newCtx, evaledValue) <- expandAll (evalDynamic ResolveLocal) ctx value -- TODO: Why expand all here?
pure
( newCtx,
do
okValue <- evaledValue
Right (XObj (Lst [the, t, okValue]) info ty)
)
evaluateThe _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateLet :: Evaluator
evaluateLet (LetPat _ (ArrPat bindings) body) = do
let binds = unwrapVar (pairwise bindings) []
ni = Env Map.empty (contextInternalEnv ctx) Nothing Set.empty InternalEnv 0
eitherCtx <- foldrM successiveEval' (Right (replaceInternalEnv ctx ni)) binds
case eitherCtx of
Left err -> pure (ctx, Left err)
Right newCtx -> do
(finalCtx, evaledBody) <- eval newCtx body (PreferLocal (map (\(name, _) -> (SymPath [] name)) binds)) ResolveLocal
let e = fromMaybe E.empty $ contextInternalEnv finalCtx
parentEnv = envParent e
pure
( replaceInternalEnvMaybe finalCtx parentEnv,
do
okBody <- evaledBody
Right okBody
)
where
unwrapVar [] acc = acc
unwrapVar ((XObj (Sym (SymPath [] x) _) _ _, y) : xs) acc = unwrapVar xs ((x, y) : acc)
unwrapVar _ _ = error "unwrapvar"
successiveEval' (n, x) =
\case
err@(Left _) -> pure err
Right ctx' -> do
-- Bind a reference to the let bind in a recursive
-- environment. This permits recursion in anonymous functions
-- in let binds such as:
-- (let [f (fn [x] (if (= x 1) x (f (dec x))))] (f 10))
let origin = (contextInternalEnv ctx')
recFix = (E.recursive origin (Just "let-rec-env") 0)
envWithSelf = fromRight recFix $ if isFn x then E.insertX recFix (SymPath [] n) x else Right recFix
ctx'' = replaceInternalEnv ctx' envWithSelf
(newCtx, res) <- eval ctx'' x preference resolver
case res of
Right okX ->
pure $ Right (fromRight (error "Failed to eval let binding!!") (bindLetDeclaration (newCtx {contextInternalEnv = origin}) n okX))
Left err -> pure $ Left err
evaluateLet _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateFn :: Evaluator
evaluateFn (FnPat self args body) = do
(newCtx, expanded) <- macroExpand ctx body
pure $
case expanded of
Right b ->
(newCtx, Right (XObj (Closure (XObj (Lst [self, args, b]) info ty) (CCtx newCtx)) info ty))
Left err -> (ctx, Left err)
evaluateFn _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateClosure :: Evaluator
evaluateClosure (AppPat (ClosurePat params body c) args) = do
(newCtx, evaledArgs) <- foldlM successiveEval (ctx, Right []) args
case evaledArgs of
Right okArgs -> do
let newGlobals = (contextGlobalEnv newCtx) <> (contextGlobalEnv c)
newTypes = TypeEnv $ (getTypeEnv (contextTypeEnv newCtx)) <> (getTypeEnv (contextTypeEnv c))
updater = replaceHistory' (contextHistory ctx) . replaceGlobalEnv' newGlobals . replaceTypeEnv' newTypes
(ctx', res) <- apply (updater c) body params okArgs
pure (replaceGlobalEnv newCtx (contextGlobalEnv ctx'), res)
Left err -> pure (newCtx, Left err)
evaluateClosure _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateDynamicFn :: Evaluator
evaluateDynamicFn (AppPat (DynamicFnPat _ params body) args) = do
(newCtx, evaledArgs) <- foldlM successiveEval (ctx, Right []) args
case evaledArgs of
Right okArgs -> apply newCtx body params okArgs
Left err -> pure (newCtx, Left err)
evaluateDynamicFn _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateMacro :: Evaluator
evaluateMacro (AppPat (MacroPat _ params body) args) = do
(ctx', res) <- apply ctx body params args
case res of
Right xobj' -> macroExpand ctx' xobj'
Left _ -> pure (ctx, res)
evaluateMacro _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateCommand :: Evaluator
evaluateCommand (AppPat (CommandPat (NullaryCommandFunction nullary) _ _) []) =
nullary ctx
evaluateCommand (AppPat (CommandPat (UnaryCommandFunction unary) _ _) [x]) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) [x]
case evaledArgs of
Right [x'] -> unary c x'
Left err -> pure (ctx, Left err)
_ -> error "eval: failed to evaluate command arguments"
evaluateCommand (AppPat (CommandPat (BinaryCommandFunction binary) _ _) [x, y]) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) [x, y]
case evaledArgs of
Right [x', y'] -> binary c x' y'
Left err -> pure (ctx, Left err)
_ -> error "eval: failed to evaluate command arguments"
evaluateCommand (AppPat (CommandPat (TernaryCommandFunction ternary) _ _) [x, y, z]) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) [x, y, z]
case evaledArgs of
Right [x', y', z'] -> ternary c x' y' z'
Left err -> pure (ctx, Left err)
_ -> error "eval: failed to evaluate command arguments"
evaluateCommand (AppPat (CommandPat (VariadicCommandFunction variadic) _ _) args) = do
(c, evaledArgs) <- foldlM successiveEval (ctx, Right []) args
case evaledArgs of
Right args' -> variadic c args'
Left err -> pure (ctx, Left err)
-- Should be caught during validation
evaluateCommand (AppPat (CommandPat _ _ _) _) =
pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateCommand _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluatePrimitive :: Evaluator
evaluatePrimitive (AppPat p@(PrimitivePat (NullaryPrimitive nullary) _ _) []) =
nullary p ctx
evaluatePrimitive (AppPat p@(PrimitivePat (UnaryPrimitive unary) _ _) [x]) = do
unary p ctx x
evaluatePrimitive (AppPat p@(PrimitivePat (BinaryPrimitive binary) _ _) [x, y]) = do
binary p ctx x y
evaluatePrimitive (AppPat p@(PrimitivePat (TernaryPrimitive ternary) _ _) [x, y, z]) = do
ternary p ctx x y z
evaluatePrimitive (AppPat p@(PrimitivePat (QuaternaryPrimitive quaternary) _ _) [x, y, z, w]) = do
quaternary p ctx x y z w
evaluatePrimitive (AppPat p@(PrimitivePat (VariadicPrimitive variadic) _ _) args) = do
variadic p ctx args
-- Should be caught during validation
evaluatePrimitive (AppPat (PrimitivePat _ _ _) _) =
pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluatePrimitive _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateApp :: Evaluator
evaluateApp (AppPat f' args) =
case f' of
l@(ListPat _) -> go l ResolveLocal
sym@(SymPat _ _) -> go sym resolver
_ -> pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
where
go x resolve =
do
(newCtx, f) <- eval ctx x preference resolve
case f of
Right fun -> do
(newCtx', res) <- eval (pushFrame newCtx xobj) (XObj (Lst (fun : args)) (xobjInfo x) (xobjTy x)) preference ResolveLocal
pure (popFrame newCtx', res)
x' -> pure (newCtx, x')
evaluateApp _ = pure (evalError ctx (format (GenericMalformed xobj)) (xobjInfo xobj))
evaluateSideEffects :: Evaluator
evaluateSideEffects forms = do
foldlM successiveEval' (ctx, dynamicNil) forms
where
successiveEval' (ctx', acc) x =
case acc of
err@(Left _) -> pure (ctx', err)
Right _ -> eval ctx' x preference resolver
macroExpand :: Context -> XObj -> IO (Context, Either EvalError XObj)
macroExpand ctx xobj =
case xobj of
XObj (Arr objs) i t -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) objs
pure
( newCtx,
do
ok <- expanded
Right (XObj (Arr ok) i t)
)
XObj (StaticArr objs) i t -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) objs
pure
( newCtx,
do
ok <- expanded
Right (XObj (StaticArr ok) i t)
)
XObj (Lst (XObj (Sym (SymPath [] "defmodule") _) _ _ : _)) _ _ ->
pure (ctx, Right xobj)
XObj (Lst [XObj (Sym (SymPath [] "quote") _) _ _, _]) _ _ ->
pure (ctx, Right xobj)
XObj (Lst [XObj (Lst (XObj Macro _ _ : _)) _ _]) _ _ -> evalDynamic ResolveLocal ctx xobj
XObj (Lst (x@(XObj (Sym _ _) _ _) : args)) i t -> do
(_, f) <- evalDynamic ResolveLocal ctx x
case f of
Right m@(XObj (Lst (XObj Macro _ _ : _)) _ _) -> do
(newCtx', res) <- evalDynamic ResolveLocal ctx (XObj (Lst (m : args)) i t)
pure (newCtx', res)
_ -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) args
pure
( newCtx,
do
ok <- expanded
Right (XObj (Lst (x : ok)) i t)
)
XObj (Lst objs) i t -> do
(newCtx, expanded) <- foldlM successiveExpand (ctx, Right []) objs
pure
( newCtx,
do
ok <- expanded
Right (XObj (Lst ok) i t)
)
_ -> pure (ctx, Right xobj)
where
successiveExpand (ctx', acc) x =
case acc of
Left _ -> pure (ctx', acc)
Right l -> do
(newCtx, expanded) <- macroExpand ctx' x
pure $ case expanded of
Right res -> (newCtx, Right (l ++ [res]))
Left err -> (newCtx, Left err)
apply :: Context -> XObj -> [XObj] -> [XObj] -> IO (Context, Either EvalError XObj)
apply ctx@Context {contextInternalEnv = internal} body params args =
let allParams = map getName params
in case splitWhen (":rest" ==) allParams of
[a, b] -> callWith a b
[a] -> callWith a []
_ ->
pure (throwErr (MacroBadArgumentSplit allParams) ctx Nothing)
where
callWith proper rest = do
let n = length proper
insideEnv = Env Map.empty internal Nothing Set.empty InternalEnv 0
insideEnv' =
foldl'
(\e (p, x) -> fromRight (error "Couldn't add local def ") (E.insertX e (SymPath [] p) (toLocalDef p x)))
insideEnv
(zip proper (take n args))
insideEnv'' =
if null rest
then insideEnv'
else
fromRight
(error "couldn't insert into inside env")
( E.insertX
insideEnv'
(SymPath [] (head rest))
(XObj (Lst (drop n args)) Nothing Nothing)
)
binds = if null rest then proper else proper ++ [(head rest)]
(c, r) <- (eval (replaceInternalEnv ctx insideEnv'') body (PreferLocal (map (\x -> (SymPath [] x)) binds)) ResolveLocal)
pure (c {contextInternalEnv = internal}, r)
-- | Parses a string and then converts the resulting forms to commands, which are evaluated in order.
executeString :: Bool -> Bool -> Context -> String -> String -> IO Context
executeString = executeStringAtLine 1
executeStringAtLine :: Int -> Bool -> Bool -> Context -> String -> String -> IO Context
executeStringAtLine line doCatch printResult ctx input fileName =
if doCatch then catch exec (catcher ctx) else exec
where
exec = case parseAtLine line input fileName of
Left parseError ->
let sourcePos = Parsec.errorPos parseError
parseErrorXObj =
XObj
(Lst [])
( Just
dummyInfo
{ infoFile = fileName,
infoLine = Parsec.sourceLine sourcePos,
infoColumn = Parsec.sourceColumn sourcePos
}
)
Nothing
in do
_ <- liftIO $ treatErr ctx (replaceChars (Map.fromList [('\n', " ")]) (show parseError)) parseErrorXObj
pure ctx
Right xobjs -> do
(res, ctx') <-
foldM
interactiveFolder
(XObj (Lst []) (Just dummyInfo) (Just UnitTy), ctx)
xobjs
when
(printResult && xobjTy res /= Just UnitTy)
(putStrLnWithColor Yellow ("=> " ++ pretty res))
pure ctx'
interactiveFolder (_, context) =
executeCommand context
treatErr ctx' e xobj = do
let fppl = projectFilePathPrintLength (contextProj ctx')
case contextExecMode ctx' of
Check -> putStrLn (machineReadableInfoFromXObj fppl xobj ++ " " ++ e)
_ -> emitErrorWithLabel "PARSE ERROR" e
throw CancelEvaluationException
-- | Used by functions that has a series of forms to evaluate and need to fold over them (producing a new Context in the end)
folder :: Context -> XObj -> IO Context
folder context xobj = do
(_, ctx) <- executeCommand context xobj
pure ctx
-- | Take a repl command and execute it.
executeCommand :: Context -> XObj -> IO (XObj, Context)
executeCommand ctx@(Context env _ _ _ _ _ _ _) xobj =
do
when (isJust (envModuleName env)) $
error ("Global env module name is " ++ fromJust (envModuleName env) ++ " (should be Nothing).")
-- The s-expression command is a special case that prefers global/static bindings over dynamic bindings
-- when given a naked binding (no path) as an argument; (s-expr inc)
(newCtx, result) <- if xobjIsSexp xobj then evalStatic ResolveGlobal ctx xobj else evalDynamic ResolveGlobal ctx xobj
case result of
Left e@EvalError {} -> do
reportExecutionError newCtx (show e)
pure (xobj, newCtx)
-- special case: calling something static at the repl
Right (XObj (Lst (XObj (Lst (XObj (Defn _) _ _ : (XObj (Sym (SymPath [] "main") _) _ _) : _)) _ _ : _)) _ _) ->
executeCommand newCtx (withBuildAndRun (XObj (Lst []) (Just dummyInfo) Nothing))
Left (HasStaticCall _ _) ->
callFromRepl newCtx xobj
Right res -> pure (res, newCtx)
where
callFromRepl newCtx xobj' = do
(nc, r) <- annotateWithinContext newCtx xobj'
case r of
Right (ann, deps) -> do
ctxWithDeps <- liftIO $ foldM (define True) nc (map Qualified deps)
executeCommand ctxWithDeps (withBuildAndRun (buildMainFunction ann))
Left err -> do
reportExecutionError nc (show err)
pure (xobj', nc)
withBuildAndRun xobj' =
XObj
( Lst
[ XObj Do (Just dummyInfo) Nothing,
xobj',
XObj
(Lst [XObj (Sym (SymPath [] "build") Symbol) (Just dummyInfo) Nothing, trueXObj])
(Just dummyInfo)
Nothing,
XObj
(Lst [XObj (Sym (SymPath [] "run") Symbol) (Just dummyInfo) Nothing])
(Just dummyInfo)
Nothing,
(XObj (Lst []) (Just dummyInfo) (Just UnitTy))
]
)
(Just dummyInfo)
Nothing
xobjIsSexp (XObj (Lst (XObj (Sym (SymPath [] "s-expr") Symbol) _ _ : _)) _ _) = True
xobjIsSexp _ = False
reportExecutionError :: Context -> String -> IO ()
reportExecutionError ctx errorMessage =
case contextExecMode ctx of
Check -> putStrLn errorMessage
_ ->
do
emitErrorBare errorMessage
throw CancelEvaluationException
-- | Decides what to do when the evaluation fails for some reason.
catcher :: Context -> CarpException -> IO Context
catcher ctx exception =
case exception of
(ShellOutException message rc) -> emitErrorWithLabel "RUNTIME ERROR" message >> stop rc
CancelEvaluationException -> stop 1
EvalException err -> emitError (show err) >> stop 1
where
stop rc =
case contextExecMode ctx of
Repl -> pure ctx
Build -> exitWith (ExitFailure rc)
Install _ -> exitWith (ExitFailure rc)
BuildAndRun -> exitWith (ExitFailure rc)
Check -> exitSuccess
specialCommandWith :: Context -> XObj -> SymPath -> [XObj] -> IO (Context, Either EvalError XObj)
specialCommandWith ctx _ path forms = do
let env = fromMaybe (contextGlobalEnv ctx) $ contextInternalEnv ctx <|> maybeId (innermostModuleEnv ctx) <|> Just (contextGlobalEnv ctx)
useThese = envUseModules env
env' = env {envUseModules = Set.insert path useThese}
ctx' = replaceGlobalEnv ctx env'
ctxAfter <- liftIO $ foldM folder ctx' forms
let envAfter = fromMaybe (contextGlobalEnv ctxAfter) (contextInternalEnv ctxAfter <|> maybeId (innermostModuleEnv ctxAfter) <|> Just (contextGlobalEnv ctxAfter))
-- undo ALL use:s made inside the 'with'.
ctxAfter' = replaceGlobalEnv ctx (envAfter {envUseModules = useThese})
pure (ctxAfter', dynamicNil)
specialCommandDefine :: Context -> XObj -> IO (Context, Either EvalError XObj)
specialCommandDefine ctx xobj =
do
(newCtx, result) <- annotateWithinContext ctx xobj
case result of
Right (annXObj, annDeps) ->
do
ctxWithDeps <- liftIO $ foldM (define True) newCtx (map Qualified annDeps)
ctxWithDef <- liftIO $ define False ctxWithDeps (Qualified annXObj)
pure (ctxWithDef, dynamicNil)
Left err ->
pure (ctx, Left err)
specialCommandWhile :: Context -> XObj -> XObj -> IO (Context, Either EvalError XObj)
specialCommandWhile ctx cond body = do
(newCtx, evd) <- evalDynamic ResolveLocal ctx cond
case evd of
Right c ->
case xobjObj c of
Bol b ->
if b
then do
(newCtx', _) <- evalDynamic ResolveLocal newCtx body
specialCommandWhile newCtx' cond body
else pure (newCtx, dynamicNil)
_ ->
pure (throwErr (WhileContainsNonBool c) ctx (xobjInfo c))
Left e -> pure (newCtx, Left e)
getSigFromDefnOrDef :: Context -> XObj -> Either EvalError (Maybe (Ty, XObj))
getSigFromDefnOrDef ctx xobj =
let pathStrings = contextPath ctx
globalEnv = contextGlobalEnv ctx
fppl = projectFilePathPrintLength (contextProj ctx)
path = getPath xobj
fullPath = case path of
(SymPath [] _) -> consPath pathStrings path
(SymPath _ _) -> path
metaData = either (const emptyMeta) id (E.lookupMeta globalEnv fullPath)
in case Meta.get "sig" metaData of
Just foundSignature ->
case xobjToTy foundSignature of
Just t ->
let sigToken = XObj (Sym (SymPath [] "sig") Symbol) Nothing Nothing
nameToken = XObj (Sym (SymPath [] (getName xobj)) Symbol) Nothing Nothing
recreatedSigForm = XObj (Lst [sigToken, nameToken, foundSignature]) Nothing (Just MacroTy)
in Right (Just (t, recreatedSigForm))
Nothing -> Left (EvalError ("Can't use '" ++ pretty foundSignature ++ "' as a type signature") (contextHistory ctx) fppl (xobjInfo xobj))
Nothing -> Right Nothing
annotateWithinContext :: Context -> XObj -> IO (Context, Either EvalError (XObj, [XObj]))
annotateWithinContext ctx xobj = do
let globalEnv = contextGlobalEnv ctx
typeEnv = contextTypeEnv ctx
sig = getSigFromDefnOrDef ctx xobj
fppl = projectFilePathPrintLength (contextProj ctx)
case sig of
Left err -> pure (ctx, Left err)
Right okSig -> do
(_, expansionResult) <- expandAll (evalDynamic ResolveLocal) ctx xobj
case expansionResult of
Left err -> pure (ctx, Left err)
Right expanded ->
let xobjFullSymbols = qualify ctx expanded
in case xobjFullSymbols of
Left err -> pure (evalError ctx (show err) (xobjInfo xobj))
Right xs ->
case annotate typeEnv globalEnv xs okSig of
Left err ->
-- TODO: Replace this with a single call to evalError (which already checks the execution mode)
case contextExecMode ctx of
Check -> pure (evalError ctx (joinLines (machineReadableErrorStrings fppl err)) Nothing)
_ -> pure (evalError ctx (show err) (xobjInfo xobj))
Right ok -> pure (ctx, Right ok)
primitiveDefmodule :: VariadicPrimitiveCallback
primitiveDefmodule xobj ctx@(Context env i tenv pathStrings _ _ _ _) (XObj (Sym (SymPath [] moduleName) _) si _ : innerExpressions) =
-- N.B. The `envParent` rewrite at the end of this line is important!
-- lookups delve into parent envs by default, which is normally what we want, but in this case it leads to problems
-- when submodules happen to share a name with an existing module or type at the global level.
either (const (defineNewModule emptyMeta)) updateExistingModule (E.searchValueBinder ((fromRight env (E.getInnerEnv env pathStrings)) {envParent = Nothing}) (SymPath [] moduleName))
>>= defineModuleBindings
>>= \(newCtx, result) ->
let updater c = (c {contextInternalEnv = (E.parent =<< contextInternalEnv c)})
in case result of
Left err -> pure (newCtx, Left err)
Right _ -> pure (updater (popModulePath newCtx), dynamicNil)
where
--------------------------------------------------------------------------------
-- Update an existing module by modifying its environment parents and updating the current context path.
updateExistingModule :: Binder -> IO (Context, Either EvalError XObj)
updateExistingModule (Binder _ (XObj (Mod innerEnv _) _ _)) =
let updateContext =
replacePath' (contextPath ctx ++ [moduleName])
. replaceInternalEnv' (innerEnv {envParent = i})
in pure (updateContext ctx, dynamicNil)
updateExistingModule (Binder meta (XObj (Lst [XObj MetaStub _ _, _]) _ _)) =
defineNewModule meta
updateExistingModule _ =
pure (throwErr (ModuleRedefinition moduleName) ctx (xobjInfo xobj))
--------------------------------------------------------------------------------
-- Define a brand new module with a context's current environments as its parents.
defineNewModule :: MetaData -> IO (Context, Either EvalError XObj)
defineNewModule meta =
pure (fromRight ctx (updater ctx), dynamicNil)
where
moduleDefs = E.new (Just (fromRight env (E.getInnerEnv env pathStrings))) (Just moduleName)
moduleTypes = E.new (Just tenv) (Just moduleName)
newModule = XObj (Mod moduleDefs moduleTypes) si (Just ModuleTy)
updater = \c ->
insertInGlobalEnv' (markQualified (SymPath pathStrings moduleName)) (Binder meta newModule) c
>>= pure . replaceInternalEnv' (moduleDefs {envParent = i})
>>= pure . replacePath' (contextPath ctx ++ [moduleName])
--------------------------------------------------------------------------------
-- Define bindings for the module.
defineModuleBindings :: (Context, Either EvalError XObj) -> IO (Context, Either EvalError XObj)
defineModuleBindings (context, Left e) = pure (context, Left e)
defineModuleBindings (context, _) =
foldM step (context, dynamicNil) innerExpressions
step :: (Context, Either EvalError XObj) -> XObj -> IO (Context, Either EvalError XObj)
step (ctx', Left e) _ = pure (ctx', Left e)
step (ctx', Right _) expressions =
macroExpand ctx' expressions
>>= \(ctx'', res) -> case res of
Left err -> pure (ctx'', Left err)
Right r -> evalDynamic ResolveLocal ctx'' r
primitiveDefmodule _ ctx (x : _) =
pure (throwErr (DefmoduleContainsNonSymbol x) ctx (xobjInfo x))
primitiveDefmodule xobj ctx [] =
pure (throwErr DefmoduleNoArgs ctx (xobjInfo xobj))
-- | "NORMAL" COMMANDS (just like the ones in Command.hs, but these need access to 'eval', etc.)
-- | Command for loading a Carp file.
commandLoad :: VariadicCommandCallback
commandLoad ctx [xobj@(XObj (Str path) i _), XObj (Str toLoad) _ _] =
loadInternal ctx xobj path i (Just toLoad) DoesReload
commandLoad ctx [XObj (Str _) _ _, x] =
pure $ throwErr (loadInvalidArgs [x]) ctx (xobjInfo x)
commandLoad ctx [x, _] =
pure $ throwErr (loadInvalidArgs [x]) ctx (xobjInfo x)
commandLoad ctx [xobj@(XObj (Str path) i _)] =
loadInternal ctx xobj path i Nothing DoesReload
commandLoad ctx x =
pure $ throwErr (loadInvalidArgs x) ctx Nothing
commandLoadOnce :: VariadicCommandCallback
commandLoadOnce ctx [xobj@(XObj (Str path) i _), XObj (Str toLoad) _ _] =
loadInternal ctx xobj path i (Just toLoad) Frozen
commandLoadOnce ctx [XObj (Str _) _ _, x] =
pure $ throwErr (loadOnceInvalidArgs [x]) ctx (xobjInfo x)
commandLoadOnce ctx [x, _] =
pure $ throwErr (loadOnceInvalidArgs [x]) ctx (xobjInfo x)
commandLoadOnce ctx [xobj@(XObj (Str path) i _)] =
loadInternal ctx xobj path i Nothing Frozen
commandLoadOnce ctx x =
pure $ throwErr (loadOnceInvalidArgs x) ctx Nothing
loadInternal :: Context -> XObj -> String -> Maybe Info -> Maybe String -> ReloadMode -> IO (Context, Either EvalError XObj)
loadInternal ctx xobj path i fileToLoad reloadMode = do
let proj = contextProj ctx
libDir <- liftIO $ cachePath $ projectLibDir proj
let relativeTo = case i of
Just ii ->
case infoFile ii of
"REPL" -> "."
file -> takeDirectory file
Nothing -> "."
carpDir = projectCarpDir proj
fullSearchPaths =
path :
(relativeTo </> path) :
map (</> path) (projectCarpSearchPaths proj) -- the path from the file that contains the '(load)', or the current directory if not loading from a file (e.g. the repl)
++ [carpDir </> "core" </> path] -- user defined search paths
++ [libDir </> path]
firstM _ [] = pure Nothing
firstM p (x : xs) = do
q <- p x
if q
then pure $ Just x
else firstM p xs
existingPath <- liftIO $ firstM doesFileExist fullSearchPaths
case existingPath of
Nothing ->
if '@' `elem` path
then tryInstall path
else pure $ invalidPath ctx path
Just firstPathFound ->
do
canonicalPath <- liftIO (canonicalizePath firstPathFound)
fileThatLoads <- liftIO (canonicalizePath $ maybe "" infoFile i)
if canonicalPath == fileThatLoads
then pure $ cantLoadSelf ctx path
else do
let alreadyLoaded = projectAlreadyLoaded proj ++ frozenPaths proj
if canonicalPath `elem` alreadyLoaded
then pure (ctx, dynamicNil)
else do
contents <- liftIO $ slurp canonicalPath
let files = projectFiles proj
files' =
if canonicalPath `elem` map fst files
then files
else files ++ [(canonicalPath, reloadMode)]
prevStack = projectLoadStack proj
proj' =
proj
{ projectFiles = files',
projectAlreadyLoaded = canonicalPath : alreadyLoaded,
projectLoadStack = canonicalPath : prevStack
}
newCtx <- liftIO $ executeString True False (replaceProject ctx proj') contents canonicalPath
pure (replaceProject newCtx (contextProj newCtx) {projectLoadStack = prevStack}, dynamicNil)
where
frozenPaths proj =
if projectForceReload proj
then [] -- No paths are Frozen when the "force reload" project setting is true.
else map fst $ filter (isFrozen . snd) (projectFiles proj)
isFrozen Frozen = True
isFrozen _ = False
invalidPath ctx' path' =
throwErr (LoadFileNotFound path') ctx' (xobjInfo xobj)
invalidPathWith ctx' path' stderr cleanup cleanupPath = do
_ <- liftIO $ when cleanup (removeDirectoryRecursive cleanupPath)
pure $
throwErr (LoadGitFailure path' stderr) ctx' (xobjInfo xobj)
replaceC _ _ [] = []
replaceC c s (a : b) = if a == c then s ++ replaceC c s b else a : replaceC c s b
cantLoadSelf ctx' path' =
throwErr (LoadRecursiveLoad path') ctx' (xobjInfo xobj)
tryInstall path' =
let split = splitOn "@" path'
in tryInstallWithCheckout (joinWith "@" (init split)) (last split)
fromURL url =
let split = splitOn "/" (replaceC ':' "_COLON_" url)
first = head split
in if first `elem` ["https_COLON_", "http_COLON_"]
then joinWith "/" (tail (tail split))
else
if '@' `elem` first
then joinWith "/" (joinWith "@" (tail (splitOn "@" first)) : tail split)
else url
tryInstallWithCheckout path' toCheckout = do
let proj = contextProj ctx
fpath <- liftIO $ cachePath $ projectLibDir proj </> fromURL path' </> toCheckout
cur <- liftIO getCurrentDirectory
pathExists <- liftIO $ doesPathExist fpath
let cleanup = not pathExists
_ <- liftIO $ createDirectoryIfMissing True fpath
_ <- liftIO $ setCurrentDirectory fpath
(_, txt, _) <- liftIO $ readProcessWithExitCode "git" ["rev-parse", "--abbrev-ref=loose", "HEAD"] ""
if txt == "HEAD\n"
then do
_ <- liftIO $ setCurrentDirectory cur
doGitLoad path' fpath
else do
_ <- liftIO $ readProcessWithExitCode "git" ["init"] ""
_ <- liftIO $ readProcessWithExitCode "git" ["remote", "add", "origin", path'] ""
(x0, _, stderr0) <- liftIO $ readProcessWithExitCode "git" ["fetch", "--all", "--tags"] ""
case x0 of
ExitFailure _ -> do
_ <- liftIO $ setCurrentDirectory cur
invalidPathWith ctx path' stderr0 cleanup fpath
ExitSuccess -> do
(x1, _, stderr1) <- liftIO $ readProcessWithExitCode "git" ["checkout", toCheckout] ""
_ <- liftIO $ setCurrentDirectory cur
case x1 of
ExitSuccess -> doGitLoad path' fpath
ExitFailure _ -> invalidPathWith ctx path' stderr1 cleanup fpath
doGitLoad path' fpath =
case fileToLoad of
Just file -> commandLoad ctx [XObj (Str (fpath </> file)) Nothing Nothing]
Nothing ->
-- we’re guessing what file to use here
let fName = last (splitOn "/" path')
realName' =
if ".git" `isSuffixOf` fName
then take (length fName - 4) fName
else fName
realName =
if ".carp" `isSuffixOf` realName'
then realName'
else realName' ++ ".carp"
fileToLoad' = fpath </> realName
mainToLoad = fpath </> "main.carp"
in do
(newCtx, res) <- commandLoad ctx [XObj (Str fileToLoad') Nothing Nothing]
case res of
ret@(Right _) -> pure (newCtx, ret)
Left _ -> commandLoad ctx [XObj (Str mainToLoad) Nothing Nothing]
-- | Load several files in order.
loadFiles :: Context -> [FilePath] -> IO Context
loadFiles = loadFilesExt commandLoad
loadFilesOnce :: Context -> [FilePath] -> IO Context
loadFilesOnce = loadFilesExt commandLoadOnce
loadFilesExt :: VariadicCommandCallback -> Context -> [FilePath] -> IO Context
loadFilesExt loadCmd = foldM load
where
load :: Context -> FilePath -> IO Context
load ctx file = do
(newCtx, ret) <- loadCmd ctx [XObj (Str file) Nothing Nothing]
case ret of
Left err -> throw (EvalException err)
Right _ -> pure newCtx
-- | Command for reloading all files in the project (= the files that has been loaded before).
commandReload :: NullaryCommandCallback
commandReload ctx = do
let paths = projectFiles (contextProj ctx)
f :: Context -> (FilePath, ReloadMode) -> IO Context
f context (_, Frozen) | not (projectForceReload (contextProj context)) = pure context
f context (filepath, _) =
do
let proj = contextProj context
alreadyLoaded = projectAlreadyLoaded proj
if filepath `elem` alreadyLoaded
then pure context
else do
contents <- slurp filepath
let proj' = proj {projectAlreadyLoaded = filepath : alreadyLoaded}
executeString False False (replaceProject context proj') contents filepath
newCtx <- liftIO (foldM f ctx paths)
pure (newCtx, dynamicNil)
-- | Command for expanding a form and its macros.
commandExpand :: UnaryCommandCallback
commandExpand = macroExpand
-- | This function will show the resulting C code from an expression.
-- | i.e. (Int.+ 2 3) => "_0 = 2 + 3"
commandC :: UnaryCommandCallback
commandC ctx xobj = do
(newCtx, result) <- expandAll (evalDynamic ResolveLocal) ctx xobj
case result of
Left err -> pure (newCtx, Left err)
Right expanded -> do
(_, annotated) <- annotateWithinContext newCtx expanded
case annotated of
Left err -> pure $ evalError newCtx (show err) (xobjInfo xobj)
Right (annXObj, annDeps) ->
do
let cXObj = printC annXObj
cDeps = concatMap printC annDeps
c = cDeps ++ cXObj
liftIO (putStr c)
pure (newCtx, dynamicNil)
-- | This function will return the compiled AST.
commandExpandCompiled :: UnaryCommandCallback
commandExpandCompiled ctx xobj = do
(newCtx, result) <- expandAll (evalDynamic ResolveLocal) ctx xobj
case result of
Left err -> pure (newCtx, Left err)