65 Commits
Author SHA1 Message Date
msyds 7bc5f25984 더 간단한 신택스 annotations
build / build (push) Successful in 1m36s
2026-09-25 14:54:50 -06:00
msyds 38b4047f5d 공통 타이프들 조금 정의 2026-09-25 13:31:47 -06:00
msyds 71a6dda189 peculiar identifiers 2026-09-25 11:30:25 -06:00
msyds 2fdc1382ae idk
build / build (push) Successful in 1m36s
2026-09-24 16:08:43 -06:00
msyds f143df400a prim syntax → builtin 2026-09-21 05:01:20 -06:00
msyds 94ab42007a prim → builtin 2026-09-21 04:58:27 -06:00
msyds 5a030b66b7 2026-09-21 04:42:29 -06:00
msyds b136737a49 2026-09-16 00:32:22 -06:00
msyds 9ab371ba01 expand kinda 2026-09-16 00:08:49 -06:00
msyds daf21ce3f5 works kinda 2026-09-15 02:21:00 -06:00
msyds fc931ca77c 2026-09-15 01:39:23 -06:00
msyds 4a6a15ae39 2026-09-14 21:51:06 -06:00
msyds a16a4a764f 2026-09-11 17:51:24 -06:00
msyds 8aee7c9a58 2026-09-11 16:53:07 -06:00
msyds 1a93f168a9 2026-09-11 16:32:15 -06:00
msyds 309f722712 cleanup options
build / build (push) Successful in 1m14s
2026-09-06 22:15:37 -06:00
msyds 31cc2b1720 cleanup
build / build (push) Successful in 26s
2026-09-06 22:10:43 -06:00
msyds 3cdae9eab4 disable broken tests
build / build (push) Successful in 1m33s
2026-09-06 21:51:39 -06:00
msyds c6036ffbb4 parse/print libraries 2026-09-06 21:39:52 -06:00
msyds 6ebe92e7cd tests 2026-09-06 00:00:30 -06:00
msyds 0f9ba3c51e eval call/cc }:) 2026-09-05 23:58:37 -06:00
msyds 10bd6b733a eval cons 2026-09-05 23:00:44 -06:00
msyds d1588bd917 fix runtime parsing lol
build / build (push) Failing after 1m35s
2026-09-05 20:22:07 -06:00
msyds c495fc064a arith prims 2026-09-05 20:22:07 -06:00
msyds ac39175767 eval agian 2026-09-05 20:22:07 -06:00
msyds 31c610db34 superfuck 2026-09-05 20:22:07 -06:00
msyds 1ec3d35282 arith 2026-09-05 20:22:07 -06:00
msyds 9f37d10e4f ughhh evaluate cps 2026-09-05 20:22:07 -06:00
msyds ba5dc401d9 okay it's time for a hard reset and some thinking </3 2026-09-05 20:22:07 -06:00
msyds bc599df65f shared closures maybe 2026-09-05 20:22:07 -06:00
msyds f26ac50d4e hoist 2026-09-05 20:22:07 -06:00
msyds 3196d8db84 kexp 2026-09-05 20:22:07 -06:00
msyds 75e6c963c7 stupid 2026-09-05 20:22:07 -06:00
msyds 25f1f008bd wip: call/cc = capture/cc × invoke/cc 2026-09-05 20:22:06 -06:00
msyds 276c2c1249 fix: closure-conversion of recursive functions
build / build (push) Successful in 1m28s
2026-08-30 02:12:16 -06:00
msyds 03797d573b mark broken callcc tests 2026-08-30 02:12:16 -06:00
msyds 0df7280236 deconstruct closures only at the bytecode level 2026-08-30 02:12:16 -06:00
msyds a09c00badd works albeit comically inefficiently 2026-08-30 02:12:16 -06:00
msyds e7c0ae9161 return, pushcall
build / build (push) Failing after 1m23s
2026-08-29 07:25:48 -06:00
msyds 5ccb3f3e1a register & label newtypes 2026-08-28 11:39:37 -06:00
msyds 9cb169f9b8 new instrs, tail-call 2026-08-28 11:39:37 -06:00
msyds c0a44c89b4 wip: stack frames 2026-08-28 11:39:37 -06:00
msyds 49292d5d01 wip: call/cc primitives 2026-08-28 11:39:37 -06:00
msyds 679cc076ad fix html output
build / build (push) Successful in 1m21s
2026-08-27 02:16:52 -06:00
msyds 8048573cd8 fix tests
build / build (push) Successful in 1m19s
2026-08-27 02:01:32 -06:00
msyds bbb5d6e99f stack vm throws jalmot
build / build (push) Failing after 1m40s
2026-08-27 01:45:10 -06:00
msyds 1f40120740 dotted list and such 2026-08-27 01:43:42 -06:00
msyds 196dd0d1b3 blah 2026-08-27 01:02:16 -06:00
msyds 009a154a6e rrrg 2026-08-27 01:01:17 -06:00
msyds 21b9f0e69d allow multiple values in cps conversion 2026-08-27 00:57:09 -06:00
msyds 87baed9efc pass continuations as arguments, use normal stack
build / build (push) Successful in 28s
2026-08-24 23:41:52 -06:00
msyds 796b967686 continue takes var 2026-08-24 23:41:52 -06:00
msyds 99d9e460fc disable doctests conditionally 2026-08-24 05:06:01 -06:00
msyds c9eb8f6f85 fac 20 lol 2026-08-24 02:41:09 -06:00
msyds 030b36cd98 rename runtime → wasm-runtime
build / build (push) Successful in 29s
2026-08-24 02:12:04 -06:00
msyds bf2fc08ec4 disable hitlertest 2026-08-24 01:59:11 -06:00
msyds 950d123760 refactor stack vm to use a basic block ish structure
build / build (push) Failing after 1m46s
2026-08-24 01:12:34 -06:00
msyds 8a20c4f4aa envrc 2026-08-23 01:46:58 -06:00
msyds d4c1385225 remove doctest
build / build (push) Successful in 20s
2026-08-23 01:14:50 -06:00
msyds 2ceefdb3df remove sexp-grammar 2026-08-23 00:54:46 -06:00
msyds 7c0642655f tests pass!
build / build (push) Failing after 1m29s
2026-08-23 00:46:02 -06:00
msyds bf5595f185 qq 2026-08-22 23:06:30 -06:00
msyds bbcc924b34 fix all the reader tests lol 2026-08-22 18:13:04 -06:00
msyds 73063d2b2c parse meta vars
build / build (push) Failing after 1m36s
2026-08-22 18:05:35 -06:00
msyds 4beeb7c4cd datum grammar
build / build (push) Failing after 30s
2026-08-22 02:40:05 -06:00
119 changed files with 4701 additions and 2526 deletions
+3
View File
@@ -9,6 +9,9 @@
. (progn (defun apply-cabal-fmt-h ()
(haskell-mode-buffer-apply-command "cabal-fmt"))
(add-hook 'before-save-hook #'apply-cabal-fmt-h nil t)))))
(scheme-mode
. ((eval . (dolist (s '(kappa κ prim))
(put s 'scheme-indent-function 1)))))
(nil
. ((eval
. (progn (defun display-ansi ()
+2
View File
@@ -1 +1,3 @@
use flake
watch_file gyehoek.cabal cabal.project
PATH_add $(dirname $(cabal list-bin gyehoek))
+2 -1
View File
@@ -8,4 +8,5 @@ dist-newstyle
*.tix
.direnv
result
play/
play/
trace.html
+5
View File
@@ -1,5 +1,10 @@
packages: *.cabal
tests: True
-- required for doctest-parallel
write-ghc-environment-files: always
-- https://github.com/martijnbastiaan/doctest-parallel/pull/66
allow-older: Cabal:process
source-repository-package
type: git
+31
View File
@@ -132,3 +132,34 @@ multiple ~env-ref~ calls could probably be replaced with a primitive that loads
$code)
1))))
#+end_src
** example
#+begin_src scheme
(λ (n m ktail)
(letrec ((f (λ (x ktail-0) (+ x n ktail-0)))
(g (λ (y ktail-1) (+ y g ktail-1))))
(prim (cons f g) ktail)))
#+end_src
#+begin_src scheme
(λ (n m ktail)
(letrec ((f-code (λ (x ktail-0)
(prim (env-get 2)
(κ (n)
(+ x n ktail-0)))))
(g-code (λ (y ktail-1)
(prim (env-get 3)
(κ (m)
(+ y m ktail-1))))))
(letrec ((with-closure-code
(κ (f g)
(prim (get-env 0)
(κ (ktail)
(prim cons f g ktail))))))
(prim (make-shared-closure (with-closure-code)
ktail)
(κ (with-closure)
(prim (make-shared-closure (f-code g-code) n m)
with-closure))))))
#+end_src
+19
View File
@@ -0,0 +1,19 @@
#+title: on libraries
* libraries and the file system
R⁷RS leaves it unspecified how exactly libraries correspond to files:
#+begin_quote
Programs and libraries are typically stored in files, although in some implementations they can be entered interactively into a running Scheme system. Other paradigms are possible. Implementations which store libraries in files should document the mapping from the name of a library to its location in the file system.
#+end_quote
thus the implementation of ~define-library~ is open to much interpretation. we could possibly define libraries as first-class objects, or deal with them statically. the former case is appealing to me, as it could massively simplify interactive use.
* semantics of declaration order
mercifully, R⁷RS is similarly ambiguous when it comes to the significance of declaration order. the authors note explicitly example two equally acceptable approaches:
#+begin_quote
One possible implementation of libraries is as follows: _After all cond-expand library declarations are expanded, a new environment is constructed for the library consisting of all imported bindings._ The expressions from all begin, include and include-ci library declarations are expanded in that environment in the order in which they occur in the library. _Alternatively, cond-expand and import declarations may be processed in left to right order interspersed with the processing of other declarations_, with the environment growing as imported bindings are added to it by each import declaration.
#+end_quote
+100
View File
@@ -0,0 +1,100 @@
* rationale?
previously, the VM's stack was used for storing local variables across blocks; a Scheme procedure was split into several low-level routines (one for the procedure itself and one for each continuation), and the stack was used as a communication channel for these separate routines. in contrast, registers were local to each routine. this aligns with Wasm's model of functions pretty well, with Wasm /locals/ acting as the VM's /registers/, and a global mutable stack serving as fallback.
this worked quite well until it became time to implement ~call/cc~.
we are considering making the following alterations to the VM:
- explicitly segment the stack into frames.
- passing procedures and return addresses on the stack.
- new instructions:
+ ~(tail-call /n/)~
+ ~(call /n/)~
+ ~(load /r/ /n/)~
+ ~(return /n/)~
* scratchpad
#+begin_src scheme
(letrec ((fac (λ (n)
(if (zero? n)
1
(* n (fac (- n 1)))))))
(fac 3))
#+end_src
#+begin_src scheme
(λ (ktail0)
(letrec ((fac
(λ (n ktail1)
(zero?
n
(κ (x0)
(if x0
(continue ktail1 1)
(- n 1
(κ (x1)
(fac x1
(κ (x2)
(* n x2 ktail1)))))))))))
(fac 3)))
#+end_src
#+begin_example
n ktail1
| |
| | x0
| | |
| | ^
| |
| | x1
| | |
| | ^
| |
| | x2
| | |
^ ^ ^
#+end_example
#+begin_src scheme
(define $fac-c0
(pop! %x0 0) ; [ x0 $fac-c0 n $fac ktail1 ]
(if %x0 ; [ $fac-c0 n $fac ktail1 ]
;; every variable but `ktail1' is dead so we pop them all.
;; this probably means that `if' should take two continuations
;; rather than two blocks.
(then (push! 1) ; [ $fac-c0 n $fac ktail1 ]
(return 1)) ; [ 1 $fac-c0 n $fac ktail1 ]
(else (load %n 1) ; [ $fac-c0 n $fac ktail1 ]
(prim %x1 (- %n 1)) ; [ $fac-c0 n $fac ktail1 ]
(push! $fac-c1) ; [ $fac-c0 n $fac ktail1 ]
(push! $fac) ; [ $fac-c1 $fac-c0 n $fac ktail1 ]
(push! %x1) ; [ $fac $fac-c1 $fac-c0 n $fac ktail1 ]
(call 1) ; [ x1 $fac $fac-c1 $fac-c0 n $fac ktail1 ]
)))
(define $fac-c1
(pop! %x2) ; [ x2 $fac-c1 $fac-c0 n $fac ktail1 ]
(load %n 3) ; [ $fac-c1 $fac-c0 n $fac ktail1 ]
(prim %x3 (* %n %x2))
(push! %x3) ; [ $fac-c1 $fac-c0 n $fac ktail1 ]
(return 1) ; [ x3 $fac-c1 $fac-c0 n $fac ktail1 ]
)
(define $fac
(load %ktail1 2) ; [ n $fac ktail1 ]
(load %n 0) ; [ n $fac ktail1 ]
(push! $fac-c0) ; [ n $fac ktail1 ]
(push! $zero?) ; [ $fac-c0 n $fac ktail1 ]
(push! %n) ; [ $zero? $fac-c0 n $fac ktail1 ]
(call 1) ; [ n $zero? $fac-c0 n $fac ktail1 ]
)
(define $start
(push! $fac) ; [ $start ktail0 ]
(push! 3) ; [ $fac $start ktail0 ]
(tail-call 1) ; [ 3 $fac $start ktail0 ]
;; ↑ `tail-call' knows how to dispose of the caller's stack frame.
)
#+end_src
+8 -5
View File
@@ -20,12 +20,13 @@
overlays = [
haskellNix.overlay
(final: prev: {
gyehoek-runtime = final.callPackage ./runtime {
gyehoek-wasm-runtime = final.callPackage ./wasm-runtime {
crane-lib = inputs.crane.mkLib final;
};
gyehoek = final.haskell-nix.project' {
src = ./.;
compiler-nix-name = "ghc912";
configureArgs = "-f-doctest";
modules = [({ pkgs, lib, ...}: {
packages.gyehoek.components.tests.test.preCheck =
let
@@ -33,14 +34,16 @@
pkgs.git # tasty uses git diff
];
in ''
export GYEHOEK_RUNTIME=${lib.getExe final.gyehoek-runtime}
export GYEHOEK_WASM_RUNTIME=${
lib.getExe final.gyehoek-wasm-runtime
}
export PATH=${lib.makeBinPath bin}:$PATH
'';
})];
shell = {
withHoogle = true;
inputsFrom = [
final.gyehoek-runtime
final.gyehoek-wasm-runtime
];
tools = {
cabal = {};
@@ -52,7 +55,7 @@
nodejs
wasm-tools
wac-cli
guile
gauche
rust-analyzer
wasmtime
# bashInteractive is necessary to work around an
@@ -91,7 +94,7 @@
hf.packages.${system} // lib.fix (packages: {
gyehoek = hf.packages.${system}."gyehoek:exe:gyehoek";
default = packages.gyehoek;
inherit (pkgs) gyehoek-runtime;
inherit (pkgs) gyehoek-wasm-runtime;
}));
devShells = each-system
+1
View File
@@ -0,0 +1 @@
(begin 123 456) ; => 456
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #t
@@ -0,0 +1,12 @@
(letrec ((iter (λ (n f)
(if (zero? n)
#f
(begin (f n)
(iter (- n 1) f))))))
(call/cc
(λ (k)
(iter 10 (λ (n)
;; i don't feel like implementing (= n 5) right now lmfao
(if (zero? (- n 5))
(k #t)
#f))))))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #t
@@ -0,0 +1,4 @@
(call/cc
(λ (k)
(begin (k #t)
#f)))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #t
@@ -0,0 +1,5 @@
;; confer ../callcc-early-exit-4
(letrec ((app (λ (f x)
(begin (f x)
#f))))
(call/cc (λ (k) (app k #t))))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #t
@@ -0,0 +1,5 @@
;; confer ../callcc-early-exit-3
(letrec ((app (λ (f x)
(begin (f x)
#f))))
(call/cc (λ (k) (app (λ (x) (k x)) #t))))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > #t
@@ -0,0 +1,4 @@
(call/cc
(λ (k)
(begin ((λ () (k #t)))
#f)))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 12
@@ -0,0 +1,4 @@
(* 2 (call/cc
(λ (k)
(begin (k 6)
3))))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 456
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 155
+10
View File
@@ -0,0 +1,10 @@
(letrec ((factorial (λ (n)
(if (zero? n)
1
(* n (factorial (- n 1)))))))
(letrec ((sum-of-factorials
(λ (n)
(if (zero? n)
0
(+ (factorial n) (sum-of-factorials (- n 1)))))))
(+ 2 (sum-of-factorials 5))))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > (6 . 7)
+1
View File
@@ -0,0 +1 @@
(cons 6 7)
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > (456 . 123)
+2
View File
@@ -0,0 +1,2 @@
(let ((p (cons 123 456)))
(cons (cdr p) (car p)))
+1 -1
View File
@@ -1,2 +1,2 @@
ret > ExitSuccess
out > 720
out > 2432902008176640000
+1 -1
View File
@@ -2,4 +2,4 @@
(if (zero? n)
1
(* n (fac (- n 1)))))))
(fac 6))
(fac 20))
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > 123
+1
View File
@@ -0,0 +1 @@
123
+2
View File
@@ -0,0 +1,2 @@
ret > ExitSuccess
out > (0 . (1 . (4 . (9 . (16 . ())))))
+7
View File
@@ -0,0 +1,7 @@
(letrec ((my-map (λ (f l)
(if (pair? l)
(cons (f (car l))
(my-map f (cdr l)))
(list)))))
(my-map (λ (x) (* x x))
(list 0 1 2 3 4)))
+4 -4
View File
@@ -1,4 +1,4 @@
(begin
책을
더
먹으세요~!)
(begin
책을
더
먹으세요~!)
+4 -4
View File
@@ -1,4 +1,4 @@
(begin
책을
더
먹으세요~!)
(begin
책을
더
먹으세요~!)
+5 -5
View File
@@ -1,5 +1,5 @@
(lambda
(어간
어미)
(display
꾸깃))
(lambda
(어간
어미)
(display
꾸깃))
+2 -2
View File
@@ -1,2 +1,2 @@
(lambda (어간 어미)
(display 꾸깃))
(lambda (어간 어미)
(display 꾸깃))
+1 -1
View File
@@ -1 +1 @@
()
()
+1 -1
View File
@@ -1 +1 @@
((((()))))
((((()))))
+4 -4
View File
@@ -1,4 +1,4 @@
(가
나
다
라)
(가
나
다
라)
+1 -1
View File
@@ -1 +1 @@
(가 나 다 라)
(가 나 다 라)
+36 -4
View File
@@ -1,5 +1,37 @@
[ SynNone :< SimpleF ( SimpleBoolean True )
, SynNone :< SimpleF ( SimpleBoolean True )
, SynNone :< SimpleF ( SimpleBoolean False )
, SynNone :< SimpleF ( SimpleBoolean False )
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/bool/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< SimpleF ( SimpleBoolean True )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/bool/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 4
}
)
} :< SimpleF ( SimpleBoolean True )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/bool/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 10
}
)
} :< SimpleF ( SimpleBoolean False )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/bool/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 13
}
)
} :< SimpleF ( SimpleBoolean False )
]
+4
View File
@@ -0,0 +1,4 @@
#;(a datum comment can
span multiple lines)
(but it ends here)
+27 -3
View File
@@ -1,8 +1,32 @@
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/decimal/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleNumber 45.0 )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/decimal/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 4
}
)
} :< SimpleF
( SimpleNumber 5667.0 )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/decimal/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 10
}
)
} :< SimpleF
( SimpleNumber
( -123.0 )
)
+45 -5
View File
@@ -1,16 +1,56 @@
[ SynNone :< CompoundF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-dot-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< CompoundF
( DotListF
(
( SynNone :< SimpleF
( MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-dot-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 2
}
)
} :< SimpleF
( SimpleSymbol "가" )
) :|
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-dot-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 5
}
)
} :< SimpleF
( SimpleSymbol "나" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-dot-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 8
}
)
} :< SimpleF
( SimpleSymbol "다" )
]
)
( SynNone :< SimpleF
( MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-dot-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 13
}
)
} :< SimpleF
( SimpleSymbol "라" )
)
)
+73 -9
View File
@@ -1,18 +1,82 @@
[ SynNone :< CompoundF
( ListF Ordinary
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< CompoundF
( ListF StyleData
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 2
}
)
} :< SimpleF
( SimpleSymbol "가" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 5
}
)
} :< SimpleF
( SimpleSymbol "나" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 8
}
)
} :< SimpleF
( SimpleSymbol "다" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 11
}
)
} :< SimpleF
( SimpleSymbol "라" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 14
}
)
} :< SimpleF
( SimpleNumber 1.0 )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 16
}
)
} :< SimpleF
( SimpleNumber 2.0 )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list-flat/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 18
}
)
} :< SimpleF
( SimpleNumber 3.0 )
]
)
+110 -14
View File
@@ -1,37 +1,133 @@
[ SynNone :< CompoundF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< CompoundF
( DotListF
(
( SynNone :< SimpleF
( MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 2
}
)
} :< SimpleF
( SimpleSymbol "a" )
) :|
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 4
}
)
} :< SimpleF
( SimpleSymbol "b" )
, SynNone :< CompoundF
( ListF Ordinary
[ SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 6
}
)
} :< CompoundF
( ListF StyleData
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 7
}
)
} :< SimpleF
( SimpleSymbol "c" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 9
}
)
} :< SimpleF
( SimpleSymbol "d" )
]
)
]
)
( SynNone :< CompoundF
( ListF Ordinary
[ SynNone :< SimpleF
( MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 14
}
)
} :< CompoundF
( ListF StyleData
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 15
}
)
} :< SimpleF
( SimpleSymbol "가" )
, SynNone :< CompoundF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 18
}
)
} :< CompoundF
( DotListF
(
( SynNone :< SimpleF
( MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 19
}
)
} :< SimpleF
( SimpleSymbol "나" )
) :| []
)
( SynNone :< SimpleF
( MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 24
}
)
} :< SimpleF
( SimpleSymbol "다" )
)
)
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/list/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 28
}
)
} :< SimpleF
( SimpleSymbol "라" )
]
)
+10
View File
@@ -0,0 +1,10 @@
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/meta-expression/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< MetaF "aHaskellVariable + abc * 2"
]
+1
View File
@@ -0,0 +1 @@
#{aHaskellVariable + abc * 2}
@@ -0,0 +1,2 @@
##{case 123 of { 123 -> blah
; xyz -> flah }}
+10
View File
@@ -0,0 +1,10 @@
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/meta-splice-expression/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< MetaSpliceF "takeWhile (\x -> even x) aHaskellList"
]
@@ -0,0 +1 @@
##{takeWhile (\x -> even x) aHaskellList}
+10
View File
@@ -0,0 +1,10 @@
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/meta-splice-variable/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< MetaSpliceF "aHaskellList"
]
@@ -0,0 +1 @@
##{aHaskellList}
+10
View File
@@ -0,0 +1,10 @@
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/meta-variable/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< MetaF "aHaskellVariable"
]
+1
View File
@@ -0,0 +1 @@
#{aHaskellVariable}
+57 -3
View File
@@ -1,7 +1,61 @@
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-dot/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "..." )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-dot/source.scm"
, sourceLine = Pos 2
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol ".." )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-dot/source.scm"
, sourceLine = Pos 3
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol ".abc" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-dot/source.scm"
, sourceLine = Pos 4
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "....abcc" )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-dot/source.scm"
, sourceLine = Pos 5
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol ".++-" )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-dot/source.scm"
, sourceLine = Pos 6
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol ".-" )
]
@@ -1 +1,6 @@
.. .abc ....abcc
...
..
.abc
....abcc
.++-
.-
+68 -2
View File
@@ -1,5 +1,71 @@
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-sign/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "+" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-sign/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 3
}
)
} :< SimpleF
( SimpleSymbol "-" )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-sign/source.scm"
, sourceLine = Pos 3
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "+." )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-sign/source.scm"
, sourceLine = Pos 3
, sourceColumn = Pos 4
}
)
} :< SimpleF
( SimpleSymbol "+.." )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-sign/source.scm"
, sourceLine = Pos 3
, sourceColumn = Pos 8
}
)
} :< SimpleF
( SimpleSymbol "-." )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-sign/source.scm"
, sourceLine = Pos 3
, sourceColumn = Pos 11
}
)
} :< SimpleF
( SimpleSymbol "-...abc" )
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/peculiar-identifier-sign/source.scm"
, sourceLine = Pos 3
, sourceColumn = Pos 19
}
)
} :< SimpleF
( SimpleSymbol "-abc.." )
]
@@ -1 +1,3 @@
+ -
+. +.. -. -...abc -abc..
+9 -1
View File
@@ -1,3 +1,11 @@
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/string/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleString "가나다라" )
]
+46 -6
View File
@@ -1,12 +1,52 @@
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier-token/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "abc" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier-token/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 4
}
)
} :< SimpleF
( SimpleString "xyz" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier-token/source.scm"
, sourceLine = Pos 2
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "수학" )
, SynNone :< CompoundF
( ListF Ordinary
[ SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier-token/source.scm"
, sourceLine = Pos 2
, sourceColumn = Pos 5
}
)
} :< CompoundF
( ListF StyleData
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier-token/source.scm"
, sourceLine = Pos 2
, sourceColumn = Pos 6
}
)
} :< SimpleF
( SimpleSymbol "數學" )
]
)
+81 -9
View File
@@ -1,19 +1,91 @@
[ SynNone :< SimpleF
[ MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "abc" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 5
}
)
} :< SimpleF
( SimpleSymbol "bala-hwa$" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 15
}
)
} :< SimpleF
( SimpleSymbol "x!!!" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 20
}
)
} :< SimpleF
( SimpleSymbol "z" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 22
}
)
} :< SimpleF
( SimpleSymbol "z123" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 27
}
)
} :< SimpleF
( SimpleSymbol "나는너무졸리다" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 1
, sourceColumn = Pos 42
}
)
} :< SimpleF
( SimpleSymbol "學" )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 3
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "車室." )
, SynNone :< SimpleF
, MkAnn
{ position = Just
( SourcePos
{ sourceName = "golden/read/typical-identifier/source.scm"
, sourceLine = Pos 5
, sourceColumn = Pos 1
}
)
} :< SimpleF
( SimpleSymbol "三個女人一臺戲。" )
]
+42 -10
View File
@@ -13,6 +13,11 @@ build-type: Simple
-- extra-doc-files: CHANGELOG.md
-- extra-source-files:
flag doctest
description: enable the doctest suite
default: True
manual: True
common ghcstuffs-dev
ghc-options:
-Wno-unused-matches -Wno-missing-signatures -Wno-typed-holes
@@ -21,6 +26,7 @@ common ghcstuffs
ghc-options:
-Wall -fdefer-type-errors -fno-show-valid-hole-fits
-fdefer-out-of-scope-variables -threaded
-Wno-name-shadowing -Wno-partial-type-signatures
default-extensions:
BlockArguments
@@ -49,28 +55,33 @@ executable gyehoek
library
import: ghcstuffs, ghcstuffs-dev
ghc-options: -fplugin=Effectful.Plugin
-- build-tool-depends: retrie:retrie
-- cabal-fmt: expand src
exposed-modules:
Gyehoek.CPS.Close
Gyehoek.CPS.Convert
Gyehoek.CPS.Eval
Gyehoek.CPS.Lower
Gyehoek.CPS.Stackify
Gyehoek.CPS.Hoist
Gyehoek.CPS.Syntax
Gyehoek.Driver
Gyehoek.GenSym
Gyehoek.Jalmot
Gyehoek.Language
Gyehoek.Language.Common
Gyehoek.Lift1
Gyehoek.Options
Gyehoek.Prelude
Gyehoek.Scheme.Expand
Gyehoek.Scheme.Expand.Old
Gyehoek.Scheme.Syntax
Gyehoek.Sexp
Gyehoek.Sexp.Grammar
Gyehoek.Sexp.Grammar.Base
Gyehoek.Sexp.Print
Gyehoek.Sexp.QQ
Gyehoek.Sexp.Read
Gyehoek.Sexp.Syntax
Gyehoek.Stack.Syntax
Gyehoek.Stack.VM
Gyehoek.Wasm
build-depends:
@@ -91,6 +102,7 @@ library
, hashable
, invertible-grammar
, lens
, lucid
, megaparsec
, mtl
, optparse-applicative
@@ -98,17 +110,21 @@ library
, pretty-simple
, prettyprinter
, prettyprinter-ansi-terminal
, prettyprinter-lucid
, process
, recursion-schemes
, scientific
, sexp-grammar
, semialign
, string-interpolate
, tardis
, template-haskell
, text
, text-short
, these
, typed-process
, unordered-containers
, vector
, witherable
hs-source-dirs: src
default-language: GHC2024
@@ -123,13 +139,12 @@ test-suite test
-- cabal-fmt: expand test -Main
other-modules:
Gyehoek.Test.CPS.Eval
Gyehoek.Test.CPS.Stackify
Gyehoek.Test.CPS.Syntax
Gyehoek.Test.Golden
Gyehoek.Test.Scheme.Syntax
Gyehoek.Test.Sexp
Gyehoek.Test.Sexp.Print
Gyehoek.Test.Stack.VM
Gyehoek.Test.Sexp.QQ
Gyehoek.Test.Sexp.Read
Gyehoek.TestUtil
Root
build-depends:
@@ -143,7 +158,6 @@ test-suite test
, lens
, pretty-simple
, process-extras
, sexp-grammar
, tasty
, tasty-expected-failure
, tasty-hunit
@@ -151,3 +165,21 @@ test-suite test
, text
default-language: GHC2024
-- https://github.com/martijnbastiaan/doctest-parallel/pull/66
test-suite doctest
import: ghcstuffs, ghcstuffs-dev
type: exitcode-stdio-1.0
hs-source-dirs: test
build-depends:
, base
, gyehoek
default-extensions: CPP
main-is: doctest.hs
if flag(doctest)
build-depends: doctest-parallel >=0.1
else
cpp-options: -DGYEHOEK_NO_DOCTEST
+11
View File
@@ -0,0 +1,11 @@
(define-syntax if-not
(syntax-rules ()
((_ c t f) (if (not c) t f))
((_ c t) (if (not c) t))))
(write (macroexpand-1 '(if-not #t 123 456)))
(define (main)
(let loop ((datum (read)))
(unless (eof-object? datum)
())))
+38 -24
View File
@@ -4,38 +4,52 @@ module Gyehoek.CPS.Close
) where
import Gyehoek.CPS.Syntax
import Data.List (nub)
import Gyehoek.GenSym
import Gyehoek.Prelude
import Debug.Pretty.Simple
import Gyehoek.Sexp qualified as S
import Data.HashSet.Lens
import Data.Traversable
close :: GenSym :> es => Exp -> Eff es Exp
close = transformM \case
ExpLetRec [(f, AbsLambda lam@(MkLambda bs kb m))] e -> do
f_code <- gensym' @Name $ f ^. _Wrapped'. to (<> "-code")
-- it would probably be most sane to generate a symbol for `env`,
-- but we're reusing the lambda binding so we don't have to
-- explicitly substitute recursive calls.
let frees = freeWithBound' [f] lam
let m' = ifoldr
(\n x q -> [cps|(prim (env-ref #{f} #{n})
(κ (#{x}) #{q}))|])
m frees
pure [cps|
(letrec ((#{f_code} (λ (#{f} ##{bs} #{kb})
#{m'})))
(prim (make-closure ($ #{f_code}) ##{frees})
(κ (#{f}) #{e})))
|]
genCodeName :: GenSym :> es => Name -> Eff es Name
genCodeName f = gensym' @Name $ f ^. _Wrapped' . to (<> "-code")
ExpApply f xs ktail -> do
code <- gensym' @Name "code"
bindEnv :: List Name -> Exp -> Exp
bindEnv frees m = [cps|
(builtin (get-env) (κ #{frees} #{m}))
|]
close1 :: forall es. GenSym :> es => Exp -> Eff es Exp
close1 = \case
lr@(ExpLetRec bs e) -> do
let boundNames = bs ^.. each . _1
let boundNames' = setOf each boundNames
let frees = bs
& foldMapOf
(each . _2)
(freeWithBound' boundNames')
& nub
env_cont_l <- gensym' @Name "env-cont"
e_l <- gensym' @Name "letrec-body-cont"
bs' <- for bs \(f,ab) -> do
f_code_l <- genCodeName f
pure ( f_code_l
, ab & absBody %~ bindEnv (boundNames ++ frees)
)
let codes = bs' ^.. each . _1 . to MkLabel
pure [cps|
(prim (env-code #{f})
(κ (#{code})
(#{code} #{f} ##{xs} #{ktail})))
(letrec #{bs'}
(builtin (make-shared-closure #{codes} #{frees})
(κ #{boundNames}
#{e})))
|]
e -> pure e
close :: forall es. GenSym :> es => Exp -> Eff es Exp
close = transformM close1
closeProgram :: GenSym :> es => Program -> Eff es Program
closeProgram = traverseOf #body close
closeProgram = traverseOf (#body . #body) close
+46 -35
View File
@@ -12,6 +12,7 @@ import Data.List.NonEmpty (NonEmpty((:|)))
import Control.Monad.Cont qualified as Cont
import qualified Data.List.NonEmpty as NE
import Gyehoek.Prelude
import Debug.Pretty.Simple
-- 뻘짓이어라
@@ -23,66 +24,73 @@ telescope f = Cont.runCont . traverse (Cont.cont . f)
one :: a -> List a
one a = [a]
oneOrUndefined :: List Val -> Val
oneOrUndefined = \case
[x] -> x
_ -> ValImm ImmUndefined
convert1 :: (GenSym :> es) => Scm.Exp -> (Val -> Eff es Exp) -> Eff es Exp
convert1 e k = convert e (k . oneOrUndefined)
-- | Transform an expression with a meta-continuation.
convert
:: forall es. (GenSym :> es)
=> Scm.Exp -> (Val -> Eff es Exp) -> Eff es Exp
=> Scm.Exp -> (List Val -> Eff es Exp) -> Eff es Exp
convert (Scm.ExpVar x) k = k $ ValVar x
convert (Scm.ExpLit l) k = k . ValImm $ case l of
convert (Scm.ExpVar x) k = k [ValVar x]
convert (Scm.ExpLit l) k = k . one . ValImm $ case l of
LitInt n -> ImmInt n
LitBool b -> ImmBool b
_ -> _
-- special case: call/cc is desugared during cps-conversion...
convert (Scm.ExpPrim (PrimCallCC withcc)) k = do
convert withcc \withcc' -> do
cc <- gensym' @Name "cc"
r <- gensym' "r"
m <- k $ ValVar r
ccish <- gensym' @Name "cc-ish"
x <- gensym' @Name "x"
convert (Scm.ExpBuiltin p) k =
telescope (convert1 @es) p \p' -> do
r_l <- gensym' "r"
m <- k [ValVar r_l]
pure [cps|
(letrec ((#{cc} (κ (#{r}) #{m})))
(letrec ((#{ccish} (λ (#{x} _) (continue #{cc} #{x}))))
(#{withcc'} #{ccish} #{cc})))
(builtin #{p'} (κ (#{r_l}) #{m}))
|]
-- ...while all other prims are left as-is for later stages to
-- handle..
convert (Scm.ExpPrim p) k =
telescope (convert @es) p \p' -> do
r <- gensym' "r"
ExpPrim p' . MkKappa [r] <$> k (ValVar r)
convert (Scm.ExpLambda xs e) k = do
f <- gensym' "lambda-body"
lam <- convertLambda xs e
ke <- k $ ValVar f
ke <- k [ValVar f]
pure [cps|
(letrec ((#{f} #{lam}))
#{ke})
|]
convert (Scm.ExpApply f xs) k =
telescope (convert @es) (f:|xs) \(f':|xs') -> do
r <- gensym' "r"
telescope (convert1 @es) (f:|xs) \(f':|xs') -> do
r <- gensym' @Name "r"
x <- gensym' "x"
m <- k (ValVar x)
pure $ ExpLetRec [(r, AbsKappa' [x] m)] $ ExpApply f' xs' r
m <- k [ValVar x]
pure $ ExpLetRec [(r, AbsKappa' [x] m)] $
ExpApply f' xs' (KexpVar r)
convert (Scm.ExpBegin xs) k = _
convert (Scm.ExpBegin xs) k = telescope (convert @es) xs (k . NE.last)
convert (Scm.ExpIf c t f) k =
convert c \c' ->
ExpIf c' <$> convert t k <*> convert f k
convert (Scm.ExpIf c t (Just f)) k =
convert1 c \c' -> do
t_l <- gensym' @Name "truthy-cont"
f_l <- gensym' @Name "falsey-cont"
t' <- convert t k
f' <- convert f k
pure [cps|
(letrec ((#{t_l} (κ () #{t'}))
(#{f_l} (κ () #{f'})))
(if #{c'} #{t_l} #{f_l}))
|]
-- let-bindings are desugared into continuation calls whose parameters
-- are the left-hand sides and whose arguments are the right-hand
-- sides.
convert (Scm.ExpLet bs e) k =
let rhss = bs ^.. each . _2
in telescope (convert @es) rhss \rhss' -> do
in telescope (convert1 @es) rhss \rhss' -> do
e' <- convert e k
kbody <- gensym' @Name "let-body"
let bs' = bs ^.. each . _1
@@ -105,12 +113,15 @@ convertLambda
=> List Name -> Scm.Exp -> Eff es Lambda
convertLambda bs m = do
ktail <- gensym' "lambda-tail"
m' <- convert m $ pure . ExpContinue ktail . (:[])
m' <- convert1 m $ pure . ExpContinue (ValVar ktail) . (:[])
pure [cps|(λ (##{bs} #{ktail}) #{m'})|]
convertProgram :: forall es. (GenSym :> es) => Scm.Program -> Eff es Program
convertProgram p =
MkProgram <$> telescope (convert @es) (p ^.. each . _Left) (pure . Halt)
convertProgram p = do
ktail <- gensym' "start-ktail"
m <- telescope (convert1 @es) (p ^.. each . _Left)
(pure . ExpContinue (ValVar ktail))
pure . MkProgram $ MkLambda [] ktail m
convertExp :: forall es. (GenSym :> es) => Scm.Exp -> Eff es Exp
convertExp e = convert e (pure . Halt1)
convertExp e = convert e (pure . Halt)
+285 -61
View File
@@ -1,79 +1,303 @@
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedLists #-}
module Gyehoek.CPS.Eval
( evalProgram
, module Gyehoek.CPS.Syntax
, evalExp
, eGrammar
) where
import Gyehoek.CPS.Syntax
import Control.Lens
import Data.Maybe (fromMaybe)
import Gyehoek.CPS.Syntax hiding (Hob(..), Obj(..), cont)
import Gyehoek.Sexp qualified as S
import Control.Lens hiding (assign)
import Data.Maybe (fromMaybe, isJust)
import Text.Show.Functions ()
import qualified Data.HashMap.Strict as H
import Gyehoek.Prelude
import Gyehoek.Prelude hiding (assign)
import Debug.Pretty.Simple
import Gyehoek.Jalmot
import Control.Monad.Cont
import Gyehoek.Sexp qualified as S
import GHC.Generics (Generically(..))
import Gyehoek.Sexp ((:-)(..))
import Data.List (nub, mapAccumR, compareLength)
import Data.HashSet.Lens (setOf)
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IM
import Data.Monoid
import Control.Monad.State
import Data.Traversable (for)
import Data.Foldable (traverse_, foldrM)
data Env = MkEnv
{ vars :: HashMap Name Obj
, labels :: HashMap Name (Env, Abs)
newtype Loc = MkLoc { getLoc :: Int }
deriving stock (Generic, Data)
deriving newtype (Show, Eq, Ord, Enum)
data Store = MkStore
{ nextLoc :: Loc
, heap :: IntMap E
}
deriving (Show, Generic)
deriving stock (Show, Generic)
eval :: Env -> Exp -> List Obj
type instance Index Store = Loc
type instance IxValue Store = E
eval g (Halt xs) = evalVal g <$> xs
instance Ixed Store where ix (MkLoc j) = #heap . ix j
instance At Store where at (MkLoc j) = #heap . at j
eval g (ExpContinue k xs) =
case g ^. #labels . at k of
Just (h, AbsKappa' bs m) -> eval h' m
where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
_ -> error [i|not a kappa: #{k}|]
emptyStore :: Store
emptyStore = MkStore
{ nextLoc = MkLoc 0
, heap = mempty
}
eval g (ExpApply ((^?! #ValVar) -> f) xs ktail) =
case g ^?! #labels . at f of
Just (h,AbsLambda' bs kb m) -> eval h' m
where h' = h & #vars <>~ envOfBinds bs (evalVal g <$> xs)
& #labels . at kb .~ (g ^. #labels . at ktail)
Nothing -> error [i|undefined label: #{f}|]
eval g (ExpLetRec [(b, ab)] e) = eval g' e
where g' = g & #labels . at b ?~ (g,ab)
eval g (ExpPrim p (MkKappa bs e)) = case evalVal g <$> p of
PrimAdd x y -> arithBinop (+) x y
PrimMul x y -> arithBinop (*) x y
PrimSub x y -> arithBinop (-) x y
PrimDiv x y -> arithBinop div x y
_ -> error [i|unhandled prim: #{p}|]
where
ret rs = eval
(g & #vars <>~ envOfBinds bs rs)
e
arithBinop f (ObjImm (ImmInt x)) (ObjImm (ImmInt y)) =
ret [ObjImm . ImmInt $ f x y]
arithBinop _ x y = error [i|bad arith: #{x}, #{y}|]
eval _ e = error [i|unimplemented case: #{e}|]
envOfBinds bs xs = foldMap (uncurry H.singleton) (zip bs xs)
evalVal :: Env -> Val -> Obj
evalVal g = \case
ValVar x -> fromMaybe (error [i|unbound: #{x}|]) $ g ^?! #vars . at x
ValImm x -> ObjImm x
newtype Env = MkEnv { getEnv :: HashMap Name Loc }
deriving stock (Show, Generic, Data)
deriving newtype (Semigroup, Monoid)
emptyEnv :: Env
emptyEnv = MkEnv
{ vars = mempty
-- a kinda silly hack to make sure `halt` is handled correctly when
-- it appears as the tail continuation of an application. the
-- special case of `eval` responsible for `halt` only covers terms
-- of the form `(continue $halt xs …)`; other terms such as
-- `($some-fn xs $halt)` just see an undefined label `$halt`.
, labels = H.singleton "halt"
( emptyEnv
, AbsKappa' ["h0"] $ Halt [ValVar "h0"]
)
}
emptyEnv = mempty
evalProgram :: Program -> List Obj
evalProgram (MkProgram e) = eval emptyEnv e
type instance Index Env = Name
type instance IxValue Env = Loc
instance Ixed Env where ix j = #getEnv . ix j
instance At Env where at j = #getEnv . at j
update :: Loc -> E -> Store -> Store
update (MkLoc loc) v = #heap %~ IM.alter f loc
where
f (Just _) = Just v
f Nothing = error "segfault lol"
updates :: Foldable f => f (Loc, E) -> Store -> Store
updates = alaf Endo foldMap (uncurry update)
fetch :: Loc -> M r E
fetch (MkLoc loc) = gets (^?! #heap . ix loc)
new :: M r Loc
new = state \st -> (st.nextLoc, st & #nextLoc %~ succ)
new' :: E -> M r Loc
new' e = state \st ->
( st.nextLoc
, st & #nextLoc %~ succ & at st.nextLoc ?~ e
)
defines :: Traversable t => t (Name, E) -> M Answer Env
defines = alaf Ap foldMap \(name,e) -> do
l <- new' e
pure $ bind name l
var :: HasCallStack => Env -> Name -> M Answer Loc
var g x = case g ^. at x of
Just l -> pure l
Nothing -> wrong [i|unbound variable #{x}|]
type CmdCont = Store -> Answer
type ExpCont = List E -> CmdCont
type M r = ContT r (State Store)
data Answer
= AnswerValues (List E)
| AnswerError AJalmot
deriving (Show, Generic)
data Mutability
= Mut
| NoMut
deriving (Show, Generic, Data, Eq)
wrong :: Text -> M Answer a
wrong s = ContT \_ -> pure . AnswerError . EvalError $ s
orWrong
:: Getting (First a) s a
-> Text -> s -> M Answer a
orWrong p msg s = case getFirst . getConst $ p (Const . First . Just) s of
Nothing -> wrong msg
Just x -> pure x
bind :: Name -> Loc -> Env
bind k = MkEnv . H.singleton k
extends :: Foldable f => f (Name, Loc) -> Env -> Env
extends xs g = g <> foldMap (uncurry bind) xs
assign :: Loc -> E -> M Answer ()
assign l e = do
use (at l) >>= \case
Just _ -> at l ?= e
Nothing -> wrong [i|#{e}에서 #{l}이라는 주소는 없다|]
-- | The denotation of an expressed value.
data E
= ESymbol Text
| ECharacter Char
| EInt Int
| EBool Bool
| EUndefined
| EUnspecified
| ENull
| EPair Mutability Loc Loc
| EVec Mutability (List Loc)
| EString Mutability (List Loc)
| EProcedure Procedure
deriving stock (Show, Generic)
type Procedure = List E -> DynPoints -> M Answer (List E)
eGrammar :: Store -> S.DatumGrammar E
eGrammar st = S.partialOsi (const . Left $ mempty) go
where
gofetch x = go $ st ^?! ix x
go = \case
ESymbol s -> S.Symbol s
ECharacter c -> S.Character c
EInt n -> S.Number (fromIntegral n)
EBool b -> S.Boolean b
EUndefined -> S.Unreadable "#<undefined>"
EUnspecified -> S.Unreadable "#<unspecified>"
EProcedure _ -> S.Unreadable "#<procedure>"
ENull -> S.List []
EPair _mut car cdr -> S.DotList [gofetch car] (gofetch cdr)
EVec _mut xs -> S.Vector . fmap gofetch $ xs
EString _mut xs -> S.String _
data DynPoints = MkDynPoints
deriving (Generic, Data)
truthy :: E -> Bool
truthy (EBool False) = False
truthy _ = True
evalVal :: Env -> Val -> M Answer E
evalVal g (ValVar x) = var g x >>= fetch
evalVal g (ValImm imm) = case imm of
ImmLabel (MkLabel l) -> var g l >>= fetch
ImmInt n -> pure $ EInt n
ImmBool b -> pure $ EBool b
ImmUndefined -> pure EUndefined
evalKexp :: Env -> Kexp -> M Answer E
evalKexp g (KexpVar x) = var g x >>= fetch
evalKexp g (KexpKappa kap) = evalAbs g (AbsKappa kap)
evalAbs :: Env -> Abs -> M Answer E
evalAbs g (MkAbs formals ktail e) = pure . EProcedure $ \xs dps ->
let
formals' = formals ++ foldMap (:[]) ktail
lformals = length formals'
lxs = length xs
in if lformals /= lxs
then wrong [i|함수는 #{lformals}개의 인자를 필요로 하는데 #{lxs}개 받았다.|]
else do
ls <- xs & traverse new'
let g' = g & extends (zip formals' ls)
eval g' dps e
eval :: Env -> DynPoints -> Exp -> M Answer (List E)
eval g dps (ExpJump f xs ktail) = do
f' <- evalVal g f
xs' <- traverse (evalVal g) xs
ktail' <- traverse (evalKexp g) (ktail ^.. _Just)
case f' of
EProcedure p -> p (xs' ++ ktail') dps
_ -> wrong "bad procedure"
eval g dps (ExpLetRec bs e) = do
ls <- for bs . const $ new' EUndefined
let g' = g & extends (zip (bs ^.. each . _1) ls)
bs' <- forOf (each . _2) bs (evalAbs g')
traverse_ (uncurry assign) $ zip ls (bs' ^.. each . _2)
eval g' dps e
eval g dps (ExpBuiltin (BuiltinCallCC withcc) k) = do
withcc' <- evalVal g withcc >>= orWrong #_EProcedure
[i|call/cc: 함수가 아닌 것을 받았다|]
k' <- evalKexp g k
kproc <- orWrong #_EProcedure [i|call/cc: 몰라...|] k'
let cc = EProcedure \xs dps -> case unsnoc xs of
Just (xs',_) -> kproc xs' dps
Nothing -> wrong [i|call/cc: 잘못하는데!|]
withcc' [cc,k'] dps
eval g dps (ExpBuiltin p k) = do
p' <- evalBuiltin g dps =<< traverse (evalVal g) p
evalKexp g k >>= \case
EProcedure fp -> fp p' dps
_ -> wrong [i|prim(#{p})의 계속을 나쁘다|]
eval g dps (ExpIf c t f) = do
c' <- evalVal g c
let b = if truthy c' then t else f
var g b >>= fetch >>= \case
EProcedure fp -> fp [] dps
_ -> wrong [i|if의 계속을 나쁘다|]
eval g dps e = error [i|unimplemented #{e}|]
evalBuiltin :: Env -> DynPoints -> Builtin E -> M Answer (List E)
evalBuiltin g dps = \case
BuiltinAdd x y -> arith2 (+) x y
BuiltinMul x y -> arith2 (*) x y
BuiltinSub x y -> arith2 (-) x y
BuiltinDiv x y -> arith2 div x y
BuiltinZeroP x -> pure1 . EBool . isJust $ x ^? #EInt . only 0
BuiltinCons x y -> pcons x y >>= pure1
BuiltinCar p -> cr p _2
BuiltinCdr p -> cr p _3
BuiltinPairP p -> pure1 . EBool . maybe False (const True) $
p ^? #_EPair
BuiltinValues xs -> pure xs
BuiltinList xs -> foldrM pcons ENull xs >>= pure1
p -> wrong [i|prim(#{p})은 벌써 나지 않다|]
where
pure1 x = pure [x]
pcons x y = do
(x',y') <- traverseOf both new' (x,y)
pure $ EPair Mut x' y'
arith2 f (EInt x) (EInt y) = pure [EInt $ f x y]
arith2 f x y = wrong [i|나쁜 인자: #{x}, #{y}|]
cr p l = orWrong (#_EPair . l) [i|car/cdr는 pair을 받지 않다|] p
>>= fmap (:[]) . fetch
evalExp :: Jalmot :> es => Exp -> Eff es _
evalExp e = _
evalProgram :: Jalmot :> es => Program -> Eff es (List S.Datum)
evalProgram (MkProgram lam) = case run (pure . AnswerValues) of
(AnswerError jm, _) -> throwError jm
(AnswerValues vs, st) -> traverse (S.toDatum $ eGrammar st) vs
where
run f = (`runState` emptyStore) . (`runContT` f) $ do
g <- setup
eval g MkDynPoints (ExpLetRec
[("_start",AbsLambda lam)]
(ExpApply (ValVar "_start") [] (KexpVar "halt")))
setup :: M Answer Env
setup = defines @List
[ ("halt", EProcedure prim_halt)
]
prim_halt :: Procedure
prim_halt xs _dps = ContT \_ -> pure $ AnswerValues xs
+24
View File
@@ -0,0 +1,24 @@
module Gyehoek.CPS.Hoist
( hoistProgram
) where
import Gyehoek.CPS.Syntax
import Gyehoek.Prelude
import qualified Data.HashMap.Strict as H
import Effectful.Writer.Static.Local
import Data.Foldable
type Hoist = Writer (HashMap Label Abs)
hoist :: Hoist :> es => Exp -> Eff es Exp
hoist = transformM \case
ExpLetRec bs m -> do
traverse_ (\(k,v) -> tell $ H.singleton (MkLabel k) v) bs
pure m
e -> pure e
hoistProgram :: Program -> Eff es HoistedProgram
hoistProgram p = do
(body,bindings) <- runWriter $ traverseOf #body hoist p.body
pure $ MkHoistedProgram {body,bindings}
-342
View File
@@ -1,342 +0,0 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultilineStrings #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE RecursiveDo #-}
{- HLINT ignore "Use camelCase" -}
module Gyehoek.CPS.Lower
(lower, lowerProgram) where
import Gyehoek.CPS.Syntax
import Data.Vector.Strict (Vector)
import Control.Lens hiding (op)
import Numeric.Natural
import qualified Data.Vector.Strict as V
import Gyehoek.Wasm qualified as Wasm
import Gyehoek.Wasm hiding (Expr)
import Language.Sexp.Located qualified as SL
import Control.Monad.Fix
import qualified Gyehoek.Sexp
import Data.Text qualified as T
import Data.Foldable (fold)
import Gyehoek.Sexp (encodeOrShow)
import Gyehoek.Prelude
data Env = MkEnv
{ vars :: Vector Name
, kvars :: Vector Name
}
deriving (Show, Generic)
type instance Index Env = Natural
type instance IxValue Env = Name
instance Ixed Env where
ix i = #vars . ix (fromIntegral i)
tonat :: Integral a => a -> Natural
tonat = fromIntegral
-- | @makeSmallFixnum@ emits an expression injecting the i32 on top
-- of the stack into the SCM unitype.
makeSmallFixnum :: Wasm.Expr
makeSmallFixnum = [expr|
(@gyehoek "construct small fixnum")
(i32.const 1)
i32.shl
ref.i31
|]
getArgRegister :: Natural -> SL.Sexp
getArgRegister n = SL.Symbol [i|$arg#{n}|]
-- | Given an expression @e@ leaving a @ref eq@ atop the stack,
-- @pushArg rt n e@ sets the nth slot of the arg-passing array to the
-- result of @e@.
pushArg :: Natural -> Wasm.Expr -> Wasm.Expr
pushArg n e = [expr|
(@gyehoek begin pushArg)
##{e}
(global.set #{reg})
(@gyehoek end pushArg)
|]
where reg = getArgRegister n
-- | Pop the nth arg from the arg-passing array onto the stack.
popArg :: Natural -> Wasm.Expr
popArg n = [expr|
(@gyehoek begin popArg)
(global.get #{reg})
ref.as_non_null
(@gyehoek end popArg)
|]
where reg = getArgRegister n
lowerVal :: (HasCallStack, GenMod :> es) => Env -> Val -> Eff es Wasm.Expr
lowerVal g (ValImm imm) =
pure $ case imm of
ImmInt n -> [expr|
(i32.const #{n})
##{makeSmallFixnum}
|]
ImmBool b -> [expr|
(i32.const #{b'})
ref.i31
|]
where b' :: Int = if b then 0b11 else 0b01
_ -> _
lowerVal g (ValVar x) = do
pure [expr|(global.get #{l})|]
where
l = getArgRegister . fromIntegral . succ $ V.elemIndex x g.vars ^?! _Just
lower' :: (GenMod :> es) => Env -> Exp -> Eff es Wasm.Expr
lower' g (Halt [v]) = do
arg <- pushArg 0 <$> lowerVal g v
pure [expr|
##{arg}
(return_call $halt (i32.const 1))
|]
lower' g e@(ExpPrim p k) =
([expr|(@gyehoek :origin #{origin})|]<>)
<$> case p of
PrimAdd x y -> lowerBinOp "i32.add" g x y k
PrimMul x y -> lowerBinOp "i32.mul" g x y k
where origin = encodeOrShow @_ @Text e
lower' g (ExpIf c t f) = do
c' <- lowerVal g c
t' <- lower' g t
f' <- lower' g f
pure [expr|
##{c'}
(call $gh-truthy?)
(if (then ##{t'})
(else ##{f'}))
|]
lower' g (ExpLetRec [(r,AbsKappa kap)] e) = do
idx <- lowerKappa g kap
let g' = g & #kvars <>~ [r]
e' <- lower' g' e
let origin = encodeOrShow @_ @Text e
pure [expr|
(@gyehoek :origin #{origin})
(@gyehoek "push cont" :idx #{idx})
(array.set $cont-stack-type
(global.get $cont-stack)
(global.get $cont-stack-top)
(ref.func #{idx}))
(global.set $cont-stack-top
(i32.add (global.get $cont-stack-top)
(i32.const 1)))
##{e'}
|]
lower' g (ExpLetRec [(r,AbsLambda lam)] e) = do
idx <- lowerLambda g lam
let g' = g & #vars <>~ [r]
let n = succ $ length g.vars
e' <- lower' g' e
let reg = getArgRegister . fromIntegral $ n
pure [expr|
(i32.const 0)
(ref.func #{idx})
(struct.new $closure)
(global.set #{reg})
##{e'}
|]
lower' g e@(ExpApply f xs ktail) = do
let nargs = length xs
f' <- lowerVal g f
let l = succ $ V.elemIndex ktail g.kvars ^?! _Just
args <- fold <$>
itraverse (\i -> fmap (pushArg $ tonat i) . lowerVal g) xs
let origin = encodeOrShow @_ @Text e
pure [expr|
(@gyehoek :origin #{origin})
(@gyehoek "load args")
##{args}
(i32.const 1)
##{f'}
(ref.cast (ref $closure))
(struct.get $closure $code)
(return_call_ref $cont-type)
(@gyehoek todo
(f' ##{f'})
(ktail #{l}))
|]
lower' g e@(ExpContinue k xs) = do
let nargs = length xs
args <- fold <$>
itraverse (\i -> fmap (pushArg $ tonat i) . lowerVal g) xs
let origin = encodeOrShow @_ @Text e
pure [expr|
(@gyehoek :origin #{origin})
(@gyehoek "push args")
##{args}
(@gyehoek "nargs")
(i32.const #{nargs})
(@gyehoek "pop cont stack")
(global.get $cont-stack-top)
(i32.const #{l})
i32.sub
(global.set $cont-stack-top)
(global.get $cont-stack)
(global.get $cont-stack-top)
(array.get $cont-stack-type)
ref.as_non_null
(return_call_ref $cont-type)
|]
where
l = succ $ V.elemIndex k g.kvars ^?! _Just
lower' g e = error $ case Gyehoek.Sexp.encode e of
Left _ -> show e
Right x -> T.unpack x
lowerKappa :: GenMod :> es => Env -> Kappa -> Eff es Idx
lowerKappa g e@(MkKappa xs m) = do
let g' = g & #vars <>~ V.fromList xs
m' <- lower' g' m
let origin = encodeOrShow @_ @Text e
idx <- Wasm.defineFunction [wat|
(func (param i32)
(@gyehoek :origin #{origin})
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
##{m'})
|]
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
pure idx
lowerLambda :: GenMod :> es => Env -> Lambda -> Eff es Idx
lowerLambda g e@(MkLambda xs ktail m) = do
let g' = g & #vars .~ V.fromList xs
& #kvars <>~ [ktail]
m' <- lower' g' m
let origin = encodeOrShow @_ @Text e
idx <- Wasm.defineFunction [wat|
(func (param i32)
(@gyehoek :origin #{origin})
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
##{m'})
|]
Wasm.emit [wats|(elem declare funcref (ref.func #{idx}))|]
pure idx
lowerBinOp
:: (GenMod :> es)
=> Text -> Env -> Val -> Val -> Kappa -> Eff es Wasm.Expr
lowerBinOp op g x y (MkKappa [r] e) = do
let op' = SL.Symbol op
let g' = g & #vars <>~ [r]
let n = succ $ length (g ^. #vars)
let reg = getArgRegister . fromIntegral $ n
x' <- lowerVal g x
y' <- lowerVal g y
e' <- lower' g' e
pure [expr|
##{x'}
(i31.get_s (ref.cast (ref i31)))
(i32.const 1)
i32.shr_u
##{y'}
(i31.get_s (ref.cast (ref i31)))
(i32.const 1)
i32.shr_u
#{op'}
##{makeSmallFixnum}
(global.set #{reg})
##{e'}
|]
emitRuntime :: GenMod :> es => Eff es ()
emitRuntime = mfix \runtime -> do
Wasm.defineFunctions [wats|
(import "gyehoek" "write" (func $gh-write (param (ref eq))))
(import "gyehoek" "truthy?" (func $gh-truthy? (param (ref eq))
(result i32)))
|]
-- cont stack
Wasm.defineTypes [wats|
(type $heap-object (sub (struct (field $hash (mut i32)))))
(type $cont-type (func (param i32)))
(type $cont-stack-type (array (mut (ref null $cont-type))))
(type $closure (sub $heap-object
(struct (field $hash (mut i32))
(field $code (ref $cont-type)))))
|]
Wasm.defineGlobals [wats|
(global $cont-stack-top (mut i32) (i32.const 0))
(global $cont-stack (ref $cont-stack-type)
(array.new_default $cont-stack-type (i32.const 128)))
|]
-- arg registers
Wasm.defineGlobals [wats|
(global $arg0 (mut (ref null eq)) (ref.null eq))
(global $arg1 (mut (ref null eq)) (ref.null eq))
(global $arg2 (mut (ref null eq)) (ref.null eq))
(global $arg3 (mut (ref null eq)) (ref.null eq))
(global $arg4 (mut (ref null eq)) (ref.null eq))
(global $arg5 (mut (ref null eq)) (ref.null eq))
(global $arg6 (mut (ref null eq)) (ref.null eq))
(global $arg7 (mut (ref null eq)) (ref.null eq))
(global $arg8 (mut (ref null eq)) (ref.null eq))
(global $arg9 (mut (ref null eq)) (ref.null eq))
(global $arg10 (mut (ref null eq)) (ref.null eq))
(global $arg11 (mut (ref null eq)) (ref.null eq))
(global $arg12 (mut (ref null eq)) (ref.null eq))
(global $arg13 (mut (ref null eq)) (ref.null eq))
(global $arg14 (mut (ref null eq)) (ref.null eq))
(global $arg15 (mut (ref null eq)) (ref.null eq))
|]
-- other things 😼
Wasm.defineGlobal [wat|
(global $result (mut (ref null eq))
(ref.null eq))
|]
-- procedures
let arg = popArg 0
Wasm.defineFunction [wat|
(func $halt (param i32)
##{arg}
(global.set $result))
|]
pure ()
lower :: Exp -> Eff es Text
lower e = fmap Wasm.renderModule . Wasm.execGenMod $ do
runtime <- emitRuntime
let g = MkEnv mempty mempty
e' <- lower' g e
let origin = encodeOrShow @_ @Text e
Wasm.defineFunction [wat|
(func $scm-entry (param i32)
(@gyehoek :origin #{origin})
(local (ref eq) (ref eq) (ref eq) (ref eq) (ref eq))
##{e'})
|]
Wasm.defineFunction [wat|
(func (export "main")
(call $scm-entry (i32.const 0))
(call $gh-write (ref.as_non_null (global.get $result))))
|]
lowerProgram :: Program -> Eff es Text
lowerProgram (MkProgram e) = lower e
-145
View File
@@ -1,145 +0,0 @@
{-# LANGUAGE OverloadedLists #-}
module Gyehoek.CPS.Stackify
( stackifyExp
, stackifyProgram
, module Gyehoek.CPS.Syntax
) where
import Gyehoek.CPS.Syntax
import Gyehoek.Stack.Syntax qualified as Stk
import Data.Sequence (Seq)
import Data.Sequence qualified as Seq
import Gyehoek.GenSym
import Effectful.Writer.Static.Shared
import Data.Foldable
import qualified Data.HashMap.Strict as H
import Data.List (elemIndex)
import Gyehoek.Prelude
type Stackify = Writer Stk.Program
runStackify :: Eff (Stackify : es) a -> Eff es (a, Stk.Program)
runStackify = runWriter
live :: Free a => Env -> a -> List Name
live g e = free' e & filter \x ->
x `H.member` g.bound
&& not (x `elem` g.contStack)
stackify
:: (GenSym :> es, Stackify :> es)
=> Env -> Exp -> Eff es (Seq Stk.Instr)
stackify g (ExpLetRec [(f, kap@(AbsKappa' xs m))] e) = do
let vs = (f, Stk.ValLabel f) : (bindReg <$> xs)
let ls = live g kap
m' <- stackify (g & #bound .~ H.fromList (vs ++ (bindReg <$> ls))) m
tell [Stk.MkBlock f xs $
[Stk.Pop x | x <- ls] <> toList m']
let g' = g & #bound . at f ?~ Stk.ValLabel f
& #liveness . at f ?~ ls
stackify g' e
stackify g (ExpLetRec [(f, AbsLambda' xs k m)] e) = do
let vs = (k:xs) <&> \x -> (x, Stk.ValReg x)
m' <- stackify (g & #bound .~ H.fromList vs
& #contStack %~ (k:)) m
tell [Stk.MkBlock f xs . toList $ m']
stackify g e
stackify g (ExpIf c t f) = do
t' <- stackify g t
f' <- stackify g f
pure [ Stk.If (stackifyVal g c) (toList t') (toList f') ]
stackify g (ExpApply f xs ktail) = do
pure $
[ Stk.PushCont k ]
<> fromList [ Stk.Push (Stk.ValReg l) | l <- ls ]
<> [ Stk.Call (stackifyVal g f) (stackifyVal g <$> xs) ]
where
k = var g ktail
ls = fold $ (k ^? #ValImm . #ImmLabel)
>>= \klbl -> g ^. #liveness . at klbl
stackify g (ExpContinue k xs) =
-- return continuations require popping the stack. how do we know
-- when a continuation is a return continuation? is this a correct
-- test?
case elemIndex k g.contStack of
Nothing -> pure [ Stk.Call (Stk.ValLabel k) xs' ]
Just j -> do
ktail <- gensym' $ k ^. _Wrapped'
pure $
Seq.replicate j (Stk.PopCont "_")
<> [ Stk.PopCont ktail
, Stk.Call (Stk.ValReg ktail) (stackifyVal g <$> xs)
]
where xs' = stackifyVal g <$> xs
stackify g (ExpPrim p (MkKappa [x] e)) = do
e' <- stackify (g & #bound . at x ?~ Stk.ValReg x) e
pure $ [ Stk.Prim x (stackifyVal g <$> p) ] <> e'
stackify _ e = error [i|unimplemented exp: #{e}|]
stackifyVal :: Env -> Val -> Stk.Val
stackifyVal g = \case
ValImm imm -> Stk.ValImm imm
ValVar v -> var g v
v -> error [i|unimplemented val: #{v}|]
var :: Env -> Name -> Stk.Val
var g v = case g ^. #bound . at v of
Just x -> x
Nothing -> Stk.ValLabel v
bindReg :: Name -> (Name, Stk.Val)
bindReg x = (x, Stk.ValReg x)
data Env = MkEnv
{ bound :: HashMap Name Stk.Val
-- | for each locally-bound continuation @k@, @liveness@ has an
-- entry @(k,ls)@ where @ls@ is the sequence of registers @k@
-- expects to find saved on the stack.
, liveness :: HashMap Name (List Name)
, contStack :: List Name
}
deriving (Show, Generic)
emptyEnv :: Env
emptyEnv = MkEnv mempty mempty ["halt"]
stackifyExp :: GenSym :> es => Name -> Exp -> Eff es Stk.Program
stackifyExp lbl e = do
(code,p) <- runStackify $ stackify emptyEnv e
pure $ p <> Stk.MkProgram [ Stk.MkBlock lbl [] (toList code) ]
stackifyProgram :: GenSym :> es => Program -> Eff es Stk.Program
stackifyProgram (MkProgram e) = stackifyExp "main" e
fac :: Program
fac = [cps|
(letrec ((fac (λ (n ktail)
(prim (zero? n)
(κ (x0)
(if x0
(continue ktail 1)
(prim (- n 1)
(κ (x1)
(letrec ((fac-k0
(κ (x2)
(prim (* n x2)
(κ (x3)
(continue ktail x3))))))
(fac x1 fac-k0))))))))))
(fac 6 halt))
|]
+279 -180
View File
@@ -5,24 +5,29 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE DeriveAnyClass #-}
{- HLINT ignore "Avoid lambda using `infix`" -}
{- HLINT ignore "Redundant $" -}
module Gyehoek.CPS.Syntax
( Val(..)
, Kappa(..)
, Lambda(..)
, Exp(..)
, Kexp(..)
, ExpF(..)
, Name(..)
, Prim(..)
, Builtin(..)
, Program(..)
, HoistedProgram(..)
, Lit(..)
, Imm(..)
, Obj(..)
, Hob(..)
, pattern Void
, Label(..)
, Reg(..)
, pattern Halt
, pattern Halt1
, _MkKappa
, _ExpPrim
, _ExpBuiltin
, _ExpLetRec
, _ExpApply
, _AbsLambda'
@@ -37,27 +42,31 @@ module Gyehoek.CPS.Syntax
, Abs(..)
, Free(..)
, pattern ValLabel
, labelName -- don't like that this is part of the api
, pattern ObjLabel
, absBody
, pattern MkAbs
, _MkAbs
, unhoist
, pattern ExpJump
, _ExpJump
)
where
import Language.SexpGrammar qualified as S
import Gyehoek.Sexp qualified
import Gyehoek.Scheme.Syntax (Name (..), Prim(..), primSexpIso, Lit(..), pattern Void)
import Language.SexpGrammar.Generic
import Gyehoek.Scheme.Syntax (Name (..), Builtin(..), builtinDatumIso, Lit(..))
import Gyehoek.Sexp qualified as S
import Control.Category
import Prelude hiding ((.), id)
import Language.Haskell.TH.Quote (QuasiQuoter)
import Language.Sexp.Located (Sexp)
import qualified Data.InvertibleGrammar.Base as IG
import Data.InvertibleGrammar.Base (type (:-)((:-)))
import qualified Data.HashSet as HS
import Data.Monoid (Endo)
import Data.Functor.Foldable.TH
import qualified Gyehoek.Sexp as GS
import qualified Language.Sexp.Located as SL
import Data.Data.Lens (uniplate)
import Gyehoek.Prelude hiding (op)
import Gyehoek.Sexp (G, (:-)(..), Datum)
import qualified Data.InvertibleGrammar.Base as IG
import Gyehoek.GenSym (Gen)
import Data.String (IsString)
import Control.Applicative
import qualified Data.HashMap.Strict as H
-- Data types
@@ -66,13 +75,24 @@ data Val
| ValVar Name
deriving (Show, Generic, Data, Eq)
pattern ValLabel :: Name -> Val
pattern ValLabel :: Label -> Val
pattern ValLabel x = ValImm (ImmLabel x)
newtype Label = MkLabel { inner :: Name }
deriving stock (Generic, Data)
deriving newtype (Show, Eq, Gen, IsString, Hashable)
deriving anyclass (NFData, Wrapped)
newtype Reg = MkReg { inner :: Name }
deriving stock (Generic, Data)
deriving newtype (Show, Eq, Gen, IsString, Hashable)
deriving anyclass (NFData, Wrapped)
data Imm
= ImmInt Int
| ImmBool Bool
| ImmLabel Name
| ImmLabel Label
| ImmUndefined
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
@@ -82,9 +102,15 @@ data Obj
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
pattern ObjLabel :: Label -> Obj
pattern ObjLabel l = ObjImm (ImmLabel l)
-- | a heap object.
data Hob
= HobClosure { label :: Name, env :: List Obj }
= HobClosure { label :: Label, env :: List Obj }
-- should a continuation have a label, or an Obj?
| HobContinuation { cont :: Obj, stack :: NonEmpty (List Obj) }
| HobPair Obj Obj
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
@@ -99,46 +125,92 @@ data Abs
| AbsLambda Lambda
deriving (Show, Generic, Data, Eq)
pattern AbsKappa' :: [Name] -> Exp -> Abs
pattern AbsKappa' :: List Name -> Exp -> Abs
pattern AbsKappa' xs e = AbsKappa (MkKappa xs e)
pattern AbsLambda' :: [Name] -> Name -> Exp -> Abs
pattern AbsLambda' :: List Name -> Name -> Exp -> Abs
pattern AbsLambda' xs e ktail = AbsLambda (MkLambda xs e ktail)
{-# COMPLETE AbsKappa', AbsLambda' #-}
_MkAbs :: Iso' Abs (List Name, Maybe Name, Exp)
_MkAbs = iso
(\case
AbsKappa' xs e -> (xs,Nothing,e)
AbsLambda' xs ktail e -> (xs,Just ktail,e))
(\(xs,ktail,e) -> case ktail of
Just k -> AbsLambda' xs k e
Nothing -> AbsKappa' xs e)
pattern MkAbs :: List Name -> Maybe Name -> Exp -> Abs
pattern MkAbs xs ktail body <- (view _MkAbs -> (xs,ktail,body))
where MkAbs xs ktail body = review _MkAbs (xs,ktail,body)
{-# COMPLETE MkAbs #-}
_ExpJump :: Prism' Exp (Val, List Val, Maybe Kexp)
_ExpJump = prism'
(\(f,xs,ktail) -> case ktail of
Just k -> ExpApply f xs k
Nothing -> ExpContinue f xs)
\case
ExpApply f xs ktail -> Just (f,xs,Just ktail)
ExpContinue f xs -> Just (f,xs,Nothing)
_ -> Nothing
pattern ExpJump :: Val -> List Val -> Maybe Kexp -> Exp
pattern ExpJump f xs ktail <- (preview _ExpJump -> Just (f,xs,ktail))
where ExpJump f xs ktail = review _ExpJump (f,xs,ktail)
data Exp
= ExpPrim (Prim Val) Kappa
= ExpBuiltin (Builtin Val) Kexp
| ExpLetRec { binders :: List (Name, Abs), body :: Exp }
| ExpContinue Name (List Val)
| ExpIf Val Exp Exp
| ExpContinue Val (List Val)
| ExpIf Val Name Name
| ExpApply
{ op :: Val
, args :: List Val
, cont :: Name
, cont :: Kexp
}
deriving (Show, Generic, Data, Eq)
data Kexp
= KexpVar Name
| KexpKappa Kappa
deriving (Show, Generic, Data, Eq)
pattern Halt :: List Val -> Exp
pattern Halt xs = ExpContinue "halt" xs
pattern Halt xs = ExpContinue (ValLabel "halt") xs
pattern Halt1 :: Val -> Exp
pattern Halt1 x = ExpContinue "halt" [x]
pattern Halt1 x = ExpContinue (ValLabel "halt") [x]
data Def = DefConstant Name Exp
deriving (Show, Generic, Data)
data Program = MkProgram
{ body :: Exp
newtype Program = MkProgram
{ body :: Lambda
}
deriving (Show, Generic, Data)
data HoistedProgram = MkHoistedProgram
{ bindings :: HashMap Label Abs
, body :: Lambda
}
deriving stock (Show, Generic, Data)
type instance Index HoistedProgram = Label
type instance IxValue HoistedProgram = Abs
instance Ixed HoistedProgram where ix j = #bindings . ix j
instance At HoistedProgram where at j = #bindings . at j
instance Each HoistedProgram HoistedProgram Abs Abs where
each = #bindings . each
makePrisms ''Kappa
-- makeLenses ''Kappa
makePrisms ''Exp
-- makeLenses ''Exp
-- makeFieldsNoPrefix ''Exp
-- makeFieldsNoPrefix ''Kappa
-- makeLensesWith abbreviatedFields ''Exp
-- makeLensesFor [("binders", "_binders"), ("body", "_body")] ''Exp
makeFieldsId ''Exp
makeFieldsId ''Kappa
makeFieldsId ''Lambda
@@ -158,159 +230,193 @@ _AbsLambda' = prism'
(\case AbsLambda' bs ktail e -> Just (bs,ktail,e)
_ -> Nothing)
instance Plated Exp where
plate = uniplate
-- plate k = \case
-- ExpPrim p kap -> ExpPrim p <$> body k kap
-- ExpLetRec bs e -> ExpLetRec <$> (each . _2 . body) k bs <*> k e
-- ExpContinue c xs -> pure $ ExpContinue c xs
-- ExpIf c t f -> ExpIf c <$> k t <*> k f
-- ExpApply f xs ktail -> pure $ ExpApply f xs ktail
instance Plated Exp where plate = uniplate
absBody :: Lens' Abs Exp
absBody = lens
(\case
AbsLambda lam -> lam.body
AbsKappa kap -> kap.body)
(\cases
(AbsLambda lam) b -> AbsLambda $ lam & #body .~ b
(AbsKappa kap) b -> AbsKappa $ kap & #body .~ b)
unhoist :: HoistedProgram -> Program
unhoist p =
MkProgram $ p.body & body %~ ExpLetRec
(p ^.. #bindings . itraversed . withIndex
. to (\(MkLabel l, ab) -> (l,ab)))
-- SexpIso instances
-- DatumIso instances
instance S.SexpIso Val where
sexpIso = match
$ With (\imm -> imm . S.sexpIso)
$ With (\var -> var . S.sexpIso)
$ End
instance S.DatumIso Val where
datumIso = S.match
$ S.With (\imm -> imm . S.datumIso)
$ S.With (\var -> var . S.datumIso)
$ S.End
instance S.SexpIso Obj where
sexpIso = match
$ With (\imm -> imm . S.sexpIso)
$ With (\hob -> hob . S.sexpIso)
$ End
instance S.DatumIso Obj where
datumIso = S.match
$ S.With (\imm -> imm . S.datumIso)
$ S.With (\hob -> hob . S.datumIso)
$ S.End
instance S.SexpIso Imm where
sexpIso = match
$ With (. S.int)
$ With (. GS.schemeBool)
$ With (. labelName)
$ End
instance S.DatumIso Imm where
datumIso = S.match
$ S.With (. S.int)
$ S.With (. S.datumIso)
$ S.With (. S.datumIso)
$ S.With (. S.unreadable (const "#<undefined>"))
$ S.End
labelName :: S.SexpGrammar Name
labelName = S.coproduct
[ S.sexpIso @Name >>> Gyehoek.Sexp.prismIso
(S.expected "label")
(prefixed @Name "$")
, S.list $ S.el (S.sym "$") >>> S.el (S.sexpIso @Name)
]
instance S.DatumIso Label where
datumIso = S.with \g -> S.coproduct
[ S.datumIso @Name >>> S.prismIso
(S.expected "label")
(prefixed @Name "$")
, S.list $ S.el (S.sym "$") >>> S.el (S.datumIso @Name)
]
>>> g
instance S.SexpIso Hob where
sexpIso = match
$ With (. closure)
$ End
instance S.DatumIso Reg where
datumIso = S.with \g ->
S.datumIso @Name >>> S.prismIso
(S.expected "register")
(prefixed @Name "%")
>>> g
instance S.DatumIso Hob where
datumIso = S.match
$ S.With (. closure)
$ S.With (. cont)
$ S.With (. conspair)
$ S.End
where
conspair = S.dottedList (S.el S.datumIso) S.datumIso
-- closures can be printed, but not parsed.
closure :: S.Grammar S.Position (Sexp :- t) (List Obj :- Name :- t)
closure :: G (Datum :- t) (List Obj :- Label :- t)
closure = IG.Flip $ IG.PartialIso
(\(env:-code:-t) -> SL.Modified SL.Hash [GS.sx|(#{code} ##{env})|] :- t)
(\(env:-code:-t) -> S.Unreadable [i|\#<procedure $#{code}>|] :- t)
(const . Left $ mempty)
cont :: G (Datum :- t) (NonEmpty (List Obj) :- _ :- t)
cont = IG.Flip $ IG.PartialIso
(\(_ :- l :- t) ->
let x = S.encodeOrShow' @Text S.datumIso l
in S.Unreadable [i|\#<continuation #{x}>|] :- t)
(const . Left $ mempty)
instance S.SexpIso Lambda where
sexpIso = match
$ With (. lambda)
$ End
instance S.DatumIso Lambda where
datumIso = S.with (lam >>>)
where
lambda = S.list $
S.el Gyehoek.Sexp.lambdaKeyword
>>> S.el binders
>>> S.el S.sexpIso
binders :: forall t.
IG.Grammar S.Position (Sexp :- t) (Name :- List Name :- t)
binders = S.list $
S.rest (S.sexpIso @Name)
>>> S.onTail (S.flipped $ IG.PartialIso
(\(ktail:-args:-t) -> (args ++ [ktail]) :- t)
(\(args:-t) -> case args ^? _Snoc of
Just (args',ktail) -> Right $ ktail :- args' :- t
Nothing -> Left $ S.expected "cont param")
)
lam :: forall t. G (Datum :- t) (Exp :- Name :- List Name :- t)
lam = S.lambdaLike
S.lambdaKeyword
binders
(S.el $ S.datumIso @Exp)
binders :: forall t. G (Datum :- t) (Name :- List Name :- t)
binders =
S.list (S.rest $ S.datumIso @Name)
>>> S.flipped S.snoced
>>> S.swap
instance S.SexpIso Kappa where
sexpIso = match
$ With (. kappa)
$ End
instance S.DatumIso Kappa where
datumIso = S.with \g ->
S.lambdaLike S.kappaKeyword
(S.datumIso @(List Name))
(S.el $ S.datumIso @Exp)
>>> g
instance S.DatumIso Abs where
datumIso = S.match
$ S.With (\lambda -> lambda . S.datumIso)
$ S.With (\kappa -> kappa . S.datumIso)
$ S.End
instance S.DatumIso Exp where
datumIso = S.match
$ S.With (. builtin)
$ S.With (. letrec)
$ S.With (. continue)
$ S.With (. if_)
$ S.With (. app)
$ S.End
where
kappa = S.list $
S.el Gyehoek.Sexp.kappaKeyword
>>> S.el (S.list $ S.rest S.sexpIso)
>>> S.el S.sexpIso
instance S.SexpIso Abs where
sexpIso = match
$ With (\lambda -> lambda . S.sexpIso)
$ With (\kappa -> kappa . S.sexpIso)
$ End
instance S.SexpIso Exp where
sexpIso = match
$ With (. prim)
$ With (. letrec)
$ With (. continue)
$ With (. if_)
$ With (. app)
$ End
where
continue = S.list $
continue = S.listWithStyle (S.StyleSyntax 1) $
S.el (S.sym "continue")
>>> S.el S.sexpIso
>>> S.rest S.sexpIso
letrec = Gyehoek.Sexp.let_ "letrec" S.sexpIso S.sexpIso S.sexpIso
if_ = S.list $ S.el (S.sym "if")
>>> S.el S.sexpIso >>> S.el S.sexpIso >>> S.el S.sexpIso
>>> S.el S.datumIso
>>> S.rest S.datumIso
letrec = S.letLike "letrec" S.datumIso S.datumIso S.datumIso
if_ = S.ifLike "if"
S.datumIso S.datumIso S.datumIso
app :: forall t.
IG.Grammar S.Position (Sexp :- t) (Name :- ([Val] :- (Val :- t)))
app = S.list $ S.el (S.sexpIso @Val)
-- >>> S.flipped Gyehoek.Sexp.nonEmptyGrammar
>>> S.rest (S.sexpIso @Val)
-- >>> _
>>> S.onTail (S.flipped $ IG.PartialIso
(\(karg :- args :- op :- t) ->
(args ++ [ValVar karg]) :- op :- t)
(\(xs :- op :- t) -> case xs ^? _Snoc of
Just (args,preview #ValVar -> Just karg) ->
Right $ karg:- args :- op :- t
_ -> Left $ S.expected "continuation arg"
))
where
_ = S.flipped $ Gyehoek.Sexp.nonEmptyGrammar @S.Position @Val
prim = S.list $
S.el (S.sym "prim")
>>> S.el (primSexpIso id (S.sexpIso @Val))
>>> S.el S.sexpIso
G (Datum :- t) (Kexp :- List Val :- Val :- t)
app = S.list $
S.flipped (S.PartialIso
(\(S.MkListContext ctx :- t) ->
case ctx of
f:kexp:xs -> S.MkListContext (f : snoc xs kexp) :- t
_ -> error "unreachable")
(\(S.MkListContext ctx :- t) ->
case unsnoc ctx of
Just (f:xs,kexp) -> Right $ S.MkListContext (f:kexp:xs) :- t
_ -> Left $ S.expected "continuation arg"))
>>> S.el (S.datumIso @Val)
>>> S.el (S.datumIso @Kexp)
>>> S.rest (S.datumIso @Val)
>>> S.onTail S.swap
builtin = S.listWithStyle (S.StyleSyntax 1) $
S.el (S.sym "builtin")
>>> S.el (builtinDatumIso id (S.datumIso @Val))
>>> S.el S.datumIso
instance S.SexpIso Program where
sexpIso = with \prog -> S.sexpIso @Exp >>> prog
instance S.DatumIso Kexp where
datumIso = S.match
$ S.With (S.datumIso @Name >>>)
$ S.With (S.datumIso @Kappa >>>)
$ S.End
instance S.DatumIso Program where
datumIso = S.with \prog -> S.datumIso @Lambda >>> prog
-- the printed representation is pretty dishonest in its current
-- state. consider the following hoisted program:
--
-- (letrec ((k (κ () (continue start-ktail 123))))
-- (λ (start-ktail)
-- (continue k)))
--
-- here, `start-ktail` is bound in `k`, but the printed representation
-- fails to reflect that.
instance S.DatumIso HoistedProgram where
datumIso = S.with \prog ->
S.letLike "letrec"
(S.datumIso @Label) (S.datumIso @Abs) (S.datumIso @Lambda)
>>> S.onTail (S.iso H.fromList H.toList)
>>> prog
-- quasiquoters
class Data a => CPS a where
toCPS :: Sexp -> a
toCPS :: HasCallStack => Datum -> a
instance CPS Exp where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Val where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Kappa where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Lambda where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Abs where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Program where toCPS = Gyehoek.Sexp.fromSexp
instance CPS Exp where toCPS = S.fromDatumUnsafe S.datumIso
instance CPS Val where toCPS = S.fromDatumUnsafe S.datumIso
instance CPS Kappa where toCPS = S.fromDatumUnsafe S.datumIso
instance CPS Lambda where toCPS = S.fromDatumUnsafe S.datumIso
instance CPS Abs where toCPS = S.fromDatumUnsafe S.datumIso
instance CPS Program where toCPS = S.fromDatumUnsafe S.datumIso
instance CPS HoistedProgram where toCPS = S.fromDatumUnsafe S.datumIso
cps :: QuasiQuoter
cps = Gyehoek.Sexp.makeSx' [| toCPS |]
cps :: S.QuasiQuoter
cps = S.makeSx' [| toCPS |]
deleteFrom :: (Foldable f, Hashable a) => f a -> HashSet a -> HashSet a
deleteFrom = flip $ foldr HS.delete
insertFrom :: (Foldable f, Hashable a) => f a -> HashSet a -> HashSet a
insertFrom = flip $ foldr HS.insert
toHashSetOf :: Hashable a => Getting (Endo (HashSet a)) s a -> s -> HashSet a
toHashSetOf l = foldrOf l HS.insert mempty
class Free a where
free :: a -> HashSet Name
free = freeWithBound mempty
@@ -318,7 +424,8 @@ class Free a where
freeWithBound :: HashSet Name -> a -> HashSet Name
freeWithBound bound = HS.fromList . freeWithBound' bound
-- | Free variables given in the order of their appearance.
-- | Free variables given in the same left-to-right order they
-- appear.
free' :: a -> List Name
free' = freeWithBound' mempty
@@ -328,22 +435,32 @@ instance Free Abs where
freeWithBound' bound (AbsKappa kap) = freeWithBound' bound kap
freeWithBound' bound (AbsLambda lam) = freeWithBound' bound lam
mif :: Alternative f => (a -> Bool) -> a -> f a
mif p a
| p a = pure a
| otherwise = empty
instance Free Kexp where
freeWithBound' bound = \case
KexpVar x -> mif (`notElem` bound) x
KexpKappa kap -> freeWithBound' bound kap
instance Free Exp where
freeWithBound' bound = \case
ExpPrim p k ->
p & toListOf (folded . #ValVar . filtered (`notElem` bound))
& (<> freeWithBound' bound k)
ExpBuiltin p k ->
(p ^.. folded . #ValVar . filtered (`notElem` bound))
++ freeWithBound' bound k
ExpLetRec bs m ->
foldMapOf (each . _2) (freeWithBound' bound') bs
<> freeWithBound' bound' m
where bound' = bound & insertFrom (bs ^.. each . _1)
ExpContinue k xs -> filter (`notElem` bound) (k : xs ^.. each . #ValVar)
ExpContinue k xs -> filter (`notElem` bound) ((k:xs) ^.. each . #ValVar)
ExpIf c t f ->
(c ^.. #ValVar . filtered (`notElem` bound))
<> freeWithBound' bound t <> freeWithBound' bound f
<> mif (`notElem` bound) t <> mif (`notElem` bound) f
ExpApply f xs k ->
(f:xs) ^.. (each . #ValVar . filtered (`notElem` bound))
<> (k ^.. filtered (`notElem` bound))
<> freeWithBound' bound k
instance Free Kappa where
freeWithBound' bound (MkKappa xs m) =
@@ -352,21 +469,3 @@ instance Free Kappa where
instance Free Lambda where
freeWithBound' bound (MkLambda xs k m) =
freeWithBound' (bound & insertFrom (k:xs)) m
class Vars a where
-- | Traverse the immediate variables of an expression.
vars :: Traversal' a Name
instance Vars Val where
vars k (ValVar x) = ValVar <$> k x
vars _ x = pure x
instance Vars a => Vars (Prim a) where
vars k p = traverseOf (each . vars) k p
instance Vars Exp where
vars k (ExpPrim p kap) = ExpPrim <$> vars k p <*> pure kap
vars k (ExpContinue kname xs) = ExpContinue <$> k kname <*> pure xs
vars _ e = pure e
+36 -37
View File
@@ -1,5 +1,5 @@
module Gyehoek.Driver
(main, lower_e2e, convert_e2e, parse_e2e, readScm, eval_e2e)
(main, convert_e2e, parse_e2e, readScm, eval_cps1_e2e, eval_cps2_e2e)
where
import Gyehoek.Options
@@ -17,7 +17,6 @@ import qualified Data.Text.Encoding as T
import System.IO (Handle)
import System.IO qualified as IO
import Gyehoek.CPS.Convert
import Gyehoek.CPS.Lower
import Gyehoek.CPS.Eval qualified as CPS
import Control.Monad
import Text.Pretty.Simple (pShowNoColor)
@@ -25,20 +24,20 @@ import System.Process.Typed
import System.Environment.Blank (getEnvDefault)
import qualified Data.Text.IO as TIO
import qualified Data.ByteString.Lazy as BS
import Gyehoek.CPS.Stackify (stackifyProgram)
import Gyehoek.Stack.VM (eval, writeObj, Obj)
import qualified Data.Text as T
import Gyehoek.Stack.Syntax qualified as Stk
import Gyehoek.CPS.Close (closeProgram)
import Control.Lens.Extras (is)
import Control.Arrow ((>>>))
import Gyehoek.Prelude
import Gyehoek.Jalmot
import qualified Gyehoek.Sexp as S
import Gyehoek.CPS.Hoist (hoistProgram)
main :: IO ()
main = do
opts <- execParser $ info (helper <*> parser) fullDesc
runEff . runFileSystem . runGenSym . driver $ opts
runJalmotIO . runFileSystem . runGenSym . driver $ opts
@@ -65,11 +64,12 @@ fileName :: FilePath -> FilePath
fileName "-" = "<interactive>"
fileName e = e
readScm :: FileSystem :> es => FilePath -> Eff es Scm.Program
readScm
:: forall es. (Jalmot :> es, FileSystem :> es)
=> FilePath -> Eff es Scm.Program
readScm f =
withFile f FS.ReadMode $ \h ->
Sexp.parseSexps @Scm.CommandOrDef (fileName f) <$> hGetContents h
>>= either error (pure . Scm.MkProgram)
S.decodeDataWith @es S.dataIso =<< hGetContents h
inspectWasm :: IOE :> es => Text -> Eff es ()
inspectWasm wat = do
@@ -107,7 +107,7 @@ dumpOrRun dump run acquire do_dump do_run =
when run (do_run x)
driver
:: (GenSym :> es, FileSystem :> es, IOE :> es)
:: (GenSym :> es, FileSystem :> es, Jalmot :> es, IOE :> es)
=> Options -> Eff es ()
driver opts = do
scm <- readScm opts.sourceFile
@@ -115,41 +115,40 @@ driver opts = do
hPutStrLn FS.stdout . view strict . pShowNoColor $ scm
cps <- convertProgram scm
when opts.dumpCPS do
hPutStrLn FS.stdout $ Sexp.encodePretty cps ^?! _Right
S.writeDatum cps
closedCps <- closeProgram cps
when opts.dumpClosed do
hPutStrLn FS.stdout $ Sexp.encodePretty closedCps ^?! _Right
S.writeDatum closedCps
hoistedCps <- hoistProgram closedCps
when opts.dumpHoisted do
S.writeDatum hoistedCps
let rt_is p = is (_Just . p) opts.runtime
dumpOrRun opts.dumpStackified (rt_is #Stackify)
(stackifyProgram closedCps)
(hPutStrLn FS.stdout . Stk.encodeProgram)
(eval >>> fmap writeObj
>>> T.unwords
>>> hPutStrLn FS.stdout)
when (rt_is #HigherOrderCPS) do
CPS.evalProgram cps
>>= S.writeData
when (rt_is #CPS) do
closedCps
& CPS.evalProgram
& fmap writeObj
& T.unwords
& hPutStrLn FS.stdout
dumpOrRun opts.inspectWasm (rt_is #Wasm)
(lowerProgram cps)
inspectWasm
(\wat -> withFile opts.output FS.WriteMode \h -> hPutStrLn h wat)
CPS.evalProgram closedCps
>>= S.writeData
parse_e2e :: FilePath -> IO Scm.Program
parse_e2e = runEff . runFileSystem . readScm
parse_e2e = runJalmotIO . runFileSystem . readScm
convert_e2e :: FilePath -> IO CPS.Program
convert_e2e = runEff . runFileSystem . runGenSym
convert_e2e = runJalmotIO . runFileSystem . runGenSym
. (closeProgram <=< convertProgram <=< readScm)
lower_e2e :: FilePath -> IO Text
lower_e2e =
runEff . runFileSystem . runGenSym
. (lowerProgram <=< closeProgram <=< convertProgram <=< readScm)
eval_cps1_e2e :: FilePath -> IO Text
eval_cps1_e2e fp = runJalmotIO . runFileSystem . runGenSym $
readScm fp
>>= convertProgram
>>= closeProgram
>>= CPS.evalProgram
>>= pure . S.encodeOrShowData' S.dataIso
eval_e2e :: FilePath -> IO (List Obj)
eval_e2e fp = runEff . runFileSystem . runGenSym $ do
stk <- stackifyProgram <=< closeProgram <=< convertProgram <=< readScm $ fp
pure . eval $ stk
eval_cps2_e2e :: FilePath -> IO Text
eval_cps2_e2e fp = runJalmotIO . runFileSystem . runGenSym $
readScm fp
>>= convertProgram
-- >>= closeProgram
>>= CPS.evalProgram
>>= pure . S.encodeOrShowData' S.dataIso
+75
View File
@@ -0,0 +1,75 @@
module Gyehoek.Jalmot
( Jalmot
, Exception(..)
, AJalmot(..)
, AJalmotCS(..)
, module Effectful.Error.Static
, runJalmot
, runJalmotIO
, runJalmotIOE
, runJalmotUnsafe
, runJalmotCS
)
where
import Gyehoek.Prelude
import Text.Megaparsec.Error (ParseErrorBundle, errorBundlePretty)
import Data.Void (Void)
import Effectful.Exception
import Effectful.Error.Static
import qualified Data.InvertibleGrammar as Grammar
import Gyehoek.Sexp.Syntax (Ann)
import Prettyprinter (defaultLayoutOptions, layoutPretty, pretty)
import Prettyprinter.Render.String (renderString)
import Control.Exception.Base (throw)
deriving instance Show p => Show (Grammar.ErrorMessage p)
deriving instance Data p => Data (Grammar.ErrorMessage p)
data AJalmot
= ReaderError (ParseErrorBundle Text Void)
| GrammarError (Grammar.ErrorMessage Ann)
| VMError Text
| EvalError Text
deriving (Show, Generic, Data)
data AJalmotCS = MkAJalmotCS !CallStack !AJalmot
deriving (Show)
type Jalmot = Error AJalmot
runJalmot :: Eff (Jalmot : es) a -> Eff es (Either (CallStack, AJalmot) a)
runJalmot = runError
runJalmotCS :: Eff (Jalmot : es) a -> Eff es (Either AJalmotCS a)
runJalmotCS = (mapped . _Left %~ uncurry MkAJalmotCS) . runError
runJalmotIOE :: IOE :> es => Eff (Jalmot : es) a -> Eff es a
runJalmotIOE eff =
runJalmot eff >>= \case
Right a -> pure a
Left (cs,jm) -> throwIO $ MkAJalmotCS cs jm
runJalmotIO :: Eff '[Jalmot, IOE] a -> IO a
runJalmotIO = runEff . runJalmotIOE
runJalmotUnsafe :: Eff '[Jalmot] a -> a
runJalmotUnsafe m = case runPureEff . runJalmot $ m of
Left (cs,e) -> throw $ MkAJalmotCS cs e
Right x -> x
instance Exception AJalmot where
displayException = \case
ReaderError eb -> errorBundlePretty eb
GrammarError err ->
pretty err
& layoutPretty defaultLayoutOptions
& renderString
VMError err -> [i|#{err}|]
EvalError err -> [i|#{err}|]
instance Exception AJalmotCS where
backtraceDesired = const False
displayException (MkAJalmotCS cs jm) =
"\n" <> displayException jm <> "\n\n" <> prettyCallStack cs <> "\n"
+1 -22
View File
@@ -1,25 +1,4 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Gyehoek.Language
( Language(..)
(
) where
import Data.Kind (Type)
import Gyehoek.Prelude
import Language.SexpGrammar (Position, Grammar, (:-), Sexp)
class Language l where
type Program l :: Type
languageName :: Text
programGrammar :: forall t. Grammar Position (List Sexp :- t) (Program l :- t)
readProgramFile
:: forall l es. Language l
=> FilePath -> Eff es (Program l)
readProgramFile fp = _
readProgramStringPos
:: forall l. Language l
=> Position -> Text -> Either Text (Program l)
readProgramStringPos pos s = _
+62
View File
@@ -0,0 +1,62 @@
{-# LANGUAGE DeriveAnyClass #-}
module Gyehoek.Language.Common
(
-- * Syntax
Lib(..)
, LibName(..)
, Exports
, Imports
, ExternName(..)
, Name(..)
) where
import Gyehoek.Prelude
import Data.String (IsString)
import Gyehoek.GenSym (Gen)
import qualified Gyehoek.Sexp as S
-- | R⁷RS 라이프러리의 표현.
data Lib name body = MkLib
{ name :: LibName
, exports :: Exports name
, imports :: Imports name
}
deriving (Show)
newtype LibName = MkLibName { getLibName :: NonEmpty Text }
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData, Hashable)
-- | A map whose keys are names of library definitions and whose
-- values are the names they are exported as. An @⟨identifier⟩@ @x@
-- corresponds to an entry @(⟨identifier⟩, ⟨identifier⟩)@, while a
-- @(rename ⟨identifier₁⟩ ⟨identifier₂⟩)@ form corresponds to an entry
-- @(⟨identifier₁⟩, ⟨identifier₂⟩)@.
type Exports name = HashMap name ExternName
-- | A map whose keys are symbols to be brought into the library's
-- environment and whose values are pairs of the library in which the
-- symbol is defined and the name the symbol is exported as.
type Imports name = HashMap name (LibName, ExternName)
-- | Representation of an identifier at the library boundary. While
-- 'Name's are used within a compilation unit and may be decorated
-- with additional structure and metadata, they are exported as
-- 'ExternName's, which are essentially just plain strings.
newtype ExternName = MkExternName { getExternName :: Text }
deriving newtype (Show)
newtype Name = MkName { inner :: Text }
deriving newtype (Show, Eq, Ord, IsString, Gen, Hashable)
deriving stock (Generic, Data)
deriving anyclass (Wrapped, NFData)
instance Prefixed Name where
prefixed (MkName s) = _Wrapped' . prefixed @Text s . from _Wrapped'
--- DatumIsos
instance S.DatumIso Name where
datumIso = S.symbol >>> S.iso coerce coerce
+30
View File
@@ -0,0 +1,30 @@
{-# LANGUAGE TemplateHaskell #-}
module Gyehoek.Lift1
( Lift1(..)
, lift1
) where
import Gyehoek.Prelude hiding ((:<))
import Language.Haskell.TH (Quote, Exp, listE)
import Language.Haskell.TH.Syntax (Lift (..))
import Control.Comonad.Cofree (Cofree(..))
-- 뻘짓뻘짓뻘짓뻘짓뻘짓
class Lift1 f where
liftLift :: Quote m => (a -> m Exp) -> f a -> m Exp
lift1 :: (Lift1 f, Lift a, Quote m) => f a -> m Exp
lift1 = liftLift lift
--- instances
instance Lift1 f => Lift1 (Cofree f) where
liftLift l (a :< e) = [|(:<) $(l a) $(liftLift (liftLift l) e)|]
instance Lift1 List where
liftLift l xs = listE $ l <$> xs
instance Lift1 NonEmpty where
liftLift l (x :| xs) = [|(:|) $(l x) $(liftLift l xs)|]
+22 -10
View File
@@ -13,14 +13,13 @@ import Data.Foldable
import Gyehoek.Prelude hiding (argument)
data Runtime = Stackify | Wasm | CPS
data Runtime = Wasm | CPS | HigherOrderCPS
deriving (Show, Generic, Eq)
data Language
= LanguageScheme
| LanguageCPS
| LanguageClosed
| LanguageStackified
| LanguageWasm
deriving (Show, Generic, Eq)
@@ -28,29 +27,30 @@ data Options = MkOptions
{ dumpClosed :: Bool
, dumpCPS :: Bool
, dumpParsed :: Bool
, dumpStackified :: Bool
, dumpHoisted :: Bool
, noColour :: Bool
, runtime :: Maybe Runtime
, inspectWasm :: Bool
, output :: FilePath
, sourceFile :: FilePath
, sourceLanguage :: Language
, targetLanguage :: Language
}
deriving (Show, Generic)
languageValues = ["scheme","cps","closed","stackified","wasm"]
languageValues = ["scheme","cps","closed","wasm"]
languageReader = maybeReader \case
"scheme" -> Just LanguageScheme
"cps" -> Just LanguageCPS
"closed" -> Just LanguageClosed
"stackified" -> Just LanguageStackified
"wasm" -> Just LanguageWasm
_ -> Nothing
runtimeValues = ["stackify","wasm","cps","none"]
runtimeReader = maybeReader \case
"stackify" -> Just (Just Stackify)
"wasm" -> Just (Just Wasm)
"cps" -> Just (Just CPS)
"cps1" -> Just (Just CPS)
("cps";"higher-order-cps") -> Just (Just HigherOrderCPS)
"none" -> Just Nothing
_ -> Nothing
@@ -58,15 +58,19 @@ parser :: Parser Options
parser = do
dumpClosed <- switch (long "dump-closed")
dumpCPS <- switch (long "dump-cps")
dumpStackified <- switch (long "dump-stackified")
dumpParsed <- switch (long "dump-parsed")
dumpHoisted <- switch (long "dump-hoisted")
noColour <- switch . fold $
[ long "no-colour"
, long "no-color"
]
inspectWasm <- switch $ long "inspect-wasm" <> short 'p'
runtime <- option runtimeReader . fold $
[ long "runtime"
, short 'R'
, value (Just Stackify)
, value (Just HigherOrderCPS)
, completeWith runtimeValues
, showDefaultWith $ const "stackify"
, showDefaultWith $ const "higher-order-cps"
, metavar "RUNTIME"
]
sourceLanguage <- option languageReader . fold $
@@ -77,6 +81,14 @@ parser = do
, showDefaultWith $ const "scheme"
, metavar "LANGUAGE"
]
targetLanguage <- option languageReader . fold $
[ long "target"
, short 'T'
, value LanguageCPS
, completeWith languageValues
, showDefaultWith $ const "cps"
, metavar "LANGUAGE"
]
output <- strOption . fold $
[ long "output"
, short 'o'
+6 -1
View File
@@ -17,9 +17,12 @@ module Gyehoek.Prelude
, NonEmpty((:|))
, Natural
, (>>>)
, (>=>)
, (<=<)
, wrappedIso
) where
import Control.Lens
import Control.Lens hiding (List, (:<))
import Data.List (List)
import Data.Text (Text)
import Effectful
@@ -37,4 +40,6 @@ import Data.Hashable (Hashable)
import Data.List.NonEmpty (NonEmpty((:|)))
import Numeric.Natural (Natural)
import Control.Category ((>>>))
import Control.Monad
import Data.Generics.Wrapped (Wrapped(..))
+536
View File
@@ -0,0 +1,536 @@
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeFamilies #-}
module Gyehoek.Scheme.Expand
(
) where
import Gyehoek.Sexp.Syntax
import Gyehoek.Sexp qualified as S
import Gyehoek.Prelude
import Gyehoek.Scheme.Syntax hiding (Prim(..))
import Gyehoek.Scheme.Syntax qualified as Scm
import qualified Data.HashSet as HS
import qualified Data.HashMap.Strict as H
import Data.Foldable
import Data.These
import Data.Zip
import Prelude hiding (filter, zip, mapMaybe)
import qualified Data.List.NonEmpty as NE
import Data.Monoid (Ap(Ap, getAp))
import Gyehoek.Sexp.Grammar.Base ((:-)(..))
import Control.Lens.Extras (is)
import Control.Applicative (Alternative(..))
import Gyehoek.GenSym
import Data.HashSet.Lens (setOf)
import Data.List (sort)
import GHC.Exts (IsList(..))
import Gyehoek.Jalmot
import Data.Maybe (isNothing)
import Data.Kind (Type)
import Data.Traversable (for, mapAccumR)
import Effectful.State.Dynamic
import Witherable
import Data.Semigroup (Arg(..))
import Data.Ord (Down(..))
import qualified Data.Scientific as Sci
data Bind
= BindLexical { symbol :: Name, identity :: Natural }
| BindGlobal { symbol :: Name }
deriving stock (Generic, Eq, Show)
deriving anyclass (Hashable)
newtype Scope = MkScope { identity :: Natural }
deriving stock (Show, Generic, Eq, Ord)
deriving anyclass (Hashable)
deriving newtype (Gen)
type ScopeSet = HashSet Scope
data Formals
= FormalsFixed (List Name)
| FormalsRest (List Name) Name
deriving (Show, Generic)
data PrimLambda e = MkPrimLambda Formals (List e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data PrimLet e = MkPrimLet (Maybe Name) (List (Name, e)) (List e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data PrimIf e = MkPrimIf e e (Maybe e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data PrimLetSyntax e = MkPrimLetSyntax (List (Name, e)) (List e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data Prim e
= PrimLambda (PrimLambda e)
| PrimLet (PrimLet e)
| PrimIf (PrimIf e)
| PrimLetSyntax (PrimLetSyntax e)
| PrimSyntaxRules Trans
deriving (Show, Generic)
data Key = MkKey Name (HashSet Scope)
deriving stock (Show, Generic, Eq)
deriving anyclass (Hashable)
instance S.DatumIso Scope where
datumIso = S.with (S.datumIso >>>)
hashSetGrammar
:: forall a. (Hashable a, Ord a)
=> S.DatumGrammar a -> S.DatumGrammar (HashSet a)
hashSetGrammar g =
S.list (S.rest g)
>>> S.iso HS.fromList (sort . HS.toList)
instance S.DatumIso Key where
datumIso = S.with \g ->
S.list
( S.el (S.sym "@")
>>> S.el (S.datumIso @Name)
>>> S.el (hashSetGrammar S.datumIso)
)
>>> g
instance S.DatumIso Formals where
datumIso = S.match
$ S.With (fixed >>>)
$ S.With (rest >>>)
$ S.End
where
fixed :: S.G (Datum :- t) (List Name :- t)
fixed = S.list $ S.rest $ S.datumIso @Name
rest :: S.G (Datum :- t) (Name :- List Name :- t)
rest = S.coproduct
[ S.dottedList (S.rest $ S.datumIso @Name) (S.datumIso @Name)
, S.datumIso @Name >>> S.onTail (S.push [] null (const mempty))
]
optEl
:: S.G (S.Datum :- t) (a :- t)
-> S.G (S.ListContext :- t) (S.ListContext :- Maybe a :- t)
optEl g =
S.coproduct
[ S.el g >>> S.onTail (S.partialIso Just \case
Nothing -> Left mempty
Just x -> Right x)
, S.onTail $ S.push Nothing isNothing (const mempty)
]
anykw :: Text -> S.G (Datum :- t) t
anykw s = S.Flip $ S.PartialIso
(\t -> adorn SynBuiltin (Symbol s) :- t)
\case
(Symbol _ :- t) -> Right t
_ -> Left $ S.expected "symbol"
prim_if :: S.DatumIso e => S.DatumGrammar (PrimIf e)
prim_if = S.with \g ->
S.listWithStyle (StyleSyntax 1)
(S.el (anykw "if")
>>> S.el S.datumIso
>>> S.el S.datumIso
>>> optEl S.datumIso)
>>> g
prim_lambda :: S.DatumIso e => S.DatumGrammar (PrimLambda e)
prim_lambda = S.with \g ->
S.lambdaLike (anykw "λ") (S.datumIso @Formals) (S.rest S.datumIso)
>>> g
prim_let
:: forall e. S.DatumIso e => S.DatumGrammar (PrimLet e)
prim_let = S.with \g ->
(S.listWithStyle (StyleSyntax 1) $
S.el (anykw "let")
>>> optEl (S.datumIso @Name)
>>> S.el (S.list $ S.rest $ S.datumIso @(Name,e))
>>> S.rest S.datumIso)
>>> g
prim_let_syntax :: S.DatumIso e => S.DatumGrammar (PrimLetSyntax e)
prim_let_syntax = S.with \g ->
(S.listWithStyle (StyleSyntax 1) $
S.el (anykw "let-syntax")
>>> S.el (S.list $ S.rest $ S.datumIso)
>>> S.rest S.datumIso)
>>> g
instance S.DatumIso Bind where
datumIso = S.match
$ S.With (\g ->
S.list (S.el (S.sym "L") >>> S.el S.datumIso >>> S.el S.datumIso)
>>> g)
$ S.With (\g ->
S.list (S.el (S.sym "G") >>> S.el S.datumIso)
>>> g)
$ S.End
{- |
* Examples
>>> :set -XTemplateHaskellQuotes
>>> pat = S.makeSx [|| S.fromDatumUnsafe @Pat S.datumIso ||]
>>> match [] [pat|(x . y)|] [S.sx|(1 2 . 2)|]
Nothing
>>> match [] [pat|(x . y)|] [S.sx|(1 . 2)|]
Just
...
>>> match ["=>"] [pat|(x => y)|] [S.sx|(1 => 2)|]
Just
...
>>> match ["=>"] [pat|(x => y)|] [S.sx|(1 -> 2)|]
Nothing
-}
match :: Foldable f
=> f Text
-- ^ Literal keywords
-> Pat
-> Datum
-> Maybe (HashMap Name Datum)
match kws p = getAp . match' p where
match' :: Pat -> Datum -> Ap Maybe (HashMap Name Datum)
match' PatWildcard _ = pure mempty
match' (PatVar x) e
| coerce x `elem` kws = case e of
Symbol x' | coerce x == x' -> pure mempty
_ -> empty
| otherwise = pure $ H.singleton x e
match' (PatList ps Nothing Nothing) (List es) = matches ps es
match' (PatList ps (Just []) Nothing) (List es) = do
let (ps',p) = ps ^?! _Snoc
(r,rest) <- fold $ alignWith f ps' es
rest' <- rest
& fmap (fmap (fmap (:[])) . match' p)
& foldr (liftA2 $ H.unionWith (<>)) mempty
& fmap (fmap List)
pure $ r <> rest'
where
f (These a b) = (,[]) <$> match' a b
f (This a) = empty
f (That b) = pure (mempty,[b])
match' (PatList ps Nothing (Just p)) (DotList es e) =
matches (p:|ps) (NE.cons e es)
match' _ _ = _
matchPrefix
:: (Semialign f, Foldable f)
=> f Pat -> f Datum -> Ap Maybe (HashMap Name Datum, List Datum)
matchPrefix ps es = fold $ alignWith f ps es
where
f (These a b) = (,[]) <$> match' a b
f (This a) = empty
f (That b) = pure (mempty,[b])
matches
:: (Semialign f, Foldable f)
=> f Pat -> f Datum -> Ap Maybe (HashMap Name Datum)
matches ps es = fold $ alignWith matchThese ps es
matchThese (These a b) = match' a b
matchThese _ = empty
-- | this hashmap is "curried" since we often want to traverse the
-- entire set of scopes associated with a given symbol.
newtype SymTable = MkSymTable
{ curried :: HashMap Name (HashMap (HashSet Scope) Bind) }
deriving (Show, Generic)
type instance Index SymTable = Name
type instance IxValue SymTable = HashMap (HashSet Scope) Bind
instance Ixed SymTable where ix j = #curried . ix j
instance At SymTable where at j = #curried . at j
instance Semigroup SymTable where
MkSymTable c1 <> MkSymTable c2 = MkSymTable $ H.unionWith (<>) c1 c2
instance Monoid SymTable where mempty = MkSymTable mempty
type Expand es = (State SymTable :> es, GenSym :> es)
runExpand :: SymTable -> Eff (State SymTable : GenSym : es) a -> Eff es (a, SymTable)
runExpand tab = runGenSym . runStateLocal tab
testExpand :: Datum -> IO (CommandOrDef, SymTable)
testExpand = runJalmotIO . runExpand (symTableOfEnv env_scheme_base)
. expand env_scheme_base mempty
-- | denotations
data Denot
= DenotVar
| DenotMacro Trans
| DenotPrim Name
| DenotSyntax Datum
| DenotKeyword Name
deriving stock (Show, Generic)
newtype Env = MkEnv { names :: HashMap Bind Denot }
deriving stock (Show, Generic)
deriving newtype (Semigroup, Monoid)
-- | @(environment '(scheme base))@
env_scheme_base :: Env
env_scheme_base = MkEnv . fromList . fold $
[ [ (BindGlobal p, DenotPrim p) | p <- prims ]
, [ (BindGlobal "λ", DenotPrim "lambda")
]
]
where
prims =
[ "if"
, "lambda"
, "let"
, "let-syntax"
]
symTableOfEnv :: Env -> SymTable
symTableOfEnv = ifoldMapOf (#names . itraversed) \cases
b@(BindGlobal n) d -> binding n mempty b
type instance IxValue Env = Denot
type instance Index Env = Bind
instance Ixed Env where ix j = #names . ix j
instance At Env where at j = #names . at j
err :: Jalmot :> es => Text -> Eff es a
err = throwError . EvalError
run :: S.DatumGrammar a -> Datum -> Maybe a
run g = preview #_Right . runPureEff . runJalmot . S.fromDatum g
parsePrim :: S.DatumIso e => Name -> Datum -> Maybe (Prim e)
parsePrim primname d = case primname of
"let" -> PrimLet <$> run prim_let d
"let-syntax" -> PrimLetSyntax <$> run prim_let_syntax d
"lambda" -> PrimLambda <$> run prim_lambda d
"if" -> PrimIf <$> run prim_if d
emit :: (Monoid m, State m :> es) => m -> Eff es ()
emit x = modify (<> x)
binding :: Name -> HashSet Scope -> Bind -> SymTable
binding sym scopes = MkSymTable . H.singleton sym . H.singleton scopes
envOfVar :: Bind -> Env
envOfVar = MkEnv . flip H.singleton DenotVar
gensymLexical :: GenSym :> es => Name -> Eff es Bind
gensymLexical symbol = do
identity <- gensym
pure $ BindLexical {symbol,identity}
lookupSymbol
:: forall es. (State SymTable :> es, Jalmot :> es)
=> Env -> HashSet Scope -> Name -> Eff es (Bind, Denot)
lookupSymbol g scopes x = do
ss <- fmap fold . preuse @SymTable @(Eff es) $ ix x
case nearest (MkKey x scopes) (H.keys ss) of
[] -> err [i|심벌 #{x}는 정의되지 않다|]
(_:_:_) -> err [i|심벌 #{x}는 모호하다|]
[s] | Just b <- ss ^? ix s
, Just d <- g ^? ix b -> pure (b,d)
| otherwise -> error "unreachable"
{- | Given a 'Key' and a collection of 'ScopeSet's, filter that
collection down to the largest subsets of the 'Key'\'s scope set.
This is our analogue of the lexical scoping rule which chooses the
"nearest" binding of a variable when shadowing occurs.
- If no qualifying subsets are found, the name is not in scope.
- If one subset is found, we're on the happy path!
- If more than one subset is found, we're on the rarest and
saddest path: the reference is ambiguous.
* Examples
> (let ((x {A} 123))
> (λ (x {A,B})
> (let ((y {A,B,C} 456))
> x {A,B,C})))
>>> :seti -XOverloadedLists
>>> :{
nearest
(MkKey "x" [MkScope 0, MkScope 1, MkScope 2])
[ [MkScope 0]
, [MkScope 0, MkScope 1] ]
:}
-}
nearest :: (Traversable f, Filterable f) => Key -> f ScopeSet -> List ScopeSet
nearest (MkKey symbol scopes) =
mapMaybe (\x ->
if x `HS.isSubsetOf` scopes
then Just . Down $ Arg (length x) x
else Nothing)
-- 나쁨. 안 좋다. 안 좋아하야.
>>> Data.Foldable.toList >>> sort
>>> foldr (\cases
x [] -> [x]
x acc@(y:_) -> case x `compare` y of
LT -> acc
EQ -> x:acc
GT -> [x])
[]
>>> fmap (\(Down (Arg _ x)) -> x)
expand
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Datum -> Eff es CommandOrDef
expand g scopes datum@(List (Symbol s : es)) = do
let s' = MkName s
(_,denot) <- lookupSymbol g scopes s'
case denot of
DenotVar -> Command . ExpApply (ExpVar s')
<$> traverse (expandAsExp g scopes) es
DenotPrim x -> expandPrim g scopes x datum
DenotMacro trans -> expandMacro g scopes trans datum
-- 신기하지 않은 경우들
expand g scopes datum = case datum of
List (x:xs) -> Command <$> (ExpApply <$> go x <*> traverse go xs)
Symbol s -> pure . Command . ExpVar . MkName $ s
Boolean b -> lit $ LitBool b
Number (Sci.floatingOrInteger -> Right n) -> lit $ LitInt n
where
go = expandAsExp g scopes
lit = pure . Command . ExpLit
expandMacro
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Trans -> Datum -> Eff es CommandOrDef
expandMacro g scopes trans datum = _
expandPrim
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Name -> Datum -> Eff es CommandOrDef
expandPrim g scopes primName datum
| Just prim <- parsePrim @Datum primName datum
= case prim of
PrimLetSyntax (MkPrimLetSyntax bs [body]) -> do
scopes' <- flip HS.insert scopes <$> gensym
(rhss,g') <- (_2 %~ (g<>) . fold) . Prelude.unzip <$> for bs \(x,trans) ->
expandAsExp g scopes trans >>= \case
ExpSyntaxRules trans' ->
(trans',) <$> bindLexical scopes' x (DenotMacro trans')
e -> err [i|syntax-rules를 원하는데 이것 받는다: #{e}|]
Command <$> (ExpLetSyntax
(zip (bs ^.. each . _1) rhss)
<$> expandAsExp g' scopes' body
)
PrimLet (MkPrimLet Nothing bs [body]) -> do
scopes' <- flip HS.insert scopes <$> gensym
g' <- (g<>) . fold <$> for (bs ^.. each . _1) \x ->
bindLexical scopes' x DenotVar
Command <$> (ExpLet
<$> traverseOf (each . _2) (expandAsExp g scopes) bs
<*> expandAsExp g scopes' body)
PrimLambda (MkPrimLambda (FormalsFixed xs) [body]) -> do
scopes' <- flip HS.insert scopes <$> gensym
g' <- (g<>) . fold <$> for xs \x -> bindLexical scopes' x DenotVar
Command . ExpLambda xs <$> expandAsExp g' scopes' body
PrimIf (MkPrimIf c t f) ->
Command <$> (ExpIf
<$> expandAsExp g scopes c
<*> expandAsExp g scopes t
<*> traverse (expandAsExp g scopes) f
)
| otherwise = err [i|prim #{primName}에 잘못한 신택스: #{datum}|]
expandAsExp
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Datum -> Eff es Exp
expandAsExp g scopes d = expand g scopes d >>= intoExp
bindLexical :: Expand es => ScopeSet -> Name -> Denot -> Eff es Env
bindLexical scopes symbol denot = do
identity <- gensym
let bind = BindLexical {symbol,identity}
modify (<> binding symbol scopes bind)
pure $ MkEnv (H.singleton bind denot)
intoExp :: Jalmot :> es => CommandOrDef -> Eff es Exp
intoExp (Command e) = pure e
intoExp (Begin es) = traverse intoExp es >>= \case
[] -> err "begin expression은 빔"
(x:xs) -> pure . ExpBegin $ x NE.:| xs
trans_when :: Trans
trans_when = MkTrans
{ ellipsis = "..."
, keywords = []
, rules =
[ MkRule
(PatList
[ PatVar "when"
, PatVar "test"
, PatVar "body" ]
(Just [])
Nothing)
(TemList
[ El $ TemVar "if"
, El $ TemVar "test"
, El $ TemList
[ El $ TemVar "begin"
, Ellipsis $ TemVar "body"
]
Nothing
]
Nothing)
]
}
trans_and :: Trans
trans_and = MkTrans
{ ellipsis = "..."
, keywords = []
, rules =
[ MkRule
(PatList
[PatVar "and"]
Nothing
Nothing)
(TemLit (LitBool True))
, MkRule
(PatList
[PatVar "and", PatVar "x"]
Nothing
Nothing)
(TemVar "x")
, MkRule
(PatList
[PatVar "and", PatVar "x", PatVar "y"]
(Just [])
Nothing)
(TemList
[ El (TemVar "if")
, El (TemVar "x")
, El (TemList
[ El (TemVar "and")
, Ellipsis (TemVar "y")
]
Nothing)
, El . TemLit . LitBool $ False
]
Nothing)
]
}
+536
View File
@@ -0,0 +1,536 @@
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeFamilies #-}
module Gyehoek.Scheme.Expand.Old
(
) where
import Gyehoek.Sexp.Syntax
import Gyehoek.Sexp qualified as S
import Gyehoek.Prelude
import Gyehoek.Scheme.Syntax hiding (Prim(..))
import Gyehoek.Scheme.Syntax qualified as Scm
import qualified Data.HashSet as HS
import qualified Data.HashMap.Strict as H
import Data.Foldable
import Data.These
import Data.Zip
import Prelude hiding (filter, zip, mapMaybe)
import qualified Data.List.NonEmpty as NE
import Data.Monoid (Ap(Ap, getAp))
import Gyehoek.Sexp.Grammar.Base ((:-)(..))
import Control.Lens.Extras (is)
import Control.Applicative (Alternative(..))
import Gyehoek.GenSym
import Data.HashSet.Lens (setOf)
import Data.List (sort)
import GHC.Exts (IsList(..))
import Gyehoek.Jalmot
import Data.Maybe (isNothing)
import Data.Kind (Type)
import Data.Traversable (for, mapAccumR)
import Effectful.State.Dynamic
import Witherable
import Data.Semigroup (Arg(..))
import Data.Ord (Down(..))
import qualified Data.Scientific as Sci
data Bind
= BindLexical { symbol :: Name, identity :: Natural }
| BindGlobal { symbol :: Name }
deriving stock (Generic, Eq, Show)
deriving anyclass (Hashable)
newtype Scope = MkScope { identity :: Natural }
deriving stock (Show, Generic, Eq, Ord)
deriving anyclass (Hashable)
deriving newtype (Gen)
type ScopeSet = HashSet Scope
data Formals
= FormalsFixed (List Name)
| FormalsRest (List Name) Name
deriving (Show, Generic)
data PrimLambda e = MkPrimLambda Formals (List e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data PrimLet e = MkPrimLet (Maybe Name) (List (Name, e)) (List e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data PrimIf e = MkPrimIf e e (Maybe e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data PrimLetSyntax e = MkPrimLetSyntax (List (Name, e)) (List e)
deriving (Show, Generic, Functor, Foldable, Traversable)
data Prim e
= PrimLambda (PrimLambda e)
| PrimLet (PrimLet e)
| PrimIf (PrimIf e)
| PrimLetSyntax (PrimLetSyntax e)
| PrimSyntaxRules Trans
deriving (Show, Generic)
data Key = MkKey Name (HashSet Scope)
deriving stock (Show, Generic, Eq)
deriving anyclass (Hashable)
instance S.DatumIso Scope where
datumIso = S.with (S.datumIso >>>)
hashSetGrammar
:: forall a. (Hashable a, Ord a)
=> S.DatumGrammar a -> S.DatumGrammar (HashSet a)
hashSetGrammar g =
S.list (S.rest g)
>>> S.iso HS.fromList (sort . HS.toList)
instance S.DatumIso Key where
datumIso = S.with \g ->
S.list
( S.el (S.sym "@")
>>> S.el (S.datumIso @Name)
>>> S.el (hashSetGrammar S.datumIso)
)
>>> g
instance S.DatumIso Formals where
datumIso = S.match
$ S.With (fixed >>>)
$ S.With (rest >>>)
$ S.End
where
fixed :: S.G (Datum :- t) (List Name :- t)
fixed = S.list $ S.rest $ S.datumIso @Name
rest :: S.G (Datum :- t) (Name :- List Name :- t)
rest = S.coproduct
[ S.dottedList (S.rest $ S.datumIso @Name) (S.datumIso @Name)
, S.datumIso @Name >>> S.onTail (S.push [] null (const mempty))
]
optEl
:: S.G (S.Datum :- t) (a :- t)
-> S.G (S.ListContext :- t) (S.ListContext :- Maybe a :- t)
optEl g =
S.coproduct
[ S.el g >>> S.onTail (S.partialIso Just \case
Nothing -> Left mempty
Just x -> Right x)
, S.onTail $ S.push Nothing isNothing (const mempty)
]
anykw :: Text -> S.G (Datum :- t) t
anykw s = S.Flip $ S.PartialIso
(\t -> adorn SynBuiltin (Symbol s) :- t)
\case
(Symbol _ :- t) -> Right t
_ -> Left $ S.expected "symbol"
prim_if :: S.DatumIso e => S.DatumGrammar (PrimIf e)
prim_if = S.with \g ->
S.listWithStyle (StyleSyntax 1)
(S.el (anykw "if")
>>> S.el S.datumIso
>>> S.el S.datumIso
>>> optEl S.datumIso)
>>> g
prim_lambda :: S.DatumIso e => S.DatumGrammar (PrimLambda e)
prim_lambda = S.with \g ->
S.lambdaLike (anykw "λ") (S.datumIso @Formals) (S.rest S.datumIso)
>>> g
prim_let
:: forall e. S.DatumIso e => S.DatumGrammar (PrimLet e)
prim_let = S.with \g ->
(S.listWithStyle (StyleSyntax 1) $
S.el (anykw "let")
>>> optEl (S.datumIso @Name)
>>> S.el (S.list $ S.rest $ S.datumIso @(Name,e))
>>> S.rest S.datumIso)
>>> g
prim_let_syntax :: S.DatumIso e => S.DatumGrammar (PrimLetSyntax e)
prim_let_syntax = S.with \g ->
(S.listWithStyle (StyleSyntax 1) $
S.el (anykw "let-syntax")
>>> S.el (S.list $ S.rest $ S.datumIso)
>>> S.rest S.datumIso)
>>> g
instance S.DatumIso Bind where
datumIso = S.match
$ S.With (\g ->
S.list (S.el (S.sym "L") >>> S.el S.datumIso >>> S.el S.datumIso)
>>> g)
$ S.With (\g ->
S.list (S.el (S.sym "G") >>> S.el S.datumIso)
>>> g)
$ S.End
{- |
* Examples
>>> :set -XTemplateHaskellQuotes
>>> pat = S.makeSx [|| S.fromDatumUnsafe @Pat S.datumIso ||]
>>> match [] [pat|(x . y)|] [S.sx|(1 2 . 2)|]
Nothing
>>> match [] [pat|(x . y)|] [S.sx|(1 . 2)|]
Just
...
>>> match ["=>"] [pat|(x => y)|] [S.sx|(1 => 2)|]
Just
...
>>> match ["=>"] [pat|(x => y)|] [S.sx|(1 -> 2)|]
Nothing
-}
match :: Foldable f
=> f Text
-- ^ Literal keywords
-> Pat
-> Datum
-> Maybe (HashMap Name Datum)
match kws p = getAp . match' p where
match' :: Pat -> Datum -> Ap Maybe (HashMap Name Datum)
match' PatWildcard _ = pure mempty
match' (PatVar x) e
| coerce x `elem` kws = case e of
Symbol x' | coerce x == x' -> pure mempty
_ -> empty
| otherwise = pure $ H.singleton x e
match' (PatList ps Nothing Nothing) (List es) = matches ps es
match' (PatList ps (Just []) Nothing) (List es) = do
let (ps',p) = ps ^?! _Snoc
(r,rest) <- fold $ alignWith f ps' es
rest' <- rest
& fmap (fmap (fmap (:[])) . match' p)
& foldr (liftA2 $ H.unionWith (<>)) mempty
& fmap (fmap List)
pure $ r <> rest'
where
f (These a b) = (,[]) <$> match' a b
f (This a) = empty
f (That b) = pure (mempty,[b])
match' (PatList ps Nothing (Just p)) (DotList es e) =
matches (p:|ps) (NE.cons e es)
match' _ _ = _
matchPrefix
:: (Semialign f, Foldable f)
=> f Pat -> f Datum -> Ap Maybe (HashMap Name Datum, List Datum)
matchPrefix ps es = fold $ alignWith f ps es
where
f (These a b) = (,[]) <$> match' a b
f (This a) = empty
f (That b) = pure (mempty,[b])
matches
:: (Semialign f, Foldable f)
=> f Pat -> f Datum -> Ap Maybe (HashMap Name Datum)
matches ps es = fold $ alignWith matchThese ps es
matchThese (These a b) = match' a b
matchThese _ = empty
-- | this hashmap is "curried" since we often want to traverse the
-- entire set of scopes associated with a given symbol.
newtype SymTable = MkSymTable
{ curried :: HashMap Name (HashMap (HashSet Scope) Bind) }
deriving (Show, Generic)
type instance Index SymTable = Name
type instance IxValue SymTable = HashMap (HashSet Scope) Bind
instance Ixed SymTable where ix j = #curried . ix j
instance At SymTable where at j = #curried . at j
instance Semigroup SymTable where
MkSymTable c1 <> MkSymTable c2 = MkSymTable $ H.unionWith (<>) c1 c2
instance Monoid SymTable where mempty = MkSymTable mempty
type Expand es = (State SymTable :> es, GenSym :> es)
runExpand :: SymTable -> Eff (State SymTable : GenSym : es) a -> Eff es (a, SymTable)
runExpand tab = runGenSym . runStateLocal tab
testExpand :: Datum -> IO (CommandOrDef, SymTable)
testExpand = runJalmotIO . runExpand (symTableOfEnv env_scheme_base)
. expand env_scheme_base mempty
-- | denotations
data Denot
= DenotVar
| DenotMacro Trans
| DenotPrim Name
| DenotSyntax Datum
| DenotKeyword Name
deriving stock (Show, Generic)
newtype Env = MkEnv { names :: HashMap Bind Denot }
deriving stock (Show, Generic)
deriving newtype (Semigroup, Monoid)
-- | @(environment '(scheme base))@
env_scheme_base :: Env
env_scheme_base = MkEnv . fromList . fold $
[ [ (BindGlobal p, DenotPrim p) | p <- prims ]
, [ (BindGlobal "λ", DenotPrim "lambda")
]
]
where
prims =
[ "if"
, "lambda"
, "let"
, "let-syntax"
]
symTableOfEnv :: Env -> SymTable
symTableOfEnv = ifoldMapOf (#names . itraversed) \cases
b@(BindGlobal n) d -> binding n mempty b
type instance IxValue Env = Denot
type instance Index Env = Bind
instance Ixed Env where ix j = #names . ix j
instance At Env where at j = #names . at j
err :: Jalmot :> es => Text -> Eff es a
err = throwError . EvalError
run :: S.DatumGrammar a -> Datum -> Maybe a
run g = preview #_Right . runPureEff . runJalmot . S.fromDatum g
parsePrim :: S.DatumIso e => Name -> Datum -> Maybe (Prim e)
parsePrim primname d = case primname of
"let" -> PrimLet <$> run prim_let d
"let-syntax" -> PrimLetSyntax <$> run prim_let_syntax d
"lambda" -> PrimLambda <$> run prim_lambda d
"if" -> PrimIf <$> run prim_if d
emit :: (Monoid m, State m :> es) => m -> Eff es ()
emit x = modify (<> x)
binding :: Name -> HashSet Scope -> Bind -> SymTable
binding sym scopes = MkSymTable . H.singleton sym . H.singleton scopes
envOfVar :: Bind -> Env
envOfVar = MkEnv . flip H.singleton DenotVar
gensymLexical :: GenSym :> es => Name -> Eff es Bind
gensymLexical symbol = do
identity <- gensym
pure $ BindLexical {symbol,identity}
lookupSymbol
:: forall es. (State SymTable :> es, Jalmot :> es)
=> Env -> HashSet Scope -> Name -> Eff es (Bind, Denot)
lookupSymbol g scopes x = do
ss <- fmap fold . preuse @SymTable @(Eff es) $ ix x
case nearest (MkKey x scopes) (H.keys ss) of
[] -> err [i|심벌 #{x}는 정의되지 않다|]
(_:_:_) -> err [i|심벌 #{x}는 모호하다|]
[s] | Just b <- ss ^? ix s
, Just d <- g ^? ix b -> pure (b,d)
| otherwise -> error "unreachable"
{- | Given a 'Key' and a collection of 'ScopeSet's, filter that
collection down to the largest subsets of the 'Key'\'s scope set.
This is our analogue of the lexical scoping rule which chooses the
"nearest" binding of a variable when shadowing occurs.
- If no qualifying subsets are found, the name is not in scope.
- If one subset is found, we're on the happy path!
- If more than one subset is found, we're on the rarest and
saddest path: the reference is ambiguous.
* Examples
> (let ((x {A} 123))
> (λ (x {A,B})
> (let ((y {A,B,C} 456))
> x {A,B,C})))
>>> :seti -XOverloadedLists
>>> :{
nearest
(MkKey "x" [MkScope 0, MkScope 1, MkScope 2])
[ [MkScope 0]
, [MkScope 0, MkScope 1] ]
:}
-}
nearest :: (Traversable f, Filterable f) => Key -> f ScopeSet -> List ScopeSet
nearest (MkKey symbol scopes) =
mapMaybe (\x ->
if x `HS.isSubsetOf` scopes
then Just . Down $ Arg (length x) x
else Nothing)
-- 나쁨. 안 좋다. 안 좋아하야.
>>> Data.Foldable.toList >>> sort
>>> foldr (\cases
x [] -> [x]
x acc@(y:_) -> case x `compare` y of
LT -> acc
EQ -> x:acc
GT -> [x])
[]
>>> fmap (\(Down (Arg _ x)) -> x)
expand
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Datum -> Eff es CommandOrDef
expand g scopes datum@(List (Symbol s : es)) = do
let s' = MkName s
(_,denot) <- lookupSymbol g scopes s'
case denot of
DenotVar -> Command . ExpApply (ExpVar s')
<$> traverse (expandAsExp g scopes) es
DenotPrim x -> expandPrim g scopes x datum
DenotMacro trans -> expandMacro g scopes trans datum
-- 신기하지 않은 경우들
expand g scopes datum = case datum of
List (x:xs) -> Command <$> (ExpApply <$> go x <*> traverse go xs)
Symbol s -> pure . Command . ExpVar . MkName $ s
Boolean b -> lit $ LitBool b
Number (Sci.floatingOrInteger -> Right n) -> lit $ LitInt n
where
go = expandAsExp g scopes
lit = pure . Command . ExpLit
expandMacro
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Trans -> Datum -> Eff es CommandOrDef
expandMacro g scopes trans datum = _
expandPrim
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Name -> Datum -> Eff es CommandOrDef
expandPrim g scopes primName datum
| Just prim <- parsePrim @Datum primName datum
= case prim of
PrimLetSyntax (MkPrimLetSyntax bs [body]) -> do
scopes' <- flip HS.insert scopes <$> gensym
(rhss,g') <- (_2 %~ (g<>) . fold) . Prelude.unzip <$> for bs \(x,trans) ->
expandAsExp g scopes trans >>= \case
ExpSyntaxRules trans' ->
(trans',) <$> bindLexical scopes' x (DenotMacro trans')
e -> err [i|syntax-rules를 원하는데 이것 받는다: #{e}|]
Command <$> (ExpLetSyntax
(zip (bs ^.. each . _1) rhss)
<$> expandAsExp g' scopes' body
)
PrimLet (MkPrimLet Nothing bs [body]) -> do
scopes' <- flip HS.insert scopes <$> gensym
g' <- (g<>) . fold <$> for (bs ^.. each . _1) \x ->
bindLexical scopes' x DenotVar
Command <$> (ExpLet
<$> traverseOf (each . _2) (expandAsExp g scopes) bs
<*> expandAsExp g scopes' body)
PrimLambda (MkPrimLambda (FormalsFixed xs) [body]) -> do
scopes' <- flip HS.insert scopes <$> gensym
g' <- (g<>) . fold <$> for xs \x -> bindLexical scopes' x DenotVar
Command . ExpLambda xs <$> expandAsExp g' scopes' body
PrimIf (MkPrimIf c t f) ->
Command <$> (ExpIf
<$> expandAsExp g scopes c
<*> expandAsExp g scopes t
<*> traverse (expandAsExp g scopes) f
)
| otherwise = err [i|prim #{primName}에 잘못한 신택스: #{datum}|]
expandAsExp
:: (Expand es, Jalmot :> es)
=> Env -> ScopeSet -> Datum -> Eff es Exp
expandAsExp g scopes d = expand g scopes d >>= intoExp
bindLexical :: Expand es => ScopeSet -> Name -> Denot -> Eff es Env
bindLexical scopes symbol denot = do
identity <- gensym
let bind = BindLexical {symbol,identity}
modify (<> binding symbol scopes bind)
pure $ MkEnv (H.singleton bind denot)
intoExp :: Jalmot :> es => CommandOrDef -> Eff es Exp
intoExp (Command e) = pure e
intoExp (Begin es) = traverse intoExp es >>= \case
[] -> err "begin expression은 빔"
(x:xs) -> pure . ExpBegin $ x NE.:| xs
trans_when :: Trans
trans_when = MkTrans
{ ellipsis = "..."
, keywords = []
, rules =
[ MkRule
(PatList
[ PatVar "when"
, PatVar "test"
, PatVar "body" ]
(Just [])
Nothing)
(TemList
[ El $ TemVar "if"
, El $ TemVar "test"
, El $ TemList
[ El $ TemVar "begin"
, Ellipsis $ TemVar "body"
]
Nothing
]
Nothing)
]
}
trans_and :: Trans
trans_and = MkTrans
{ ellipsis = "..."
, keywords = []
, rules =
[ MkRule
(PatList
[PatVar "and"]
Nothing
Nothing)
(TemLit (LitBool True))
, MkRule
(PatList
[PatVar "and", PatVar "x"]
Nothing
Nothing)
(TemVar "x")
, MkRule
(PatList
[PatVar "and", PatVar "x", PatVar "y"]
(Just [])
Nothing)
(TemList
[ El (TemVar "if")
, El (TemVar "x")
, El (TemList
[ El (TemVar "and")
, Ellipsis (TemVar "y")
]
Nothing)
, El . TemLit . LitBool $ False
]
Nothing)
]
}
+389 -159
View File
@@ -10,37 +10,34 @@
{-# LANGUAGE OrPatterns #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE ViewPatterns #-}
{- HLINT ignore "Avoid lambda using `infix`" -}
{- HLINT ignore "Redundant $" -}
module Gyehoek.Scheme.Syntax
( Name(..)
, Prim(..)
, Builtin(..)
, Lit(..)
, Def(..)
, Exp(..)
, ExpF(..)
, Sexp(..)
, Program(..)
, CommandOrDef(..)
, primSexpIso
, pattern Void
, Trans(..)
, Rule(..)
, Pat(..)
, Tem(..)
, El(..)
, builtinDatumIso
, free
, subst
, getName
, scm
, readExp
, readProgram
, free'
, freeWithBound'
, freeO
, encodeProgram
)
where
import Data.List (intersperse)
import Language.SexpGrammar
( SexpIso(..), list, el, rest, sym, symbol )
import Language.SexpGrammar qualified as Sexp
import Language.SexpGrammar.Generic
import Effectful
import Prelude hiding ((.), id)
import Control.Category
import Gyehoek.Sexp qualified as GS
@@ -53,12 +50,12 @@ import Data.Functor.Foldable hiding (fold)
import qualified Data.HashSet as HS
import Data.Foldable (fold, toList)
import Language.Haskell.TH.Quote (QuasiQuoter)
import Effectful.FileSystem (runFileSystem)
import qualified Effectful.FileSystem.IO as FS
import qualified Data.Text.Encoding as T
import qualified Effectful.FileSystem.IO.ByteString as FB
import qualified Data.Set.Ordered as O
import Gyehoek.Sexp.Grammar qualified as S
import Gyehoek.Sexp.Grammar (DatumIso, G, DataIso, (:-)((:-)))
import Gyehoek.Prelude
import Control.Lens.Extras (is)
import qualified Data.Scientific as Sci
newtype Name = MkName { inner :: Text }
@@ -72,65 +69,121 @@ instance Prefixed Name where
getName :: Name -> Text
getName (MkName x) = x
data Prim e
= PrimAdd e e
| PrimSub e e
| PrimMul e e
| PrimDiv e e
| PrimCons e e
| PrimCar e
| PrimCdr e
| PrimImmediateP e
| PrimConsP e
| PrimIntegerP e
| PrimWrite e
| PrimZeroP e
| PrimNewline
| PrimMakeClosure { code :: e, env :: List e }
| PrimEnvRef e Int
| PrimEnvCode e
| PrimCallCC e
data Builtin e
= BuiltinAdd e e
| BuiltinSub e e
| BuiltinMul e e
| BuiltinDiv e e
| BuiltinCons e e
| BuiltinCar e
| BuiltinCdr e
| BuiltinImmediateP e
| BuiltinConsP e
| BuiltinIntegerP e
| BuiltinWrite e
| BuiltinZeroP e
| BuiltinNewline
| BuiltinMakeClosure { code :: e, env :: List e }
| BuiltinMakeSharedClosure { codes :: List e, env :: List e }
| BuiltinGetEnv
| BuiltinEnv
| BuiltinEnvRef Int
| BuiltinCallCC e
| BuiltinCaptureCC
| BuiltinInvokeCC e (List e)
| BuiltinValues (List e)
| BuiltinCallWithValues e e
| BuiltinPairP e
| BuiltinList (List e)
deriving stock (Show, Generic, Functor, Foldable, Traversable, Data, Eq)
deriving anyclass (NFData)
instance Each (Prim e) (Prim e') e e'
instance Each (Builtin e) (Builtin e') e e'
data Lit
= LitInt Int
| LitNil
| LitBool Bool
| LitString Text
| LitQuote Sexp
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
pattern Void :: Lit
pattern Void = LitNil
data Def
= DefConstant Name Exp
| DefProcedure Name (List Name) (List Exp)
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data Formals a
= FormalsFixed (List a)
| FormalsVar (List a) a
deriving stock (Show, Generic, Data, Functor, Foldable, Traversable)
deriving anyclass (NFData)
data Exp
= ExpLet (List (Name, Exp)) Exp
| ExpLetSyntax (List (Name, Trans)) Exp
| ExpLetRec (List (Name, Exp)) Exp
| ExpPrim (Prim Exp)
| ExpBegin (List Exp)
| ExpIf Exp Exp Exp
| ExpBuiltin (Builtin Exp)
| ExpBegin (NonEmpty Exp)
| ExpIf Exp Exp (Maybe Exp)
| ExpLit Lit
| ExpLambda (List Name) Exp
| ExpVar Name
| ExpSyntaxRules Trans
| ExpApply Exp (List Exp)
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data Sexp
= SexpCons Sexp Sexp
| SexpSymbol Text
| SexpLit Lit
deriving stock (Show, Generic, Data, Eq)
data Trans = MkTrans
{ ellipsis :: Name
, keywords :: List Name
, rules :: List Rule
}
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data Rule = MkRule
{ rhs :: Pat
, lhs :: Tem
}
deriving (Show, Generic, Data)
deriving anyclass (NFData)
data Pat
= PatWildcard
| PatVar Name
| PatList
{ init :: List Pat
, ellipsis :: Maybe (List Pat)
, tail :: Maybe Pat
}
-- | PatVec
-- { init :: List Pat
-- , ellipsis :: Maybe (List Pat)
-- }
deriving (Show, Generic, Data)
deriving anyclass (NFData)
data Tem
-- | @(⟨element⟩ …)@
-- @(⟨element⟩ ⟨element⟩ … . ⟨element⟩)@
= TemList
{ init :: List El
, tail :: Maybe Tem
}
-- | @(⟨ellipsis⟩ ⟨template⟩)@
| TemTrail Tem
| TemLit Lit
| TemVar Name
deriving (Show, Generic, Data)
deriving anyclass (NFData)
data El
-- | @⟨template⟩ ⟨ellipsis⟩@
= Ellipsis Tem
-- | @⟨template⟩@
| El Tem
deriving (Show, Generic, Data)
deriving anyclass (NFData)
data CommandOrDef
@@ -140,8 +193,37 @@ data CommandOrDef
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
newtype LibName = MkLibName { inner :: NonEmpty Name }
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data ImportSet
= ImportLib LibName
| ImportOnly ImportSet (NonEmpty Name)
| ImportExcept ImportSet (NonEmpty Name)
| ImportPrefix ImportSet Name
| ImportRename ImportSet (NonEmpty (Name, Name))
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
newtype ImportDecl = MkImportDecl (NonEmpty ImportSet)
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data LibDecl
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data Lib = MkLib
{ name :: LibName
, decls :: List LibDecl
}
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data Program = MkProgram
{ commandsAndDefs :: List CommandOrDef
{ imports :: List ImportDecl
, commandsAndDefs :: List CommandOrDef
}
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
@@ -159,102 +241,274 @@ makeBaseFunctor ''Exp
instance SexpIso Name where
sexpIso = symbol >>> Sexp.partialOsi f g
instance DatumIso Name where
datumIso = S.symbol >>> S.iso coerce coerce
builtinDatumIso
:: (Text -> Text)
-> S.DatumGrammar a -> S.DatumGrammar (Builtin a)
builtinDatumIso namefn a = S.match
$ S.With (. ht2 "+")
$ S.With (. ht2 "-")
$ S.With (. ht2 "*")
$ S.With (. ht2 "/")
$ S.With (. ht2 "cons")
$ S.With (. ht1 "car")
$ S.With (. ht1 "cdr")
$ S.With (. ht1 "immediate?")
$ S.With (. ht1 "cons?")
$ S.With (. ht1 "integer?")
$ S.With (. ht1 "write")
$ S.With (. ht1 "zero?")
$ S.With (. ht0 "newline")
$ S.With (. ht1' "make-closure")
$ S.With (. S.headTagged2 (namefn "make-shared-closure")
(S.list $ S.rest a)
(S.list $ S.rest a))
$ S.With (. ht0 "get-env")
$ S.With (. ht0 "env")
$ S.With (. S.headTagged1 (namefn "env-ref") S.int)
$ S.With (. ht1 "call/cc")
$ S.With (. ht0 "capture/cc")
$ S.With (. ht1' "invoke/cc")
$ S.With (. ht0' "values")
$ S.With (. ht2 "call-with-values")
$ S.With (. ht1 "pair?")
$ S.With (. ht0' "list")
$ S.End
where
f = Right . MkName
g (MkName s) = s
idn = S.el . S.sym . namefn
ht0 s = S.list $ idn s
ht1 s = S.headTagged1 (namefn s) a
ht2 s = S.headTagged2 (namefn s) a a
ht1' s = S.headTagged1' (namefn s) a a
ht0' s = S.headTagged0' (namefn s) a
primSexpIso :: (Text -> Text) -> Sexp.SexpGrammar a -> Sexp.SexpGrammar (Prim a)
primSexpIso namefn a = match
$ With (. ht2 "+")
$ With (. ht2 "-")
$ With (. ht2 "*")
$ With (. ht2 "/")
$ With (. ht2 "cons")
$ With (. ht1 "car")
$ With (. ht1 "cdr")
$ With (. ht1 "immediate?")
$ With (. ht1 "cons?")
$ With (. ht1 "integer?")
$ With (. ht1 "write")
$ With (. ht1 "zero?")
$ With (. nullop "newline")
$ With (. ht1' "make-closure")
$ With (. GS.headTagged2 (namefn "env-ref") a Sexp.int)
$ With (. ht1 "env-code")
$ With (. ht1 "call/cc")
$ End
instance DatumIso a => DatumIso (Builtin a) where
datumIso = builtinDatumIso id S.datumIso
instance DatumIso Lit where
datumIso = S.match
$ S.With (. S.int)
$ S.With (. S.boolean)
$ S.With (. S.string)
$ S.End
instance DatumIso Def where
datumIso = S.match
$ S.With (. defconst)
$ S.With (. defun)
$ S.End
where
idn s = el (sym (namefn s))
nullop s = list $ idn s
ht1 s = GS.headTagged1 (namefn s) a
ht2 s = GS.headTagged2 (namefn s) a a
ht1' s = GS.headTagged1' (namefn s) a a
instance SexpIso a => SexpIso (Prim a) where
-- sexpIso = primSexpIso ("prim:"<>) sexpIso
sexpIso = primSexpIso id sexpIso
instance SexpIso Lit where
sexpIso = match
$ With (. sexpIso)
$ With (. sym "nil")
$ With (. GS.schemeBool)
$ With (. sexpIso)
$ With (. GS.prefixSugar "quote" Sexp.Quote sexpIso)
$ End
instance SexpIso Sexp where
sexpIso = match
$ With (\conss -> conss . GS.todo)
$ With (\s -> s . symbol)
$ With (\lit -> lit . sexpIso)
$ End
instance SexpIso Def where
sexpIso = match
$ With (. defconst)
$ With (. defun)
$ End
where
defconst = list $ el (sym "define") >>> el sexpIso >>> el sexpIso
defun = list $ el (sym "define") >>> el args >>> rest sexpIso
args = list $ el sexpIso >>> rest sexpIso
defconst = S.list $ S.el (S.sym "define")
>>> S.el S.datumIso >>> S.el S.datumIso
defun = S.list $ S.el (S.sym "define")
>>> S.el args >>> S.rest S.datumIso
args = S.list $ S.el S.datumIso >>> S.rest S.datumIso
instance SexpIso Exp where
sexpIso = match
$ With (. GS.let_ "let" sexpIso sexpIso sexpIso)
$ With (. GS.let_ "letrec" sexpIso sexpIso sexpIso)
$ With (. sexpIso)
$ With (\bgn -> bgn . list (el (sym "begin") >>> rest sexpIso))
$ With (. if_)
$ With (. sexpIso)
$ With (. lam)
$ With (. sexpIso)
$ With (\app -> app . list (el sexpIso >>> rest sexpIso))
$ End
where
if_ = list $ el (sym "if") >>> el sexpIso >>> el sexpIso >>> el sexpIso
lam = list
( el GS.lambdaKeyword
>>> el (sexpIso @(List Name))
>>> el sexpIso )
instance DatumIso Trans where
datumIso = S.with \g ->
S.listWithStyle
(S.StyleSyntax 1)
( S.el (S.sym "syntax-rules")
>>> S.Iso (\(ctx:-t) -> ctx:-"...":-t) (\(ctx:-_:-t) -> ctx:-t)
>>> S.el (S.list $ S.rest (S.datumIso @Name))
>>> S.rest (S.datumIso @Rule)
)
>>> g
instance SexpIso CommandOrDef where
sexpIso = match
$ With (\_Command -> _Command . sexpIso)
$ With (\_Definition -> _Definition . sexpIso)
$ With (\_Begin -> _Begin . bgn)
$ End
instance DatumIso Exp where
datumIso = S.match
$ S.With (. S.letLike "let" S.datumIso S.datumIso S.datumIso)
$ S.With (letsyntax >>>)
$ S.With (. S.letLike "letrec" S.datumIso S.datumIso S.datumIso)
$ S.With (. S.datumIso)
$ S.With (. begin)
$ S.With (. if_)
$ S.With (. S.datumIso)
$ S.With (. lam)
$ S.With (. S.datumIso)
$ S.With (. S.datumIso)
$ S.With (\app -> app . S.list (S.el S.datumIso >>> S.rest S.datumIso))
$ S.End
where
bgn = list $ el (sym "begin") >>> rest sexpIso
letsyntax = S.listWithStyle (S.StyleSyntax 1) $
S.el (S.sym "let-syntax")
>>> S.el (S.list $ S.rest $ S.datumIso)
>>> S.el S.datumIso
lam = S.lambdaLike S.lambdaKeyword S.datumIso (S.el S.datumIso)
if_ = S.ifLike "if" S.datumIso S.datumIso $ S.datumIso @Exp >>> S.iso
Just
\case
Just x -> x
Nothing -> error "안 괜찮다ㅠㅠ"
begin :: forall t. G (S.Datum :- t) (NonEmpty Exp :- t)
begin = S.beginLike "begin" $
S.el (S.datumIso @Exp) >>> S.rest (S.datumIso @Exp)
>>> S.onTail (S.Iso
(\(xs:-x:-t) -> (x:|xs):-t)
(\((x:|xs):-t) -> xs:-x:-t))
instance S.DatumIso Rule where
datumIso = S.with \g ->
S.list (S.el S.datumIso >>> S.el S.datumIso) >>> g
instance S.DatumIso Pat where
datumIso = S.match
$ S.With (S.sym "_" >>>)
$ S.With (S.datumIso >>>)
$ S.With (lst >>>)
$ S.End
where
lst :: S.G (S.Datum :- t) (Maybe Pat :- Maybe (List Pat) :- List Pat :- t)
lst = S.coproduct
[ S.list $
ellipsis
>>> S.onTail (S.push Nothing (is _Nothing) (const mempty))
, S.dottedList
ellipsis
( S.datumIso @Pat
>>> S.partialIso Just (maybe (Left mempty) Right) )
]
ellipsis =
S.restData split
>>> S.onTail
( S.onHead (S.traversed . S.traversed . S.sealed $
S.datumIso @Pat)
>>> S.onTail (S.onHead . S.traversed . S.sealed $
S.datumIso @Pat)
)
split
:: forall t. S.G (List S.Datum :- t)
(Maybe (List S.Datum) :- List S.Datum :- t)
split = S.Iso
(\(ps0:-t) ->
let (ps,ell) = splitEllipsis ps0
in ell :- ps :- t)
(\(ell:-ps:-t) -> (ps ++ foldMap ([S.Symbol "..."]++) ell) :- t)
splitEllipsis :: List S.Datum -> (List S.Datum, Maybe (List S.Datum))
splitEllipsis [] = ([], Nothing)
splitEllipsis (S.Symbol "..." : xs) = ([], Just xs)
splitEllipsis (x:xs) = splitEllipsis xs & _1 %~ (x:)
instance S.DataIso El where
dataIso = S.match
$ S.With (ellipsis >>>)
$ S.With (noellipsis >>>)
$ S.End
where
ellipsis = S.recontextualise $
S.el (S.datumIso @Tem) >>> S.el (S.sym "...")
noellipsis = S.recontextualise $ S.el (S.datumIso @Tem)
instance S.DatumIso Tem where
datumIso = S.match
$ S.With (lst >>>)
$ S.With (trail >>>)
$ S.With (S.datumIso @Lit >>>)
$ S.With (S.datumIso @Name >>>)
$ S.End
where
trail = S.list $ S.el (S.sym "...") >>> S.el (S.datumIso @Tem)
lst :: S.G (S.Datum :- t) (Maybe Tem :- List El :- t)
lst = S.coproduct
[ S.list els
>>> (S.push Nothing (is _Nothing) (const mempty))
, S.dottedList els $
S.datumIso @Tem
>>> S.partialIso Just (maybe (Left mempty) Right)
]
els :: S.G (S.ListContext :- t) (S.ListContext :- List El :- t)
els =
S.iso
(\(S.MkListContext ds) -> affixEllipses ds)
(S.MkListContext . foldMap \(d,b) ->
d : if b then [S.Symbol "..."] else [])
>>> S.onHead (S.traversed . S.sealed $
S.flipped S.pair
>>> S.onTail (S.datumIso @Tem)
>>> S.pair
>>> S.iso
(\(t,b) -> if b then Ellipsis t else El t)
(\case
Ellipsis t -> (t,True)
El t -> (t,False)))
>>> S.push (S.MkListContext [])
(\(S.MkListContext xs) -> null xs)
(const mempty)
affixEllipses :: List S.Datum -> List (S.Datum, Bool)
affixEllipses (x : S.Symbol "..." : xs) = (x,True) : affixEllipses xs
affixEllipses (x : xs) = (x,False) : affixEllipses xs
affixEllipses [] = []
instance DatumIso CommandOrDef where
datumIso = S.match
$ S.With (\_Command -> _Command . S.datumIso)
$ S.With (\_Definition -> _Definition . S.datumIso)
$ S.With (\_Begin -> _Begin . S.beginLike "begin" (S.rest S.datumIso))
$ S.End
instance DatumIso LibName where
datumIso = S.with \g ->
S.list (S.restData $ S.nonEmptyData comp)
>>> g
where
comp = S.partialOsi
(\case
S.Symbol s -> Right $ MkName s
S.Number (Sci.floatingOrInteger @Double @Int -> Right n)
| n > 0 -> Right $ MkName [i|#{n}|]
_ -> Left $ S.expected "library name part"
)
\(MkName s) -> S.Symbol s
instance DatumIso ImportSet where
datumIso = S.match
$ S.With (S.datumIso @LibName >>>)
$ S.With (imp "only" >>>)
$ S.With (imp "except" >>>)
$ S.With (imp' "prefix" >>>)
$ S.With (imp "rename" >>>)
$ S.End
where
imp s = S.list $ S.el (S.sym s)
>>> S.el S.datumIso >>> S.restData S.dataIso
imp' s = S.list $
S.el (S.sym s)
>>> S.el S.datumIso
>>> S.el S.datumIso
instance DatumIso ImportDecl where
datumIso = S.with \decl ->
S.list (S.el (S.sym "import") >>> S.restData S.dataIso)
>>> decl
instance DataIso Program where
dataIso = S.with \g ->
splitG
>>> S.onHead (S.sealed S.dataIso)
>>> S.onTail (S.onHead . S.sealed $ S.dataIso)
>>> g
where
isImport = \case
S.List (S.Symbol "import" : _) -> True
_ -> False
splitG :: G (List S.Datum :- t) (List S.Datum :- List S.Datum :- t)
splitG = S.Iso
(\(xs:-t) ->
let (ys,zs) = span isImport xs
in zs :- ys :- t
)
\(zs:-ys:-t) -> (ys ++ zs) :- t
-- utilities
scm :: QuasiQuoter
scm = GS.makeSx [|| GS.fromSexp @Exp ||]
scm = GS.makeSx [|| S.fromDatumUnsafe @Exp S.datumIso ||]
freeWithBound' :: Foldable f => f Name -> Exp -> List Name
freeWithBound' bound = filter (`elem` bound) . free'
@@ -309,27 +563,3 @@ subst f = \e -> cata go e mempty where
go (ExpLetF _ _) _ = error "todo lol"
go (ExpLambdaF bs e) bound = e $ insertFrom bs bound
go e bound = embed $ fmap ($ bound) e
fileName :: FilePath -> FilePath
fileName "-" = "<interactive>"
fileName e = e
hGetContents :: FS.FileSystem :> es => FS.Handle -> Eff es Text
hGetContents h = T.decodeUtf8 <$> FB.hGetContents h
readProgram :: IOE :> es => FilePath -> Eff es Program
readProgram fp = runFileSystem $
FS.withFile fp FS.ReadMode $ \h ->
GS.parseSexps @CommandOrDef (fileName fp) <$> hGetContents h
>>= either error (pure . MkProgram)
readExp :: IOE :> es => FilePath -> Eff es Exp
readExp fp = readProgram fp <&> (^?! #commandsAndDefs . _head . #Command)
encodeProgram :: Program -> Text
encodeProgram p = p.commandsAndDefs
& fmap ((^?! _Right) . GS.encodePretty)
& intersperse "\n\n"
& mconcat
+6 -448
View File
@@ -1,453 +1,11 @@
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE OrPatterns #-}
module Gyehoek.Sexp
( let_
, sexp
, nonempty
, nonEmptyGrammar
, encode
, decode
, parseSexps
, prefixSugar
, todo
, isoIso
, encodeWith
, decodeWith
, kappa
, lambda
, kappaKeyword
, lambdaKeyword
, encodePrettyWith
, encodePretty
, SpliceSexp(..)
, Position(..)
, parseSexpsWithPos
, parseSexpWithPos
, parseSexp
, sx
, sxs
, makeSx
, makeSxs
, makeSx'
, toSexp
, fromSexp
, fromSexp'
, stripLocation
, format
, equivalent
, encodeOrShow
, readSxs
, prismIso
, schemeBool
, headTagged1'
, headTagged1
, headTagged2
( module Gyehoek.Sexp.QQ
, module Gyehoek.Sexp.Syntax
, module Gyehoek.Sexp.Grammar
)
where
import Data.Text (Text)
import Language.SexpGrammar as Sexp hiding (toSexp, List, encode, decode, encodeWith, decodeWith, iso, encodePrettyWith, encodePretty, fromSexp)
import Language.SexpGrammar qualified as Sexp
import Language.Sexp qualified as S
import Data.InvertibleGrammar.Base qualified as IGB
import Data.InvertibleGrammar qualified as IG
import Data.InvertibleGrammar.Base ((:-)((:-)))
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.List (List, groupBy)
import Data.Text.Encoding
import GHC.Generics (Generic)
import Control.Lens hiding (para)
import Control.Monad (join)
import qualified Language.Sexp.Located as SL
import Data.Void (absurd)
import Language.Haskell.TH.Quote
import Language.Haskell.TH (Quote, location, Loc (..), ExpQ, varE, mkName, listE, Exp, appE, Q, Code, unTypeCode)
import qualified Data.Text as T
import qualified Control.Category
import Data.Data (Data (..), Typeable, cast)
import Language.Haskell.TH.Syntax (lift, Lift, liftData)
import Data.Functor.Foldable (cata)
import Data.Vector (Vector)
import Numeric.Natural (Natural)
import qualified Data.Vector.Strict
import Data.Function (on)
import Data.String (IsString (fromString))
import Effectful
import qualified Effectful.FileSystem.IO as FS
import qualified Effectful.FileSystem.IO.ByteString as FB
import qualified Data.Text.Encoding as T
import Gyehoek.Sexp.QQ
import Gyehoek.Sexp.Syntax
import Gyehoek.Sexp.Grammar
sexp :: SexpIso a => Iso' a Text
sexp = iso
(either error id . encode)
(either error id . decode)
format :: Sexp -> Text
format = decodeUtf8 . view strict . SL.format
encode :: SexpIso a => a -> Either String Text
encode = encodeWith sexpIso
decode :: SexpIso a => Text -> Either String a
decode = decodeWith sexpIso
encodeWith :: SexpGrammar a -> a -> Either String Text
encodeWith g = (_Right %~ decodeUtf8 . view strict) . Sexp.encodeWith g
encodePretty :: SexpIso a => a -> Either String Text
encodePretty = encodePrettyWith sexpIso
decodeWith :: SexpGrammar a -> Text -> Either String a
decodeWith g = Sexp.decodeWith g "FILE" . view lazy . encodeUtf8
encodePrettyWith :: SexpGrammar a -> a -> Either String Text
encodePrettyWith g =
(_Right %~ decodeUtf8 . view strict) . Sexp.encodePrettyWith g
parseSexps :: SexpIso a => FilePath -> Text -> Either String (List a)
parseSexps f = marshal . SL.parseSexps f . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp sexpIso)
parseSexpsWith :: SexpGrammar a -> FilePath -> Text -> Either String (List a)
parseSexpsWith g f = marshal . SL.parseSexps f . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp g)
parseSexp :: SexpIso a => FilePath -> Text -> Either String a
parseSexp f = marshal . SL.parseSexp f . view lazy . encodeUtf8
where marshal = join . traverseOf _Right (Sexp.fromSexp sexpIso)
readSexpWithPos :: Position -> Text -> Either String Sexp
readSexpWithPos pos = SL.parseSexpWithPos pos . view lazy . encodeUtf8
readSexpsWithPos :: Position -> Text -> Either String (List Sexp)
readSexpsWithPos pos = SL.parseSexpsWithPos pos . view lazy . encodeUtf8
parseSexpsWithPos :: SexpGrammar a -> Position -> Text -> Either String (List a)
parseSexpsWithPos g pos =
marshal . SL.parseSexpsWithPos pos . view lazy . encodeUtf8
where marshal = join . traverseOf (_Right . each) (Sexp.fromSexp g)
parseSexpWithPos :: SexpGrammar a -> Position -> Text -> Either String a
parseSexpWithPos g pos =
marshal . SL.parseSexpWithPos pos . view lazy . encodeUtf8
where marshal = join . traverseOf _Right (Sexp.fromSexp g)
fileName :: FilePath -> FilePath
fileName "-" = "<interactive>"
fileName e = e
hGetContents :: FS.FileSystem :> es => FS.Handle -> Eff es Text
hGetContents h = T.decodeUtf8 <$> FB.hGetContents h
readSxs
:: IOE :> es
=> SexpGrammar a
-> FilePath -> Eff es (List a)
readSxs g fp = FS.runFileSystem $
FS.withFile fp FS.ReadMode $ \h ->
parseSexpsWith g (fileName fp) <$> hGetContents h
>>= either error pure
nonEmptyGrammar :: Grammar p (NonEmpty x :- t) (List x :- x :- t)
nonEmptyGrammar = IGB.Iso
(\((x:|xs) :- t) -> reverse xs :- x :- t)
(\(xs :- x :- t) -> (x :| reverse xs) :- t)
nonempty :: SexpGrammar a -> SexpGrammar (NonEmpty a)
nonempty a =
list (el a >>> rest a) >>>
IG.flipped nonEmptyGrammar
let_
:: Text
-> (forall t. Grammar Position (Sexp :- t) (a :- t))
-> (forall t. Grammar Position (Sexp :- t) (b :- t))
-> Grammar Position (Sexp :- (List (a, b) :- t1)) t2
-> Grammar Position (Sexp :- t1) t2
let_ kw name rhs e = list (el (sym kw) >>> el bindings >>> el e)
where
-- bindings :: Grammar Position (Sexp :- _) (List (_, _) :- _)
bindings = list $ rest binding
binding :: Grammar Position (Sexp :- t) ((_, _) :- t)
binding = list (el name >>> el rhs) >>> pair
data DotList a = MkDotList (NonEmpty a) a
deriving (Show, Generic)
-- | Define a sexp representation as either (⟨name⟩ ⟨e⟩) or '⟨e⟩.
prefixSugar
:: Text -> Prefix
-> Grammar Position (Sexp :- t') a
-> Grammar Position (Sexp :- t') a
prefixSugar name prefix e = coproduct
-- 'something
[ Sexp.prefixed prefix e
-- (quote something)
, list $ el (sym name) >>> el e
]
todo :: Grammar p (Sexp :- t) t'
todo = IGB.Flip (IGB.PartialIso absurd f) >>> IGB.PartialIso absurd g
where
f _ = Left $ unexpected "todo"
g _ = Left $ unexpected "todo"
kappa
:: (forall t. Grammar Position (Sexp :- t) (a :- t))
-> Grammar Position (Sexp :- List a :- t1) t2
-> Grammar Position (Sexp :- t1) t2
kappa name e = list $
el kappaKeyword
>>> el (list $ rest name)
>>> el e
lambda
:: (forall t. Grammar Position (Sexp :- t) (a :- t))
-> Grammar Position (Sexp :- List a :- t1) t2
-> Grammar Position (Sexp :- t1) t2
lambda name e = list $
el lambdaKeyword
>>> el (list $ rest name)
>>> el e
isoIso :: Iso' s a -> Grammar p (s :- t) (a :- t)
isoIso l = Sexp.iso (view l) (review l)
prismIso :: Mismatch -> Prism' s a -> Grammar p (s :- t) (a :- t)
prismIso mm p = Sexp.partialOsi
(maybe (Left mm) Right . preview p)
(review p)
kappaKeyword :: Grammar Position (Sexp :- t) t
kappaKeyword = coproduct [ sym "κ", sym "kappa" ]
lambdaKeyword :: Grammar Position (Sexp :- t) t
lambdaKeyword = coproduct [ sym "λ", sym "lambda" ]
schemeBool :: SexpGrammar Bool
schemeBool = Sexp.hashed $ Sexp.partialOsi f g
where
f (SL.Symbol ("t";"true")) = Right True
f (SL.Symbol ("f";"false")) = Right False
f _ = Left $ Sexp.expected "bool"
g True = SL.Symbol "true"
g False = SL.Symbol "false"
headTagged1 :: Text -> SexpGrammar a -> Grammar Position (Sexp :- t) (a :- t)
headTagged1 s g1 = list $ el (sym s) >>> el g1
headTagged1'
:: Text
-> SexpGrammar a -> SexpGrammar b
-> Grammar Position (Sexp :- t) (List b :- a :- t)
headTagged1' s g1 gt = list $ el (sym s) >>> el g1 >>> rest gt
headTagged2
:: Text
-> SexpGrammar a -> SexpGrammar b
-> Grammar Position (Sexp :- t) (b :- a :- t)
headTagged2 s g1 g2 = list $ el (sym s) >>> el g1 >>> el g2
instance SexpIso Sexp where
sexpIso = Control.Category.id
-- evil ass orphan instances
deriving instance (Data a, Data e) => Data (SL.LocatedBy a e)
deriving instance Data SL.Atom
deriving instance Data SL.Prefix
deriving instance Data SL.Position
deriving instance (Data e) => Data (SL.SexpF e)
-- Quasiquoter
getPos = do
Loc {loc_filename,loc_start} <- location
pure $ SL.Position loc_filename (fst loc_start) (snd loc_start)
fromSexp :: SexpIso a => Sexp -> a
fromSexp = either error id . Sexp.fromSexp sexpIso
fromSexp' :: SexpGrammar a -> Sexp -> a
fromSexp' g = either error id . Sexp.fromSexp g
toSexp :: SexpIso a => a -> Sexp
toSexp = either error id . Sexp.toSexp sexpIso
toSexps :: (Foldable f, SexpIso a) => f a -> List Sexp
toSexps = foldMap \x -> [toSexp x]
pattern Unquote :: Text -> Sexp
pattern Unquote x =
SL.Modified Hash (SL.BraceList [SL.Symbol x])
pattern UnquoteSplicing :: Text -> Sexp
pattern UnquoteSplicing x =
SL.Modified Hash (SL.Modified Hash (SL.BraceList [SL.Symbol x]))
_UnquoteSplicing :: Prism' Sexp.Sexp Text
_UnquoteSplicing = prism'
UnquoteSplicing
(\case { UnquoteSplicing x -> Just x ; _ -> Nothing })
instance Each Sexp Sexp Sexp Sexp where
each k (SL.ParenList xs) = SL.ParenList <$> traverse k xs
each k (SL.BracketList xs) = SL.BracketList <$> traverse k xs
each k (SL.BraceList xs) = SL.BraceList <$> traverse k xs
-- each k (SL.Modified m e) = SL.Modified m <$> each k e
each _ e@(SL.Atom _; SL.Modified _ _) = pure e
stripLocation :: Sexp -> Sexp
stripLocation = cata \case
SL.Compose (a SL.:< e) ->
SL.Fix . SL.Compose $ SL.dummyPos SL.:< e
-- | @('==')@ for 'Sexp's modulo source location — return true if the
-- two sexps are equal in all but 'Position' fields.
equivalent :: Sexp -> Sexp -> Bool
equivalent = (==) `on` stripLocation
instance SexpIso Natural where
sexpIso = Sexp.integer >>> Sexp.partialOsi f g
where
f n | n < 0 = Left $ Sexp.unexpected "negative"
<> Sexp.expected "natural"
| otherwise = Right $ fromIntegral n
g n = fromIntegral n
class SpliceSexp a where
spliceSexp :: a -> List Sexp
instance SexpIso a => SpliceSexp (Data.Vector.Strict.Vector a) where
spliceSexp = toSexps
instance SexpIso a => SpliceSexp (Vector a) where
spliceSexp = toSexps
instance SexpIso a => SpliceSexp (List a) where
spliceSexp = toSexps
instance SpliceSexp Sexp where
spliceSexp = toListOf each
unquoteSplicingRecursive :: List Sexp.Sexp -> ExpQ
unquoteSplicingRecursive xs = [| mconcat $(spans) |]
where
spans = xs
& groupBy \cases
(UnquoteSplicing _) _ -> False
_ (UnquoteSplicing _) -> False
_ _ -> True
& fmap \case
[UnquoteSplicing x] ->
[| spliceSexp $(varE (mkName (T.unpack x))) |]
es -> listE $ unquoteRecursive <$> es
& listE
unquoteRecursive :: Sexp.Sexp -> ExpQ
unquoteRecursive = \case
Unquote x -> [| toSexp $(varE (mkName (T.unpack x))) |]
SL.ParenList xs -> [|SL.ParenList $(unquoteSplicingRecursive xs)|]
e -> liftData e
_ParenList :: Prism' Sexp (List Sexp)
_ParenList = prism' SL.ParenList \case
SL.ParenList xs -> Just xs
_ -> Nothing
metaSexps :: List Sexp.Sexp -> Maybe ExpQ
metaSexps = Just . unquoteSplicingRecursive
metaSexp :: Sexp.Sexp -> Maybe ExpQ
metaSexp = Just . unquoteRecursive
-- 뻘짓뻘짓뻘짓뻘짓뻘짓
class Lift1 f where
liftLift :: Quote m => (a -> m Exp) -> f a -> m Exp
lift1 :: (Lift1 f, Lift a, Quote m) => f a -> m Exp
lift1 = liftLift lift
instance Lift1 f => Lift (SL.Fix f) where
lift (SL.Fix inner) = appE [|SL.Fix|] (lift1 inner)
instance (Lift1 f, Lift1 g) => Lift1 (SL.Compose f g) where
liftLift l (SL.Compose fga) = [|SL.Compose $(liftLift (liftLift l) fga)|]
instance Lift a => Lift1 (SL.LocatedBy a) where
liftLift l (a SL.:< e) = [|(SL.:<) $(lift a) $(l e)|]
instance Lift1 List where
liftLift l xs = listE $ l <$> xs
instance Lift1 SL.SexpF where
liftLift l = \case
SL.AtomF a -> [|SL.AtomF $(lift a)|]
SL.ParenListF es -> [|SL.ParenListF $(liftLift l es)|]
SL.BracketListF es -> [|SL.BracketListF $(liftLift l es)|]
SL.BraceListF es -> [|SL.BraceListF $(liftLift l es)|]
SL.ModifiedF p e -> [|SL.ModifiedF $(lift p) $(l e)|]
-- deriving instance Lift a => Lift (SL.SexpF a)
deriving instance Lift SL.Atom
deriving instance Lift SL.Position
deriving instance Lift SL.Prefix
encodeOrShow :: (SexpIso a, Show a, IsString s) => a -> s
encodeOrShow a = fromString case encode a of
Left _ -> show a
Right e -> T.unpack e
extQ :: (Typeable a, Typeable b) => (a -> r) -> (b -> r) -> a -> r
extQ f g a = maybe (f a) g (cast a)
makeSxs :: Data r => Code Q (List Sexp -> r) -> QuasiQuoter
makeSxs f = QuasiQuoter
{ quoteExp = \str -> do
pos <- getPos
case readSexpsWithPos pos (T.pack str) of
Left e -> fail e
Right xs -> [| $(unTypeCode f) $e |]
where
e = dataToExpQ
(const Nothing `extQ` metaSexp `extQ` metaSexps)
xs
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
-- | An untyped variant of 'makeSx', useful when the user function is
-- polymorphic in its return value.
makeSx' :: ExpQ -> QuasiQuoter
makeSx' f = QuasiQuoter
{ quoteExp = \str -> do
pos <- getPos
case readSexpWithPos pos (T.pack str) of
Left e -> fail e
Right x -> [| $f $e |]
where
e = dataToExpQ
(const Nothing `extQ` metaSexp `extQ` metaSexps)
x
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
makeSx :: Data r => Code Q (Sexp -> r) -> QuasiQuoter
makeSx = makeSx' . unTypeCode
sxs = makeSxs [||id||]
sx = makeSx [||id||]
+201 -2
View File
@@ -1,4 +1,203 @@
module Gyehoek.Sexp.Grammar
(
) where
( module Gyehoek.Sexp.Grammar.Base
, module Data.InvertibleGrammar.Combinators
, (>>>)
, toDatum
, fromDatum
, toData
, fromData
, encodeWith
, encodeWith'
, encodeDataWith
, decodeWith
, encodeTest
, encodeTestColour
, encodeDataTest
, encodeDataTestColour
, encodeOrShow'
, encodeOrShowData'
, decodeDataWith
, encodeDataWith'
, decodeTest
, decodeDataTest
, DataIso(..)
, DatumIso(..)
-- * generics
, with
, match
, Coproduct(..)
, fromDatumUnsafe
, Control.Category.id
, fromDataUnsafe
, writeDatum
, writeData
)
where
import Gyehoek.Sexp.Grammar.Base
import Gyehoek.Prelude hiding (snoc, Iso, flipped, cons, traversed, iso)
import Data.InvertibleGrammar (backward, sealed, forward, runGrammar)
import Gyehoek.Sexp.Print (printDatum, printDatum', printData, printData')
import Gyehoek.Jalmot
import Data.InvertibleGrammar.Combinators
import qualified Gyehoek.Sexp.Read as Read
import qualified Data.Text.IO as TIO
import Text.Pretty.Simple (pPrintNoColor)
import Data.InvertibleGrammar.Generic
import qualified Control.Category
import qualified Data.Vector as V
import Data.String (IsString (fromString))
import qualified Data.Text as T
import System.Environment (lookupEnv)
import Data.Foldable (toList)
toDatum :: Jalmot :> es => DatumGrammar a -> a -> Eff es Datum
toDatum g =
backward (sealed g)
>>> runGrammar noAnn
>>> either (throwError . GrammarError) pure
toData :: Jalmot :> es => DataGrammar a -> a -> Eff es (List Datum)
toData g =
backward (sealed g)
>>> runGrammar noAnn
>>> either (throwError . GrammarError) pure
fromDatum :: (HasCallStack, Jalmot :> es) => DatumGrammar a -> Datum -> Eff es a
fromDatum g =
forward (sealed g)
>>> runGrammar noAnn
>>> either (throwError . GrammarError) pure
fromDatumUnsafe :: HasCallStack => DatumGrammar a -> Datum -> a
fromDatumUnsafe g = runJalmotUnsafe . fromDatum g
fromDataUnsafe :: HasCallStack => DataGrammar a -> List Datum -> a
fromDataUnsafe g = runJalmotUnsafe . fromData g
fromData
:: (HasCallStack, Jalmot :> es)
=> DataGrammar a -> List Datum -> Eff es a
fromData g =
forward (sealed g)
>>> runGrammar noAnn
>>> either (throwError . GrammarError) pure
encodeWith :: Jalmot :> es => DatumGrammar a -> a -> Eff es Text
encodeWith g = toDatum g >>> fmap printDatum
encodeDataWith :: Jalmot :> es => DataGrammar a -> a -> Eff es Text
encodeDataWith g = toData g >>> fmap printData
encodeDataWith' :: Jalmot :> es => DataGrammar a -> a -> Eff es Text
encodeDataWith' g = toData g >>> fmap printData'
encodeWith' :: Jalmot :> es => DatumGrammar a -> a -> Eff es Text
encodeWith' g = toDatum g >>> fmap printDatum'
decodeWith :: forall es a. Jalmot :> es => DatumGrammar a -> Text -> Eff es a
decodeWith g = Read.readString1 @es >=> fromDatum g
decodeDataWith
:: forall es a. Jalmot :> es => DataGrammar a -> Text -> Eff es a
decodeDataWith g = Read.readString @es >=> fromData g
-- | run a grammar, quick and dirty.
decodeTest :: Show a => DatumGrammar a -> Text -> IO ()
decodeTest g = pPrintNoColor <=< (runJalmotIO . decodeWith g)
-- | run a grammar, quick and dirty.
encodeTest :: DatumGrammar a -> a -> IO ()
encodeTest g = TIO.putStrLn <=< (runJalmotIO . encodeWith' g)
-- | run a grammar, quick and dirty.
encodeTestColour :: DatumGrammar a -> a -> IO ()
encodeTestColour g = TIO.putStrLn <=< (runJalmotIO . encodeWith g)
encodeDataTest :: DataGrammar a -> a -> IO ()
encodeDataTest g = TIO.putStrLn <=< (runJalmotIO . encodeDataWith' g)
encodeDataTestColour :: DataGrammar a -> a -> IO ()
encodeDataTestColour g = TIO.putStrLn <=< (runJalmotIO . encodeDataWith g)
-- | run a grammar, quick and dirty.
decodeDataTest :: Show a => DataGrammar a -> Text -> IO ()
decodeDataTest g = pPrintNoColor <=< (runJalmotIO . decodeDataWith g)
encodeOrShow' :: (IsString s, Show a) => DatumGrammar a -> a -> s
encodeOrShow' g x = fromString $
case runPureEff . runJalmot . encodeWith' g $ x of
Left _ -> show x
Right t -> T.unpack t
encodeOrShow :: (IsString s, Show a) => DatumGrammar a -> a -> s
encodeOrShow g x = fromString $
case runPureEff . runJalmot . encodeWith g $ x of
Left _ -> show x
Right t -> T.unpack t
encodeOrShowData' :: (IsString s, Show a) => DataGrammar a -> a -> s
encodeOrShowData' g x = fromString $
case runPureEff . runJalmot . encodeDataWith' g $ x of
Left _ -> show x
Right t -> T.unpack t
encodeOrShowData :: (IsString s, Show a) => DataGrammar a -> a -> s
encodeOrShowData g x = fromString $
case runPureEff . runJalmot . encodeDataWith g $ x of
Left _ -> show x
Right t -> T.unpack t
useColour :: IO Bool
useColour = maybe True (const False) <$> lookupEnv "NO_COLOR"
writeDatum :: (Show a, DatumIso a, MonadIO m) => a -> m ()
writeDatum x = do
c <- liftIO useColour
let f = if c then encodeOrShow else encodeOrShow'
liftIO . TIO.putStrLn . f datumIso $ x
writeData :: (Show a, DataIso a, MonadIO m) => a -> m ()
writeData x = do
c <- liftIO useColour
let f = if c then encodeOrShowData else encodeOrShowData'
liftIO . TIO.putStrLn . f dataIso $ x
class DatumIso a where
datumIso :: DatumGrammar a
class DataIso a where
dataIso :: DataGrammar a
instance DatumIso a => DatumIso (List a) where
datumIso = list $ rest datumIso
instance DatumIso Bool where datumIso = boolean
instance DatumIso Int where datumIso = int
instance DatumIso Natural where
datumIso = int
>>> partialOsi
(\x -> if x < 0
then Left $ expected "non-negative integer" <> unexpected [i|#{x}|]
else Right $ fromIntegral x)
fromIntegral
instance DatumIso Datum where datumIso = Control.Category.id
instance DatumIso a => DataIso (List a) where
dataIso = onHead . traversed . sealed $ datumIso @a
instance DatumIso a => DataIso (V.Vector a) where
dataIso = iso fromList V.toList
>>> (onHead . traversed . sealed $ datumIso @a)
instance DatumIso a => DataIso (NonEmpty a) where
dataIso = nonEmptyData datumIso
instance (DatumIso a, DatumIso b) => DatumIso (a, b) where
datumIso = with \tup2 -> list (el datumIso >>> el datumIso) >>> tup2
+467
View File
@@ -0,0 +1,467 @@
{- HLINT ignore "Avoid lambda" -}
-- | cribbed from sexp-grammar:Language.SexpGrammar.Base
module Gyehoek.Sexp.Grammar.Base
( module Gyehoek.Sexp.Syntax
, module Data.InvertibleGrammar.Combinators
, expected, unexpected
-- * types
, G
, Grammar(..)
, DatumGrammar
, DataGrammar
, ListContext(..)
, (:-)((:-))
-- * lists
, list
, listWithStyle
, el
, rest
, restData
, nonEmptyData
, headTagged0'
, headTagged0
, headTagged1'
, headTagged1
, headTagged2
, headTagged2'
-- * atoms
, simple
, string
, symbol
, sym
, boolean
, number
, integer
, int
, unreadable
-- * TODO: sort lol
, prismIso
, isoIso
, snoced
, letLike
, ifLike
, lambdaLike
, lambdaKeyword
, kappaKeyword
, beginLike
, dottedList
, reifyContext, recontextualise, decontextualise, redecorate
) where
import Data.InvertibleGrammar
import Data.InvertibleGrammar.Base
import Data.InvertibleGrammar.Combinators
import Gyehoek.Prelude hiding (flipped, traversed, iso, cons, coerced, Iso, Simple, simple)
import Gyehoek.Sexp.Syntax hiding (position)
import Gyehoek.Sexp.Print (printDatum')
import Data.Scientific (Scientific)
import qualified Data.Scientific as Sci
import qualified Data.Text as T
import qualified Data.List.NonEmpty as NE
import Data.Foldable (toList)
-- $setup
-- >>> :set -XOverloadedStrings
-- >>> import Gyehoek.Sexp.Grammar
-- >>> import Data.Text (Text)
-- >>> import GHC.Generics (Generic)
-- >>> import Data.List (List)
type G = Grammar Ann
type DatumGrammar a = forall t. G (Datum :- t) (a :- t)
type DataGrammar a = forall t. G (List Datum :- t) (a :- t)
-- | extract\/inject an annotation from\/into a 'Datum'.
position :: G (Datum :- t) (Ann :- Datum :- t)
position = Iso
(\(s :- t) -> view ann s :- s :- t)
(\(a :- s :- t) -> (s & ann .~ a) :- t)
locate :: G (Datum :- t) (Datum :- t)
locate =
position
>>> onHead Locate
>>> Iso
(\(_ :- t) -> t)
(\t -> noAnn :- t)
newtype ListContext = MkListContext { inner :: List Datum }
unexpectedSimple :: Simple -> Mismatch
unexpectedSimple = unexpected . printDatum' . Simple
unexpectedDatum :: Datum -> Mismatch
unexpectedDatum = unexpected . printDatum'
list
:: G (ListContext :- t) (ListContext :- t')
-> G (Datum :- t) t'
list = listWithStyle StyleData
-- |
-- >>> let grammar = with \g -> dottedList (el int) int >>> g
-- >>> decodeTest @(Int,Int) grammar "(1 . 2)"
-- ( 1
-- , 2
-- )
-- >>> let grammar = with \g -> dottedList (el int >>> el int) int >>> g
-- >>> decodeTest @(Int,Int,Int) grammar "(1 2 . 3)"
-- ( 1
-- , 2
-- , 3
-- )
dottedList
:: forall t t' t''. G (ListContext :- t) (ListContext :- t')
-> G (Datum :- t') t''
-> G (Datum :- t) t''
dottedList g final = begin >>> Dive (onTail (g >>> end) >>> final)
where
begin = locate >>> Flip (PartialIso
(\(x:-MkListContext xs:-t) -> case NE.nonEmpty xs of
Just xs' -> DotList xs' x :- t
Nothing -> error "fuck")
(\case
DotList xs x :- t -> Right $ x :- MkListContext (NE.toList xs) :- t
_ -> Left $ expected "dotted list"))
end :: Grammar Ann (ListContext :- t') t'
end = Flip $ PartialIso
(\t -> MkListContext [] :- t)
(\(MkListContext lst :- t) ->
case lst of
[] -> Right t
d:_ -> Left $ unexpectedDatum d)
listWithStyle
:: Style
-> G (ListContext :- t) (ListContext :- t')
-> G (Datum :- t) t'
listWithStyle ind g = begin >>> Dive (g >>> end)
where
begin = locate >>> partialOsi
(\case
List xs -> Right . MkListContext $ xs
_ -> Left $ expected "list")
(List' ind . coerce)
end = Flip $ PartialIso
(\t -> MkListContext [] :- t)
(\(MkListContext lst :- t) ->
case lst of
[] -> Right t
d:_ -> Left $ unexpectedDatum d)
-- |
-- >>> decodeTest (list $ el simple) "(in-here!)"
-- SimpleSymbol "in-here!"
el
:: G (Datum :- t) t'
-> G (ListContext :- t) (ListContext :- t')
el g = coerced (Flip cons >>> onTail g >>> Step)
reifyContext :: G (ListContext :- t) (List Datum :- t)
reifyContext = iso coerce coerce
decontextualise
:: G (List Datum :- t) (List Datum :- t')
-> G (ListContext :- t) t'
decontextualise g = reifyContext >>> g >>> end
where
end = Flip $ PartialIso
(\t -> [] :- t)
(\(lst :- t) ->
case lst of
[] -> Right t
d:_ -> Left $ unexpectedDatum d)
recontextualise
:: G (ListContext :- t) (ListContext :- t')
-> G (List Datum :- t) t'
recontextualise g = flipped reifyContext >>> g >>> end
where
end = Flip $ PartialIso
(\t -> MkListContext [] :- t)
(\(MkListContext lst :- t) ->
case lst of
[] -> Right t
d:_ -> Left $ unexpectedDatum d)
-- | matches the remainder of a list as repetition of a given
-- grammar.
--
-- >>> decodeTest (list $ rest simple) "(ga na da ra)"
-- [ SimpleSymbol "ga"
-- , SimpleSymbol "na"
-- , SimpleSymbol "da"
-- , SimpleSymbol "ra"
-- ]
rest
:: (forall t'. G (Datum :- t') (a :- t'))
-> G (ListContext :- t) (ListContext :- List a :- t)
rest g =
iso coerce coerce >>>
onHead (Traverse (sealed g >>> Step)) >>>
Iso (\a -> MkListContext [] :- a) (\(_ :- a) -> a)
-- | matches the remainder of a list with a 'DataGrammar'. this
-- differs from 'rest' in that the tail can be matched as a single
-- chunk, as opposed to matching each element individually with a
-- homogeneous \"rest element\" grammar.
--
-- >>> :{
-- data Example = MkExample (List Int) Text
-- deriving (Generic, Show)
-- dataGrammar :: DataGrammar Example
-- dataGrammar = with \g ->
-- flipped snoced >>>
-- onHead (traversed $ sealed int) >>>
-- onTail (onHead $ sealed symbol) >>>
-- swap >>>
-- g
-- :}
--
-- a 'DataGrammar' is usually used to code sequences of S-expressions,
-- e.g. the top-level of a Scheme program:
-- >>> decodeDataTest dataGrammar "1 2 3 end"
-- MkExample
-- [ 1
-- , 2
-- , 3
-- ] "end"
-- >>> encodeDataTest dataGrammar $ MkExample [1,2,3] "end"
-- 1
-- <BLANKLINE>
-- 2
-- <BLANKLINE>
-- 3
-- <BLANKLINE>
-- end
--
-- with 'dataRest', we can apply that same "top-level" grammar within a list:
-- >>> :{
-- dataRestGrammar :: DatumGrammar Example
-- dataRestGrammar = list . restData $ dataGrammar
-- :}
--
-- >>> decodeTest dataRestGrammar "(1 2 3 end)"
-- MkExample
-- [ 1
-- , 2
-- , 3
-- ] "end"
-- >>> encodeTest dataRestGrammar $ MkExample [1,2,3] "end"
-- (1 2 3 end)
restData
:: G (List Datum :- t) t'
-> G (ListContext :- t) (ListContext :- t')
restData g =
iso coerce coerce
>>> g
>>> push (MkListContext []) (const True) mempty
nonEmptyData :: DatumGrammar a -> DataGrammar (NonEmpty a)
nonEmptyData g = partialOsi
(\case
[] -> Left $ expected "non-empty sequence"
x:xs -> Right $ x:|xs)
toList
>>> (onHead . traversed . sealed $ g)
snoced
:: Snoc s s a a
=> Grammar p (s :- a :- t) (s :- t)
snoced = PartialIso
(\(s:-a:-t) -> Gyehoek.Prelude.snoc s a :- t)
(\(s:-t) -> case s ^? _Snoc of
Nothing -> Left $ expected "list element"
Just (s',a) -> Right $ s' :- a :- t)
-- atoms
-- | matches simple forms — atomic S-expressions.
--
-- >>> decodeTest simple "call/cc"
-- SimpleSymbol "call/cc"
simple :: G (Datum :- t) (Simple :- t)
simple = locate >>> partialOsi
(\case Simple s -> Right s
_ -> Left . expected $ "atom")
Simple
prismGrammar
-- | expected
:: Text
-- | unexpected
-> (s -> Mismatch)
-> Prism' s a
-> Grammar p (s :- t) (a :- t)
prismGrammar exp unexp p =
partialOsi
((_Left %~ \x -> expected exp <> unexp x) . matching p)
(review p)
-- |
-- >>> decodeTest symbol "symbolic-of-what???"
-- "symbolic-of-what???"
symbol :: G (Datum :- t) (Text :- t)
symbol = simple >>> prismGrammar "symbol" unexpectedSimple #SimpleSymbol
-- |
-- >>> let grammar = list $ el (sym "a-specific-symbol") >>> el string
-- >>> decodeTest grammar "(a-specific-symbol \"this works\")"
-- "this works"
-- >>> decodeTest grammar "(some-other-symbol \"this does not\")"
-- *** Exception:
-- <none>:1:2: mismatch:
-- Expected: symbol a-specific-symbol
-- But got: some-other-symbol
-- ...
sym :: Text -> G (Datum :- t) t
sym s = simple >>> Flip (PartialIso
(SimpleSymbol s :-)
(\(a :- t) ->
case a of
SimpleSymbol s' | s == s' -> Right t
other -> Left $ expected ("symbol " <> s) <>
unexpectedSimple other))
-- |
-- >>> decodeTest string "\"these r annoying to escape\""
-- "these r annoying to escape"
-- >>> encodeTest string "john Haskell"
-- "john Haskell"
string :: G (Datum :- t) (Text :- t)
string = simple >>> prismGrammar "string" unexpectedSimple #SimpleString
-- |
-- >>> decodeTest boolean "#t"
-- True
-- >>> decodeTest boolean "#false"
-- False
-- >>> encodeTest boolean True
-- #t
boolean :: G (Datum :- t) (Bool :- t)
boolean = simple >>> prismGrammar "boolean" unexpectedSimple #SimpleBoolean
-- |
-- >>> decodeTest number "123"
-- 123.0
-- >>> encodeTest number (fromInteger 456)
-- 456
number :: G (Datum :- t) (Scientific :- t)
number = simple >>> prismGrammar "number" unexpectedSimple #SimpleNumber
-- |
-- >>> decodeTest integer "123"
-- 123
-- >>> encodeTest number 456
-- 456
integer :: G (Datum :- t) (Integer :- t)
integer = number >>> partialOsi
((_Left %~ (unexpected . T.pack . show @Double)) . Sci.floatingOrInteger)
fromIntegral
int :: G (Datum :- t) (Int :- t)
int = integer >>> iso fromIntegral fromIntegral
-- high-level combinators
redecorate :: Style -> G (Datum :- t) t' -> G (Datum :- t) t'
redecorate sty g = iso (styleWith sty) (styleWith sty) >>> g
headTagged0 :: Text -> G (Datum :- t) t
headTagged0 s = listWithStyle StyleCode $ el (sym s)
headTagged0' :: Text -> DatumGrammar a -> G (Datum :- t) (List a :- t)
headTagged0' s gt = listWithStyle StyleCode $ el (sym s) >>> rest gt
headTagged1 :: Text -> DatumGrammar a -> G (Datum :- t) (a :- t)
headTagged1 s g1 = listWithStyle StyleCode $ el (sym s) >>> el g1
headTagged1'
:: Text
-> DatumGrammar a -> DatumGrammar b
-> G (Datum :- t) (List b :- a :- t)
headTagged1' s g1 gt = list $ el (sym s) >>> el g1 >>> rest gt
headTagged2
:: Text
-> DatumGrammar a -> DatumGrammar b
-> G (Datum :- t) (b :- a :- t)
headTagged2 s g1 g2 = listWithStyle StyleCode $ el (sym s) >>> el g1 >>> el g2
headTagged2'
:: Text
-> DatumGrammar a -> DatumGrammar b -> DatumGrammar c
-> G (Datum :- t) (List c :- b :- a :- t)
headTagged2' s g1 g2 gt =
listWithStyle StyleCode $ el (sym s) >>> el g1 >>> el g2 >>> rest gt
ifLike
-- | keyword
:: Text
-- | condition
-> DatumGrammar a
-- | consequent (then-branch)
-> DatumGrammar b
-- | alternative (else-branch)
-> DatumGrammar c
-> G (Datum :- t) (c :- b :- a :- t)
ifLike kw c t f =
listWithStyle (StyleSyntax 1) $
el (sym kw) >>> el c >>> el t >>> el f
letLike
:: Text
-> (forall t. G (Datum :- t) (a :- t))
-> (forall t. G (Datum :- t) (b :- t))
-> G (Datum :- List (a, b) :- t1) t2
-> G (Datum :- t1) t2
letLike kw name rhs e = listWithStyle (StyleSyntax 1) $
el (sym kw) >>> el bindings >>> el e
where
bindings = list $ rest binding
binding :: G (Datum :- t) ((_, _) :- t)
binding = list (el name >>> el rhs) >>> pair
lambdaLike
:: (forall t. G (Datum :- t) t)
-> G (Datum :- t1) (a :- t2)
-> G (ListContext :- a :- t2) (ListContext :- t3)
-> G (Datum :- t1) t3
lambdaLike kw formals body = listWithStyle (StyleSyntax 1) $
el kw
>>> el formals
>>> body
lambdaKeyword :: G (Datum :- t) t
lambdaKeyword = coproduct [ sym "λ", sym "lambda" ]
kappaKeyword :: G (Datum :- t) t
kappaKeyword = coproduct [ sym "κ", sym "kappa" ]
beginLike
:: Text
-> G (ListContext :- t) (ListContext :- t')
-> G (Datum :- t) t'
beginLike kw g =
listWithStyle (StyleSyntax 0) $
el (sym kw) >>> g
-- | define a printed syntax for an object which cannot be read.
unreadable
:: (t -> Text)
-> G (Datum :- t) t
unreadable f = Flip $ PartialIso
(\t -> Unreadable (f t) :- t)
(const $ Left mempty)
isoIso :: Iso' s a -> Grammar p (s :- t) (a :- t)
isoIso l = iso (view l) (review l)
prismIso :: Mismatch -> Prism' s a -> Grammar p (s :- t) (a :- t)
prismIso mm p = partialOsi
(maybe (Left mm) Right . preview p)
(review p)
+120 -41
View File
@@ -1,29 +1,89 @@
{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
module Gyehoek.Sexp.Print
( printDatum
, printDatumW
, printDatum'
, printData
, printData'
, htmlDatum
, htmlData
, putDoc
) where
import Gyehoek.Sexp.Syntax
import Data.Text.Prettyprint.Doc
import Data.Functor.Foldable
import qualified Control.Comonad.Trans.Cofree as F
import Prettyprinter.Util
import Gyehoek.Prelude hiding (Simple, (:<))
import Gyehoek.Sexp.Read (rd)
import Data.Foldable (traverse_)
import Prettyprinter
import Gyehoek.Prelude hiding (Simple)
import qualified Prettyprinter.Render.Terminal as ANSI
import System.IO (stdout)
import Prettyprinter.Render.Terminal (Color(..), color, italicized, AnsiStyle, bold)
import Prettyprinter.Render.Terminal (Color(..), color, italicized, AnsiStyle, bold, colorDull)
import Prettyprinter.Render.Text (renderStrict)
import Data.List (intersperse)
import Lucid
import Prettyprinter.Render.Util.SimpleDocTree (treeForm)
import Prettyprinter.Lucid (renderHtml)
import Data.Foldable (toList)
import qualified Data.Scientific as Sci
data Syn
= SynSyntax
| SynProcedure
| SynParen Int
| SynString
| SynConstant
| SynVariable
| SynNone
deriving (Show, Read, Data, Generic, Eq)
printDatum' :: Datum -> Text
printDatum' =
prettyDatum 0
>>> layoutSmart opts
>>> renderStrict
where
opts = LayoutOptions
{ layoutPageWidth = AvailablePerLine 80 1.0
}
htmlDatum :: Datum -> Html ()
htmlDatum =
prettyDatum 0
>>> layoutPretty opts
>>> treeForm
>>> fmap highlightHtml
>>> renderHtml
where
opts = LayoutOptions
{ layoutPageWidth = AvailablePerLine 80 1.0
}
htmlData :: Foldable f => f Datum -> Html ()
htmlData =
foldr f mempty
>>> layoutPretty opts
>>> treeForm
>>> fmap highlightHtml
>>> renderHtml
where
f x y = prettyDatum 0 x <> hardline <> hardline <> y
opts = LayoutOptions
{ layoutPageWidth = AvailablePerLine 80 1.0
}
printDatum :: Datum -> Text
printDatum = printDatumW 80
printData :: List Datum -> Text
printData = mconcat . intersperse "\n\n" . fmap printDatum
printData' :: List Datum -> Text
printData' = mconcat . intersperse "\n\n" . fmap printDatum'
printDatumW :: Int -> Datum -> Text
printDatumW w =
prettyDatum 0
>>> layoutSmart opts
>>> reAnnotateS highlight
>>> reAnnotateS highlightAnsi
>>> ANSI.renderStrict
where
opts = LayoutOptions
@@ -31,51 +91,70 @@ printDatumW w =
}
prettyDatum :: Int -> Datum -> Doc Syn
prettyDatum depth = \case
syn :< SimpleF s -> annotate syn $ prettySimple depth s
syn :< CompoundF compound -> case compound of
ListF indent xs ->
case indent of
NSpecial n | keyword:args <- xs ->
let (specialArgs,body) = splitAt n args
in pparen depth . nest 2 . vsep $
[ group . nest 2 . hcat $
[ prettyDatum (depth+1) keyword
, if null specialArgs then mempty else softline
, hsep $ prettyDatum (depth+1) <$> specialArgs
]
, vsep $ prettyDatum (depth+1) <$> body
]
Ordinary; NSpecial _ -> pparen depth $
group . align . vsep $
prettyDatum (depth+1) <$> xs
prettyDatum depth datum = case datum of
Simple simp -> prettySimple depth simp
DotList xs x ->
pparen depth . group . align $
vsep [ vsep (prettyDatum (depth+1) <$> toList xs)
, "."
, prettyDatum (depth+1) x
]
List' sty xs -> case sty of
StyleSyntax n | keyword:args <- xs ->
let (specialArgs,body) = splitAt n args
in pparen depth . nest 2 . vsep $
[ group . nest 2 . hcat $
[ annotate SynSyntax $ prettyDatum (depth+1) keyword
, if null specialArgs then mempty else softline
, hsep $ prettyDatum (depth+1) <$> specialArgs
]
, vsep $ prettyDatum (depth+1) <$> body
]
StyleCode | f:args <- xs -> pparen depth $
group . align . vsep . (_head %~ annotate SynProcedure) $
prettyDatum (depth+1) <$> xs
StyleData; StyleSyntax _; StyleCode -> pparen depth $
group . align . vsep $
prettyDatum (depth+1) <$> xs
_ -> error [i|unimplemented: #{datum}|]
pparen depth = enclose (delim depth "(") (delim depth ")")
delim depth = annotate (SynParen depth)
delimited :: Int -> Doc Syn -> Doc Syn -> List (Doc Syn) -> Doc Syn
delimited depth open close =
encloseSep (delim depth open) (delim depth close) softline
prettySimple :: Int -> Simple -> Doc Syn
prettySimple depth = \case
SimpleBoolean b -> annotate SynConstant $ if b then "#t" else "#f"
SimpleNumber n -> annotate SynConstant $ viaShow n
SimpleNumber n -> n
& Sci.floatingOrInteger @Double @Integer
& either viaShow viaShow
& annotate SynConstant
SimpleString s -> annotate SynString $ viaShow s
SimpleSymbol s -> pretty s
rdpr n s = rd s >>= traverse_ \x -> do
putDocW n . prettyDatum 0 $ x
putStr "\n"
SimpleUnreadable s -> pretty s
putDoc :: Doc Syn -> IO ()
putDoc = ANSI.renderIO stdout
. reAnnotateS highlight . layoutSmart defaultLayoutOptions . (<>"\n")
. reAnnotateS highlightAnsi . layoutSmart defaultLayoutOptions . (<>"\n")
highlight :: Syn -> AnsiStyle
highlight = \case
(SynBuiltin; SynMacro) -> color Magenta <> italicized <> bold
SynParen n -> color $ rainbow ^?! ix n
highlightAnsi :: Syn -> AnsiStyle
highlightAnsi = \case
SynSyntax -> color Magenta <> italicized <> bold
SynProcedure -> color Blue
SynConstant -> color Yellow
SynParen n -> colorDull $ rainbow ^?! ix n
_ -> mempty
where
rainbow = cycle [Red,Yellow,Green,Blue,Magenta,Cyan]
highlightHtml :: Syn -> Html () -> Html ()
highlightHtml syn = span_ [class_ synClass]
where
synClass = case syn of
SynSyntax -> "syn-builtin"
SynConstant -> "syn-constant"
SynString -> "syn-string"
SynProcedure -> "syn-procedure"
SynVariable -> "syn-variable"
SynNone -> "syn-none"
SynParen n -> [i|syn-paren-#{mod n 5}|]
+129
View File
@@ -0,0 +1,129 @@
{-# LANGUAGE TemplateHaskell #-}
module Gyehoek.Sexp.QQ
( makeSxs
, makeSx
, makeSx'
, sx
, sxs
, QuasiQuoter
) where
import Data.Data (Typeable, cast)
import Gyehoek.Prelude
import Gyehoek.Sexp.Syntax
import Language.Haskell.TH
import qualified Data.Text as T
import Data.List (groupBy)
import Language.Haskell.TH.Syntax (liftData, Lift (lift))
import Gyehoek.Jalmot
import Gyehoek.Sexp.Grammar
import Control.Exception (throw)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Haskell.TH.Syntax (dataToExpQ)
import qualified Gyehoek.Sexp.Read as Read
import Text.Megaparsec.Pos (mkPos)
import Gyehoek.Lift1
import Data.Foldable (toList)
extQ :: (Typeable a, Typeable b) => (a -> r) -> (b -> r) -> a -> r
extQ f g a = maybe (f a) g (cast a)
spliceMeta :: (HasCallStack, DataIso a) => a -> List Datum
spliceMeta x =
case runPureEff . runJalmot . toData dataIso $ x of
Left (cs,e) -> throw $ MkAJalmotCS cs e
Right xs -> xs
meta :: (HasCallStack, DatumIso a) => a -> Datum
meta x =
case runPureEff . runJalmot . toDatum datumIso $ x of
Left (cs,e) -> throw $ MkAJalmotCS cs e
Right xs -> xs
unquoteSplicingRecursive :: List Datum -> ExpQ
unquoteSplicingRecursive xs = [| mconcat $(spans) |]
where
spans = xs
& groupBy \cases
(MetaSplice _) _ -> False
_ (MetaSplice _) -> False
_ _ -> True
& fmap \case
[MetaSplice x] ->
[| spliceMeta $(varE (mkName (T.unpack x))) |]
es -> listE $ unquoteRecursive <$> es
& listE
unquoteRecursive :: Datum -> ExpQ
unquoteRecursive = \case
Meta x -> [| meta $(varE (mkName (T.unpack x))) |]
a :< CompoundF x -> [| $(liftData a) :< CompoundF $c|]
where
c = case x of
ListF ind xs ->
[| ListF $(lift ind) $(unquoteSplicingRecursive xs) |]
VectorF xs ->
[| VectorF $(unquoteSplicingRecursive xs) |]
DotListF xs t ->
[| DotListF $(liftLift unquoteRecursive xs) $(unquoteRecursive t) |]
AbbrevF p t ->
[| AbbrevF $(lift p) $(unquoteRecursive t) |]
e -> liftData e
getPos :: Q SourcePos
getPos = do
Loc {loc_filename,loc_start} <- location
pure $ SourcePos
{ sourceName = loc_filename
, sourceLine = mkPos $ fst loc_start
, sourceColumn = mkPos $ snd loc_start
}
readq
:: (SourcePos -> Text -> Eff '[Jalmot, IOE] a)
-> String -> Q a
readq f s = do
pos <- getPos
liftIO . runJalmotIO . f pos . T.pack $ s
makeSxs :: Data r => Code Q (List Datum -> r) -> QuasiQuoter
makeSxs f = QuasiQuoter
{ quoteExp = \str -> do
xs <- readq Read.readStringWithPos str
let e = dataToExpQ
(const Nothing
`extQ` (Just . unquoteRecursive)
`extQ` (Just . unquoteSplicingRecursive))
xs
[| $(unTypeCode f) $e |]
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
-- | An untyped variant of 'makeSx', useful when the user function is
-- polymorphic in its return value.
makeSx' :: ExpQ -> QuasiQuoter
makeSx' f = QuasiQuoter
{ quoteExp = \str -> do
x <- readq Read.readStringWithPos1 str
let e = dataToExpQ
(const Nothing
`extQ` (Just . unquoteRecursive)
`extQ` (Just . unquoteSplicingRecursive))
x
[| $f $e |]
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
makeSx :: Data r => Code Q (Datum -> r) -> QuasiQuoter
makeSx = makeSx' . unTypeCode
sx, sxs :: QuasiQuoter
sxs = makeSxs [|| Prelude.id @(List Datum) ||]
sx = makeSx [|| Prelude.id @Datum ||]
+127 -39
View File
@@ -1,7 +1,11 @@
{-# LANGUAGE ApplicativeDo #-}
module Gyehoek.Sexp.Read
( readFile
, readString
, rd
, readString1
, SourcePos(..)
, readStringWithPos
, readStringWithPos1
) where
import Text.Megaparsec
@@ -11,46 +15,80 @@ import Data.Void (Void)
import Gyehoek.Sexp.Syntax
import Gyehoek.Prelude hiding (Simple, (:<))
import qualified Data.Text.IO as T
import System.IO (stderr, hPutStrLn)
import Prelude hiding (readFile)
import Data.Functor (($>), void)
import Data.Functor (($>))
import qualified Data.Text as T
import Data.Char (GeneralCategory(..), generalCategory)
import Control.Exception hiding (try)
import Data.Scientific (Scientific)
import Gyehoek.Jalmot
import Data.Foldable
-- i'm lazy
newtype ReaderError = MkReaderError String
deriving (Show)
instance Exception ReaderError where
displayException (MkReaderError x) = x
-- temp
rd = runEff . readString
readFile :: IOE :> es => FilePath -> Eff es (List Datum)
readFile :: (Jalmot :> es, IOE :> es) => FilePath -> Eff es (List Datum)
readFile f = do
s <- liftIO . T.readFile $ f
case runParser file f s of
Right x -> pure x
Left e -> do
liftIO . throw . MkReaderError . errorBundlePretty $ e
Left eb -> throwError . ReaderError $ eb
readString :: IOE :> es => Text -> Eff es (List Datum)
readString :: Jalmot :> es => Text -> Eff es (List Datum)
readString s =
case runParser file "<none>" s of
Right x -> pure x
Left e -> do
liftIO . throw . MkReaderError . errorBundlePretty $ e
Left eb -> throwError . ReaderError $ eb
readString1 :: Jalmot :> es => Text -> Eff es Datum
readString1 s =
case runParser file1 "<none>" s of
Right x -> pure x
Left eb -> throwError . ReaderError $ eb
initialStateFromSourcePos :: SourcePos -> s -> State s e
initialStateFromSourcePos pos s = State
{ stateInput = s
, stateOffset = 0
, stateParseErrors = []
, statePosState = PosState
{ pstateInput = s
, pstateOffset = 0
, pstateSourcePos = pos
, pstateTabWidth = defaultTabWidth
, pstateLinePrefix = ""
}
}
readStringWithPos1
:: Jalmot :> es
=> SourcePos
-> Text
-> Eff es Datum
readStringWithPos1 pos s =
case snd $ runParser' file1 st of
Right x -> pure x
Left eb -> throwError . ReaderError $ eb
where
st = initialStateFromSourcePos pos s
readStringWithPos
:: Jalmot :> es
=> SourcePos
-> Text
-> Eff es (List Datum)
readStringWithPos pos s =
case snd $ runParser' file st of
Right x -> pure x
Left eb -> throwError . ReaderError $ eb
where
st = initialStateFromSourcePos pos s
type P = Parsec Void Text
--- lexer helpers
-- TODO: check R⁷RS
-- TODO: check R⁷RS's definition of ⟨atmosphere⟩.
-- TODO: datum comments.
-- | whitespace consumer.
sc :: P ()
sc = L.space space1
(L.skipLineComment ";")
@@ -59,6 +97,7 @@ sc = L.space space1
lexeme :: P a -> P a
lexeme = L.lexeme sc
-- | verbatim text.
verb :: Text -> P Text
verb = L.symbol sc
@@ -67,18 +106,51 @@ verb = L.symbol sc
identifier :: P Text
identifier = label "identifier" . lexeme . choice $
[ typical-- , delimited, peculiar
[ typical
-- , delimited
, peculiar
]
where
typical = T.cons <$> initial <*> subsequent
where
subsequent = takeWhileP Nothing \c ->
isInitial c ||
c `hasCategory` [SpacingCombiningMark, EnclosingMark, DecimalNumber]
|| c == '.' || c == '@' || c == '+' || c == '-'
initial = satisfy isInitial
subsequent = takeWhileP Nothing \c ->
isInitial c ||
c `hasCategory` [SpacingCombiningMark, EnclosingMark, DecimalNumber]
|| c == '.' || c == '@' || c == '+' || c == '-'
initial = satisfy isInitial
delimited = _
peculiar = _
peculiar = peculiarSign <|> peculiarDot
-- peculiarSign과 R⁷RS의 이 production 새 개들은 같음:
-- ⟨explicit sign⟩
-- ⟨explicit sign⟩ ⟨sign subsequent⟩ ⟨subsequent⟩*
-- ⟨explicit sign⟩ . ⟨dot subsequent⟩ ⟨subsequent⟩*
-- 같음:
-- ⟨explicit sign⟩
-- ((⟨sign subsequent⟩ | . ⟨dot subsequent⟩) ⟨subsequent⟩*)?
peculiarSign = do
sign <- explicitSign
r <- fold <$> optional do
neck <- choice
[ T.singleton <$> signSubsequent
, T.cons <$> single '.' <*> (T.singleton <$> dotSubsequent)
]
subs <- subsequent
pure $ neck <> subs
pure $ T.cons sign r
-- . ⟨dot subsequent⟩ ⟨subsequent⟩*
peculiarDot = do
dot <- single '.'
dotSub <- dotSubsequent
subs <- subsequent
pure $ T.cons dot $ T.cons dotSub subs
dotSubsequent = single '.' <|> signSubsequent
<?> "dot subsequent"
explicitSign = (satisfy \c -> c == '+' || c == '-')
<?> "explicit sign"
signSubsequent = initial <|> explicitSign <|> satisfy (=='@')
<?> "sign subsequent"
hasCategory c xs = generalCategory c `elem` xs
isInitial c = (c `hasCategory`
@@ -90,6 +162,7 @@ identifier = label "identifier" . lexeme . choice $
, ModifierSymbol, OtherSymbol, PrivateUse ]
|| c == '\x200c' || c == '\x200d')
&& c /= ';' && c /= '|' && c /= '"' && c /= '.'
&& c /= ',' && c /= '#'
boolean :: P Bool
boolean = label "boolean" . lexeme $ choice
@@ -135,18 +208,33 @@ string = label "string" . lexeme $
, "\\\\" $> '\\'
]
metaSplice :: P Text
metaSplice = label "splicing meta" . lexeme . between "##{" "}" $
takeWhile1P Nothing (/= '}')
meta :: P Text
meta = label "meta" . lexeme . between "#{" "}" $
takeWhile1P Nothing (/= '}')
file :: P (List Datum)
file = many datum <* eof
file = sc *> many datum <* eof
file1 :: P Datum
file1 = sc *> datum <* eof
datum :: P Datum
datum = choice
[ (SynNone :<) . CompoundF <$> compoundDatum
, (SynNone :<) . SimpleF <$> simpleDatum
-- , labeled
-- , labelRef
]
datum = do
pos <- getSourcePos
(position ?~ pos) <$> choice
[ Compound <$> compoundDatum
, Simple <$> simpleDatum
-- , labeled
-- , labelRef
, MetaSplice <$> metaSplice
, Meta <$> meta
]
simpleDatum :: P Simple
simpleDatum = choice
@@ -154,7 +242,7 @@ simpleDatum = choice
, SimpleNumber <$> try number
-- , SimpleCharacter <$> character
, SimpleString <$> string
, SimpleSymbol <$> symbol
, SimpleSymbol <$> try symbol
-- , SimpleBytevector <$> bytevector
]
@@ -166,9 +254,9 @@ compoundDatum = choice
list :: P Compound
list = label "list" . between lparen rparen $ do
optional datum >>= \case
Nothing -> pure $ ListF Ordinary []
Nothing -> pure $ ListF StyleData []
Just x -> do
xs <- many datum
optional (dot *> datum) >>= \case
Nothing -> pure $ ListF Ordinary (x:xs)
Nothing -> pure $ ListF StyleData (x:xs)
Just y -> pure $ DotListF (x:|xs) y
+131 -43
View File
@@ -1,6 +1,7 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ApplicativeDo #-}
{- HLINT ignore "Use newtype instead of data" -}
module Gyehoek.Sexp.Syntax
( DatumF(..)
, Simple(..)
@@ -13,38 +14,52 @@ module Gyehoek.Sexp.Syntax
, Cofree((:<))
, Fix(..)
, Compound
, Indentation(..)
, Syn(..)
, Style(..)
, pattern Simple
, pattern Compound
, pattern Labeled
, pattern LabelRef
, pattern Meta
, pattern MetaSplice
, pattern Abbrev
, pattern Vector
, pattern DotList
, pattern Gyehoek.Sexp.Syntax.List
, syntax
, indentation
, adorn
, indentWith
, style
, styleWith
, pattern Unreadable
, pattern Bytevector
, pattern Symbol
, pattern String
, pattern Character
, pattern Number
, pattern Boolean
, Ann(..)
, noAnn
, ann
, dat
, pattern List'
, position
, stripAnn
) where
import Language.Haskell.TH.Syntax (Lift)
import Language.Haskell.TH.Syntax (Lift (lift))
import Data.Scientific (Scientific)
import Data.ByteString (ByteString)
import Gyehoek.Prelude hiding ((:<), Simple)
import Text.Megaparsec.Pos (SourcePos(..))
import Control.Comonad.Cofree (Cofree((:<)), _extract)
import Gyehoek.Prelude hiding (Simple)
import Text.Megaparsec.Pos (SourcePos(..), sourcePosPretty)
import Control.Comonad.Cofree (Cofree((:<)), _extract, _unwrap)
import Data.Fix (Fix (..))
import Data.Functor.Foldable
import Text.Show.Deriving (deriveShow1)
import Data.Eq.Deriving (deriveEq1)
import qualified Control.Comonad.Trans.Cofree as F
import Prettyprinter (Pretty (pretty), viaShow)
import Gyehoek.Lift1 (Lift1 (liftLift))
import Data.Data (Typeable, cast)
import Language.Haskell.TH
import qualified Data.Text as T
import Control.Comonad.Trans.Cofree (tailF)
data DatumF a
@@ -52,6 +67,10 @@ data DatumF a
| CompoundF (CompoundF a)
| LabeledF Label a
| LabelRefF Label
-- | Should not be used outside of the "Gyehoek.Sexp.QQ" implementation.
| MetaF Text
-- | Should not be used outside of the "Gyehoek.Sexp.QQ" implementation.
| MetaSpliceF Text
deriving stock (Show, Eq, Data, Generic, Lift, Functor, Foldable, Traversable)
deriving anyclass (NFData)
@@ -62,11 +81,12 @@ data Simple
| SimpleString Text
| SimpleSymbol Text
| SimpleBytevector ByteString
| SimpleUnreadable Text
deriving stock (Show, Eq, Data, Generic, Lift)
deriving anyclass (NFData)
data CompoundF a
= ListF Indentation (List a)
= ListF Style (List a)
| DotListF (NonEmpty a) a
| VectorF (List a)
| AbbrevF Prefix a
@@ -74,7 +94,12 @@ data CompoundF a
deriving anyclass (NFData)
data Prefix
= Quote | Backtick | Comma | CommaAt
= Quote -- ^ @'@
| Backtick -- ^ @`@
| Comma -- ^ @,@
| CommaAt -- ^ @,\@@
| PoundQuote -- ^ @#'@
| PoundBacktick -- ^ @#`@
deriving stock (Show, Eq, Data, Generic, Lift)
deriving anyclass (NFData)
@@ -92,80 +117,143 @@ newtype Label = MkLabel Natural
type Datum = Cofree DatumF Syn
type Datum = Cofree DatumF Ann
type Compound = CompoundF Datum
data Indentation
= NSpecial Int
| Ordinary
data Style
= StyleSyntax Int
| StyleCode
| StyleData
deriving stock (Data, Eq, Generic, Show, Lift, Read)
deriving anyclass (NFData)
data Syn
= SynMacro
| SynBuiltin
| SynProcedure
| SynParen Int
| SynString
| SynConstant
| SynNone
deriving (Show, Read)
data Ann = MkAnn
{ position :: Maybe SourcePos
}
deriving (Show, Data, Eq, Generic)
noAnn :: Ann
noAnn = MkAnn
{ position = Nothing
}
-- requisite of the Pretty instance for invertible-grammar's error type.
instance Pretty Ann where
pretty = pretty . maybe "<unknown>" sourcePosPretty . view #position
deriveShow1 ''CompoundF
deriveEq1 ''CompoundF
deriveShow1 ''DatumF
deriveEq1 ''DatumF
syntax :: Lens' Datum Syn
syntax = _extract
--- modification and extraction of annotations
indentation :: Traversal' Datum Indentation
indentation k (syn :< CompoundF (ListF ind xs)) = do
ann :: Lens' Datum Ann
ann = _extract
dat :: Lens' Datum (DatumF Datum)
dat = _unwrap
position :: Lens' Datum (Maybe SourcePos)
position = ann . #position
-- affine
style :: Traversal' Datum Style
style k (syn :< CompoundF (ListF ind xs)) = do
ind' <- k ind
pure $ syn :< CompoundF (ListF ind' xs)
indentation k a = pure a
style k a = pure a
adorn :: Syn -> Datum -> Datum
adorn syn (_ :< d) = syn :< d
styleWith :: Style -> Datum -> Datum
styleWith = set style
indentWith :: Indentation -> Datum -> Datum
indentWith = set indentation
stripAnn :: Datum -> Fix DatumF
stripAnn = hoist tailF
--- pattern synonyms
pattern Simple :: Simple -> Datum
pattern Simple a <- _ :< SimpleF a
where Simple a = SynNone :< SimpleF a
where Simple a = noAnn :< SimpleF a
pattern Compound :: CompoundF Datum -> Datum
pattern Compound a <- _ :< CompoundF a
where Compound a = SynNone :< CompoundF a
where Compound a = noAnn :< CompoundF a
pattern Labeled :: Label -> Datum -> Datum
pattern Labeled l a <- _ :< LabeledF l a
where Labeled l a = SynNone :< LabeledF l a
where Labeled l a = noAnn :< LabeledF l a
pattern LabelRef :: Label -> Datum
pattern LabelRef l <- _ :< LabelRefF l
where LabelRef l = SynNone :< LabelRefF l
where LabelRef l = noAnn :< LabelRefF l
pattern MetaSplice :: Text -> Datum
pattern MetaSplice x <- _ :< MetaSpliceF x
where MetaSplice x = noAnn :< MetaSpliceF x
pattern Meta :: Text -> Datum
pattern Meta x <- _ :< MetaF x
where Meta x = noAnn :< MetaF x
pattern List :: List Datum -> Datum
pattern List a <- _ :< CompoundF (ListF _ a)
where List a = SynNone :< CompoundF (ListF Ordinary a)
where List a = noAnn :< CompoundF (ListF StyleData a)
pattern List' :: Style -> List Datum -> Datum
pattern List' ind a <- _ :< CompoundF (ListF ind a)
where List' ind a = noAnn :< CompoundF (ListF ind a)
pattern DotList :: NonEmpty Datum -> Datum -> Datum
pattern DotList xs x <- _ :< CompoundF (DotListF xs x)
where DotList xs x = SynNone :< CompoundF (DotListF xs x)
where DotList xs x = noAnn :< CompoundF (DotListF xs x)
pattern Vector :: [Datum] -> Datum
pattern Vector xs <- _ :< CompoundF (VectorF xs)
where Vector xs = SynNone :< CompoundF (VectorF xs)
where Vector xs = noAnn :< CompoundF (VectorF xs)
pattern Abbrev :: Prefix -> Datum -> Datum
pattern Abbrev p a <- _ :< CompoundF (AbbrevF p a)
where Abbrev p a = SynNone :< CompoundF (AbbrevF p a)
where Abbrev p a = noAnn :< CompoundF (AbbrevF p a)
pattern Boolean :: Bool -> Datum
pattern Boolean a = Simple (SimpleBoolean a)
pattern Number :: Scientific -> Datum
pattern Number a = Simple (SimpleNumber a)
pattern Character :: Char -> Datum
pattern Character a = Simple (SimpleCharacter a)
pattern String :: Text -> Datum
pattern String a = Simple (SimpleString a)
pattern Symbol :: Text -> Datum
pattern Symbol a = Simple (SimpleSymbol a)
pattern Bytevector :: ByteString -> Datum
pattern Bytevector a = Simple (SimpleBytevector a)
pattern Unreadable :: Text -> Datum
pattern Unreadable a = Simple (SimpleUnreadable a)
--- Lift1 instances
instance Lift1 DatumF where
liftLift l = \case
SimpleF s -> [|SimpleF $(lift s)|]
CompoundF c -> [|CompoundF $(liftLift l c)|]
LabeledF lbl x -> [|LabeledF $(lift lbl) $(l x)|]
LabelRefF lbl -> [|LabelRefF $(lift lbl)|]
MetaF x -> [|MetaF $(lift x)|]
MetaSpliceF x -> [|MetaSpliceF $(lift x)|]
instance Lift1 CompoundF where
liftLift l = \case
ListF ind xs -> [|ListF $(lift ind) $(liftLift l xs)|]
DotListF xs t -> [|DotListF $(liftLift l xs) $(l t)|]
VectorF xs -> [|VectorF $(liftLift l xs)|]
AbbrevF p x -> [|AbbrevF $(lift p) $(l x)|]
-119
View File
@@ -1,119 +0,0 @@
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveAnyClass #-}
module Gyehoek.Stack.Syntax
( Program(..)
, Block(..)
, Instr(..)
, Val(..)
, Lit(..)
, Obj(..)
, Imm(..)
, Hob(..)
, Prim(..)
, Name
, pattern ValLabel
, encodeProgram
) where
import Control.Lens
import Language.SexpGrammar (SexpIso, (>>>))
import Language.SexpGrammar qualified as S
import Language.SexpGrammar.Generic
import qualified Gyehoek.Sexp
import Gyehoek.Scheme.Syntax (Name(..), Lit(..), Prim(..))
import GHC.Exts (IsList(..))
import Data.List (intersperse)
import Gyehoek.CPS.Syntax (Imm(..), Obj(..), Hob(..), labelName)
import Gyehoek.Prelude
newtype Program = MkProgram
{ blocks :: List Block
}
deriving stock (Show, Generic, Data)
deriving newtype (Semigroup, Monoid)
deriving anyclass (NFData)
instance IsList Program where
type Item Program = Block
fromList = MkProgram
toList = view #blocks
data Block = MkBlock
{ label :: Name
, params :: List Name
, code :: List Instr
}
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
instance Each Block Block Instr Instr where
each = #code . each
data Instr
= Pop Name
| Push Val
| PopCont Name
| PushCont Val
| Prim Name (Prim Val)
| Call Val (List Val)
| If Val (List Instr) (List Instr)
deriving stock (Show, Generic, Data)
deriving anyclass (NFData)
data Val
= ValReg Name
| ValImm Imm
deriving stock (Show, Generic, Data, Eq)
deriving anyclass (NFData)
pattern ValLabel :: Name -> Val
pattern ValLabel x = ValImm (ImmLabel x)
--- sexp work
pure []
instance SexpIso Instr where
sexpIso = match
$ With (Gyehoek.Sexp.headTagged1 "pop!" regName >>>)
$ With (Gyehoek.Sexp.headTagged1 "push!" S.sexpIso >>>)
$ With (Gyehoek.Sexp.headTagged1 "pop-cont!" regName >>>)
$ With (Gyehoek.Sexp.headTagged1 "push-cont!" S.sexpIso >>>)
$ With (Gyehoek.Sexp.headTagged2 "prim" regName S.sexpIso >>>)
$ With (Gyehoek.Sexp.headTagged1' "call" S.sexpIso S.sexpIso >>>)
$ With (if_ >>>)
$ End
where
if_ = S.list $ S.el (S.sym "if")
>>> S.el (S.sexpIso @Val)
>>> S.el (S.list $ S.el (S.sym "then") >>> S.rest (S.sexpIso @Instr))
>>> S.el (S.list $ S.el (S.sym "else") >>> S.rest (S.sexpIso @Instr))
instance SexpIso Val where
sexpIso = match
$ With (regName >>>)
$ With (S.sexpIso >>>)
$ End
instance SexpIso Block where
sexpIso = with (block >>>)
where
block = S.list $
S.el (S.sym "define")
>>> S.el (S.list $ S.el labelName >>> S.rest regName)
>>> S.rest (S.sexpIso @Instr)
encodeProgram :: Program -> Text
encodeProgram p = p.blocks
& fmap ((^?! _Right) . Gyehoek.Sexp.encodePretty)
& intersperse "\n\n"
& mconcat
regName :: S.SexpGrammar Name
regName = S.sexpIso @Name >>> Gyehoek.Sexp.prismIso
(S.expected "register")
(prefixed @Name "%")
-149
View File
@@ -1,149 +0,0 @@
{-# LANGUAGE ViewPatterns #-}
module Gyehoek.Stack.VM
( VM(..)
, Env(..)
, eval
, trace
, module Gyehoek.Stack.Syntax
, writeObj
) where
import Gyehoek.Stack.Syntax
import Control.Lens
import qualified Data.HashMap.Strict as H
import Data.List (unfoldr)
import Gyehoek.Prelude
data VM = MkVM
{ stack :: List Obj
, kstack :: List Name
, code :: List Instr
, registers :: HashMap Name Obj
, stdout :: Text
, result :: Maybe (List Obj)
}
deriving (Show, Generic)
data Env = MkEnv
{ blocks :: HashMap Name Block
}
deriving (Show, Generic)
step :: Env -> VM -> VM
step e vm = case vm ^. #code of
c:cs -> stepI e (vm & #code .~ cs) c
_ -> error "halt never called"
stepI :: Env -> VM -> Instr -> VM
stepI e vm (Push v) = vm & #stack %~ (evalVal e vm v :)
stepI e vm (PushCont k) = vm & #kstack %~ (evalToLabel e vm k :)
stepI e vm (Prim r p) = case evalVal e vm <$> p of
PrimZeroP x -> case x of
ObjImm (ImmInt n) -> ret . ObjImm . ImmBool $ n == 0
_ -> error [i|bad arg to zero?: #{x}|]
PrimAdd x y -> arith_binop (+) x y
PrimMul x y -> arith_binop (*) x y
PrimSub x y -> arith_binop (-) x y
PrimDiv x y -> arith_binop div x y
PrimMakeClosure f env ->
case f of
ObjImm (ImmLabel l) -> ret . ObjHob $ HobClosure l env
_ -> error [i|expected label, got #{f}|]
PrimEnvCode env ->
case env of
ObjHob (HobClosure l _) -> ret . ObjImm . ImmLabel $ l
_ -> error [i|expected closure, got #{env}|]
PrimEnvRef env n ->
case env of
ObjHob (HobClosure _ xs) -> ret $ xs ^?! ix n
_ -> error [i|expected closure, got #{env}|]
x -> error [i|unimplemented prim: #{p}|]
where
ret v = vm & #registers . at r ?~ v
arith_binop op (ObjImm (ImmInt x)) (ObjImm (ImmInt y)) =
ret $ ObjImm (ImmInt (op x y))
arith_binop _ x y = error [i|bad arith: #{x}, #{y}|]
stepI e vm (Pop r) = case vm ^. #stack of
[] -> error "empty stack"
(x:xs) -> vm & #registers . at r ?~ x
& #stack .~ xs
stepI e vm ins@(PopCont r) = case vm ^. #kstack of
[] -> error [i|empty cont stack: #{ins}|]
(x:xs) -> vm & #registers . at r ?~ ObjImm (ImmLabel x)
& #kstack .~ xs
stepI e vm (Call v xs) =
case evalToLabel e vm v of
"halt" -> vm & #result ?~ fmap (evalVal e vm) xs
l -> vm & #code .~ b.code
& #registers .~ fmap (evalVal e vm) (H.fromList $ b.params `zip` xs)
where
b = case e ^. #blocks . at l of
Just x -> x
Nothing -> error [i|undefined label: #{l}|]
stepI e vm (If c t f) =
case evalVal e vm c of
ObjImm (ImmBool False) -> vm & #code .~ f
_ -> vm & #code .~ t
stepI e vm ins = error [i|unimplemented instruction: #{ins}|]
evalToLabel e vm v =
case evalVal e vm v of
ObjImm (ImmLabel x) -> x
x -> error [i|not a label: #{x}|]
evalVal :: Env -> VM -> Val -> Obj
evalVal e vm = \case
ValImm imm -> ObjImm imm
ValReg r -> case vm ^. #registers . at r of
Just x -> x
Nothing -> error [i|undefined register: #{r}|]
initialVM :: VM
initialVM = MkVM
{ stack = []
, kstack = ["halt"]
, code = [Call (ValImm $ ImmLabel "main") []]
, registers = mempty
, stdout = ""
, result = Nothing
}
initialEnv :: Program -> Env
initialEnv (MkProgram bs) = MkEnv
{ blocks = bs & foldMap \b -> H.singleton b.label b
}
loop :: (a -> Either b a) -> a -> b
loop f a = case f a of
Right a' -> loop f a'
Left b -> b
eval :: Program -> List Obj
eval p = initialVM & loop \vm -> case vm ^. #result of
Nothing -> Right $ step (initialEnv p) vm
Just rs -> Left rs
trace :: Program -> List VM
trace p = initialVM & unfoldr \vm ->
case vm.result of
Just _ -> Nothing
Nothing -> Just (vm, step e vm)
where e = initialEnv p
writeObj :: Obj -> Text
writeObj (ObjImm im) = case im of
ImmInt n -> [i|#{n}|]
ImmBool True -> "#t"
ImmBool False -> "#f"
ImmLabel l -> "#<procedure>"
writeObj (ObjHob h) = case h of
HobClosure code env -> "#<procedure>"
+18 -165
View File
@@ -1,4 +1,3 @@
{- HLINT ignore "Use newtype instead of data" -}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
@@ -6,187 +5,41 @@ module Gyehoek.Wasm
(
-- * syntax
Module
, Idx
, Program
, Function
, Expr
-- ** quasiquoters
, expr
, Gyehoek.Sexp.sx
, Gyehoek.Sexp.sxs
-- * GenMod effect
, GenMod
, runGenMod
, execGenMod
, defineFunction
, defineType
, defineGlobal
, emit
, renderModule
, watM
, wat
, wats
, defineFunctions
, defineTypes
, defineGlobals
)
where
import Language.SexpGrammar
( SexpIso(..), (>>>) )
import Language.SexpGrammar qualified as Sexp
import Language.SexpGrammar.Generic
import Data.List (List)
import GHC.Generics (Generic)
import Data.Text (Text)
import Effectful
import Numeric.Natural (Natural)
import Effectful.Dispatch.Dynamic
import Effectful.State.Dynamic
import Control.Lens
import Data.Vector.Strict (Vector)
import qualified Data.Vector.Strict as V
import Language.Sexp.Located
import qualified Gyehoek.Sexp
import GHC.IsList (IsList(..))
import Language.Haskell.TH.Quote (QuasiQuoter)
import Gyehoek.Sexp qualified as S
import Gyehoek.Sexp (Datum, (>>>))
import Data.Data (Data)
import Gyehoek.Sexp (sx)
import Data.Foldable (traverse_)
newtype Module = MkModule { inner :: Vector Sexp }
deriving (Show, Generic)
type Program = Module
type Function = Datum
type Expr = List Datum
newtype Module = MkModule { inner :: List Datum }
deriving (Show, Generic, Data)
deriving newtype (Semigroup, Monoid)
newtype Expr = MkExpr { inner :: Vector Instr }
deriving (Show, Generic, Data, Eq)
deriving newtype (Semigroup, Monoid)
instance IsList Expr where
type Item Expr = Instr
fromList = MkExpr . V.fromList
toList = V.toList . view #inner
newtype Instr = MkInstr { inner :: Sexp }
deriving (Show, Generic, Data, Eq)
newtype Idx = MkIdx { inner :: Natural }
deriving (Generic, Data)
deriving newtype (Show)
-- GenMod
-- | 'GenModState' is a 'Module' paired with the numbers of functions,
-- types, globals, etc. defined in the module.
data GenModState = MkGenModState
{ mod :: Module
, funcs :: Natural
, types :: Natural
, globals :: Natural
}
deriving (Show, Generic)
instance Semigroup GenModState where
m1 <> m2 = MkGenModState
{ mod = m1.mod <> m2.mod
, funcs = m1.funcs + m2.funcs
, types = m1.types + m2.types
, globals = m1.globals + m2.globals
}
instance Monoid GenModState where
mempty = MkGenModState
{ mod = mempty
, funcs = 0
, types = 0
, globals = 0
}
data GenMod :: Effect where
DefineFunction :: Sexp -> GenMod m Idx
DefineType :: Sexp -> GenMod m Idx
DefineGlobal :: Sexp -> GenMod m Idx
Emit :: Sexp -> GenMod m ()
type instance DispatchOf GenMod = Dynamic
defineFunction :: GenMod :> es => Sexp -> Eff es Idx
defineFunction = send . DefineFunction
defineFunctions :: GenMod :> es => List Sexp -> Eff es (List Idx)
defineFunctions = traverse (send . DefineFunction)
defineType :: GenMod :> es => Sexp -> Eff es Idx
defineType = send . DefineType
defineTypes :: GenMod :> es => List Sexp -> Eff es (List Idx)
defineTypes = traverse (send . DefineType)
defineGlobal :: GenMod :> es => Sexp -> Eff es Idx
defineGlobal = send . DefineGlobal
defineGlobals :: GenMod :> es => List Sexp -> Eff es (List Idx)
defineGlobals = traverse (send . DefineGlobal)
emit :: GenMod :> es => List Sexp -> Eff es ()
emit = traverse_ (send . Emit)
appendAndIncrement
:: State GenModState :> es
=> LensLike' ((,) Natural) GenModState Natural
-> Sexp
-> Eff es Idx
appendAndIncrement l s =
state \st -> st
& #mod . #inner <>~ V.singleton s
& l <<%~ succ
& _1 %~ MkIdx
runGenMod :: Eff (GenMod : es) a -> Eff es (a, Module)
runGenMod =
let run = (mapped . _2 %~ view #mod) . runStateLocal (mempty @GenModState)
in reinterpret run \cases
_ (DefineFunction s) -> appendAndIncrement #funcs s
_ (DefineType s) -> appendAndIncrement #types s
_ (DefineGlobal s) -> appendAndIncrement #globals s
_ (Emit s) -> #mod . #inner <>= V.singleton s
execGenMod :: Eff (GenMod : es) a -> Eff es Module
execGenMod = fmap snd . runGenMod
renderModule :: Module -> Text
renderModule (MkModule ss) = Gyehoek.Sexp.format [sx|
(module ##{ss})
|]
-- SexpIso instances
instance SexpIso Idx where
sexpIso = with \idx ->
Sexp.integer >>> Sexp.partialOsi f g
>>> idx
where
f n | n < 0 = Left $ Sexp.unexpected "negative"
<> Sexp.expected "natural"
| otherwise = Right $ fromIntegral n
g = fromIntegral
instance SexpIso Instr where
sexpIso = with id
instance Gyehoek.Sexp.SpliceSexp Expr where
spliceSexp = toListOf $ #inner . each . #inner
instance S.DatumIso Module where
datumIso = S.with \g ->
S.list (S.el (S.sym "module") >>> S.rest S.datumIso)
>>> g
-- quasiquoters
expr :: QuasiQuoter
expr = Gyehoek.Sexp.makeSxs
[||MkExpr . V.fromList . (each . #inner %~ Gyehoek.Sexp.stripLocation)
. fmap (Gyehoek.Sexp.fromSexp @Instr) ||]
wat :: QuasiQuoter
wat = Gyehoek.Sexp.makeSx [|| id ||]
wat = S.makeSxs [|| S.fromDataUnsafe (S.dataIso @(List Datum)) ||]
wats :: QuasiQuoter
wats = Gyehoek.Sexp.makeSxs [|| id ||]
watM :: QuasiQuoter
watM = S.makeSx [|| S.fromDatumUnsafe (S.datumIso @Module) ||]
BIN
View File
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
(import (scheme eval))
(eval '(λ (x) x)
(environment))
+68 -44
View File
@@ -2,54 +2,78 @@ module Gyehoek.Test.CPS.Eval where
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit
import Language.SexpGrammar ()
import Gyehoek.CPS.Syntax (cps, Obj(..), Hob(..), Imm(..))
import Gyehoek.CPS.Eval qualified as Sut
import Data.List (List)
import Test.Tasty.ExpectedFailure (ignoreTestBecause, expectFail)
import System.Directory (listDirectory)
import Test.Tasty.Silver
import System.FilePath
import Control.Exception
import qualified Gyehoek.Driver as Driver
import Control.DeepSeq (($!!))
import System.Exit (ExitCode(..))
import qualified Data.Text as T
import Data.Function (applyWhen)
import Gyehoek.Prelude
test_cpsInterpreter = testGroup "cps interpreter" $
[ primitives
, testCase "halt with constant" do
evalsTo [ObjImm (ImmInt 123)] [cps|
(continue halt 123)
|]
, testCase "identity cont" do
evalsTo [ObjImm (ImmInt 154)] [cps|
(letrec ((id (κ (x)
(continue halt x))))
(continue id 154))
|]
, testCase "identity function" do
evalsTo [ObjImm (ImmInt 456)] [cps|
(letrec ((id (λ (x ktail)
(continue ktail x))))
(id 456 halt))
|]
, testCase "square" do
evalsTo [ObjImm (ImmInt 81)] [cps|
(letrec ((square (λ (x ktail)
(prim (* x x)
(κ (r) (continue ktail r))))))
(square 9 halt))
|]
]
brokenEvalTests :: List String
brokenEvalTests =
[]
-- [ "adder"
-- , "apply2"
-- , "apply-twice"
-- , "arith"
-- , "begin-1"
-- , "callcc-constant"
-- , "callcc-discard"
-- , "callcc-early-exit-1"
-- , "callcc-early-exit-2"
-- , "callcc-early-exit-3"
-- , "callcc-early-exit-4"
-- , "callcc-early-exit-5"
-- , "callcc-early-exit-6"
-- , "callcc-nested-1"
-- , "callcc-nested-2"
-- , "complicated-1"
-- , "cons-1"
-- , "factorial"
-- , "false"
-- , "fn-of-fn"
-- , "if-false"
-- , "if-number"
-- , "if-true"
-- , "lambda"
-- , "letrec-fn"
-- , "let-fn"
-- , "lit-int"
-- , "square"
-- , "true"
-- ]
evalsTo :: HasCallStack => List Obj -> Sut.Program -> Assertion
evalsTo rs p = Sut.evalProgram p @?= rs
primitives = testGroup "primitives"
[ testGroup "arith"
[ testCase "basic 1" do
evalsTo [ObjImm (ImmInt 20)] [cps|
(prim (* 4 5)
(κ (x) (continue halt x)))
|]
, testCase "basic 2" do
evalsTo [ObjImm (ImmInt 35)] [cps|
(prim (* 2 16)
(κ (x) (prim (+ x 3)
(κ (r) (continue halt r)))))
|]
test_eval :: IO TestTree
test_eval = do
cs <- listDirectory "golden/exec"
<&> fmap ("golden/exec" </>)
pure $ testGroup "cps interpreter"
[ testGroup "higher-order" $ cpsCase Driver.eval_cps2_e2e <$> cs
-- , testGroup "first-order" $ cpsCase Driver.eval_cps1_e2e <$> cs
]
]
maybeBroken name broken = applyWhen (name `elem` broken) expectFail
cpsCase :: (FilePath -> IO Text) -> FilePath -> TestTree
cpsCase f test =
maybeBroken testName brokenEvalTests $
goldenVsAction testName resultFile action printProcResult
where
testName = takeFileName test
resultFile = test </> "exec"
sourceFile = test </> "source.scm"
action = catch @SomeException
(do r <- f sourceFile
pure $!! ( ExitSuccess
, r
, "" ))
\e -> pure (ExitFailure 1, "", T.pack $ displayException e)

Some files were not shown because too many files have changed in this diff Show More