mirror of
https://github.com/kanaka/mal.git
synced 2024-10-27 06:40:14 +03:00
00d168fdca
Self hosting fails at step4 because of exceeded TeX capacity.
174 lines
4.8 KiB
TeX
174 lines
4.8 KiB
TeX
\documentclass{article}
|
|
\usepackage
|
|
% Uncomment this and \debug_on below when debugging.
|
|
% [enable-debug]
|
|
{expl3}
|
|
\usepackage{types}
|
|
\usepackage{printer}
|
|
\usepackage{reader}
|
|
\ExplSyntaxOn
|
|
|
|
% Slow but quite useful.
|
|
% \debug_on:n { all }
|
|
|
|
% Step 2
|
|
|
|
\cs_new:Nn \mal_eval_map:nN
|
|
{
|
|
% \iow_term:n {eval_map~ast=#1~env=#2}
|
|
\mal_map_new:
|
|
\prop_map_inline:cn { #1 }
|
|
{
|
|
\str_if_eq:nnF { ##1 } { __meta__ }
|
|
{
|
|
\seq_push:NV \l_mal_stack_seq \l_tmpa_tl
|
|
\mal_eval:nN { ##2 } #2
|
|
\seq_pop:NN \l_mal_stack_seq \l_tmpb_tl
|
|
\tl_if_head_eq_charcode:VNTF \l_tmpa_tl e
|
|
{ \prop_map_break: }
|
|
{
|
|
\prop_put:cnV \l_tmpb_tl { ##1 } \l_tmpa_tl
|
|
\tl_set_eq:NN \l_tmpa_tl \l_tmpb_tl
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
\cs_new:Nn \mal_eval_iterate_tl:nN
|
|
{
|
|
% The evaluated elements are appended to \l_tmpa_tl.
|
|
% \iow_term:n {eval_tl:~forms=#1~env=#2}
|
|
\tl_map_inline:nn { #1 }
|
|
{
|
|
\seq_push:NV \l_mal_stack_seq \l_tmpa_tl
|
|
\mal_eval:nN { ##1 } #2
|
|
\seq_pop:NN \l_mal_stack_seq \l_tmpb_tl
|
|
\tl_if_head_eq_charcode:VNTF \l_tmpa_tl e
|
|
{ \tl_map_break: }
|
|
{
|
|
\tl_set:Nx \l_tmpa_tl
|
|
{ \exp_not:V \l_tmpb_tl { \exp_not:V \l_tmpa_tl } }
|
|
}
|
|
}
|
|
}
|
|
\cs_generate_variant:Nn \mal_eval_iterate_tl:nN { oN }
|
|
|
|
% EVAL
|
|
|
|
\cs_new:Nn \mal_fn_apply:nn
|
|
{
|
|
% \iow_term:n {fn_apply:~func=#1~args=#2}
|
|
\tl_if_head_eq_charcode:nNTF { #1 } b
|
|
{ \use_none:nn #1 { #2 } }
|
|
{
|
|
\tl_set:Nx \l_tmpa_tl
|
|
{ e s \tl_to_str:n { can~only~apply~functions } }
|
|
}
|
|
% \iow_term:V \l_tmpa_tl
|
|
}
|
|
\cs_generate_variant:Nn \mal_fn_apply:nn { nx, Vo, VV, xx }
|
|
|
|
\cs_new:Nn \mal_eval_list:nN
|
|
{
|
|
% \iow_term:n {eval_mal_list~tl=#1~env=#2}
|
|
\tl_set:Nx \l_tmpa_tl { \tl_head:n {#1} }
|
|
\bool_case_true:nF
|
|
{
|
|
{ \tl_if_eq_p:NN \l_tmpa_tl \c_empty_tl }
|
|
{ \tl_set:Nn \l_tmpa_tl { l n } }
|
|
}
|
|
{
|
|
% \iow_term:n {eval_mal_list~apply_phase~tl=#1~env=#2}
|
|
\mal_eval:xN { \tl_head:n { #1 } } #2
|
|
\tl_if_head_eq_charcode:VNF \l_tmpa_tl e
|
|
{
|
|
\seq_push:NV \l_mal_stack_seq \l_tmpa_tl
|
|
\tl_clear:N \l_tmpa_tl
|
|
\mal_eval_iterate_tl:oN { \use_none:n #1 } #2
|
|
\seq_pop:NN \l_mal_stack_seq \l_tmpb_tl
|
|
\tl_if_head_eq_charcode:VNF \l_tmpa_tl e
|
|
{ \mal_fn_apply:VV \l_tmpb_tl \l_tmpa_tl }
|
|
}
|
|
}
|
|
}
|
|
\cs_generate_variant:Nn \mal_eval_list:nN { oN }
|
|
|
|
\cs_new:Nn \mal_eval:nN
|
|
{
|
|
% \iow_term:n {EVAL:~ast=#1~env=#2}
|
|
\exp_args:Nx \token_case_charcode:NnF { \tl_head:n {#1} }
|
|
{
|
|
l
|
|
{ \mal_eval_list:oN { \use_none:nn #1 } #2 }
|
|
y
|
|
{
|
|
\prop_get:NnNF #2 { #1 } \l_tmpa_tl
|
|
{
|
|
\tl_set:Nx \l_tmpa_tl
|
|
{ e s \use_none:n #1 \tl_to_str:n { ~not~found } }
|
|
}
|
|
}
|
|
v
|
|
{
|
|
\tl_set:Nn \l_tmpa_tl { v n }
|
|
\mal_eval_iterate_tl:oN { \use_none:nn #1 } #2
|
|
}
|
|
m
|
|
{ \mal_eval_map:nN { #1 } #2 }
|
|
}
|
|
{ \tl_set:Nn \l_tmpa_tl {#1} }
|
|
% \iow_term:n {EVAL:~ast=#1~returns}
|
|
% \iow_term:V \l_tmpa_tl
|
|
}
|
|
\cs_generate_variant:Nn \mal_eval:nN { oN, VN, xN }
|
|
|
|
% REPL
|
|
|
|
\prop_new:N \l_mal_repl_env_prop
|
|
\cs_new:Nn \mal_int_op:nnN
|
|
{
|
|
\tl_set:Nx \l_tmpa_tl
|
|
{ i \int_eval:n { \use_none:n #1 #3 \use_none:n #2 } }
|
|
}
|
|
\cs_new:Nn \mal_add:n { \mal_int_op:nnN #1 + }
|
|
\cs_new:Nn \mal_sub:n { \mal_int_op:nnN #1 - }
|
|
\cs_new:Nn \mal_mul:n { \mal_int_op:nnN #1 * }
|
|
\cs_new:Nn \mal_div:n { \mal_int_op:nnN #1 / }
|
|
\prop_put:Nnn \l_mal_repl_env_prop { y + } { b n \mal_add:n }
|
|
\prop_put:Nnn \l_mal_repl_env_prop { y - } { b n \mal_sub:n }
|
|
\prop_put:Nnn \l_mal_repl_env_prop { y * } { b n \mal_mul:n }
|
|
\prop_put:Nnn \l_mal_repl_env_prop { y / } { b n \mal_div:n }
|
|
|
|
|
|
\cs_new:Nn \repl_loop:
|
|
{
|
|
% \ior_str_get_term is able to display a prompt on the same line,
|
|
% but this would make ./run far more complex for little benefit.
|
|
\iow_term:n {user>~}
|
|
\ior_str_get_term:nN {} \l_tmpa_str
|
|
\str_if_eq:VnF \l_tmpa_str {MAL_LATEX3_END_OF_INPUT} % from ./run
|
|
{
|
|
% Ignore empty lines, the MAL self-hosting relies on this
|
|
% *not* triggering an error.
|
|
\str_if_eq:VnF \l_tmpa_str {}
|
|
{
|
|
\mal_read_str:
|
|
\tl_if_head_eq_charcode:VNF \l_tmpa_tl e
|
|
{ \mal_eval:VN \l_tmpa_tl \l_mal_repl_env_prop }
|
|
\iow_term:x { \mal_printer_pr_str:VN \l_tmpa_tl \c_true_bool }
|
|
}
|
|
\repl_loop:
|
|
}
|
|
}
|
|
|
|
% ./run removes the normal LaTeX output.
|
|
\iow_term:n {MAL_LATEX3_START_OF_OUTPUT}
|
|
|
|
\repl_loop:
|
|
|
|
\iow_term:n {MAL_LATEX3_END_OF_OUTPUT} % for ./run
|
|
|
|
\ExplSyntaxOff
|
|
\begin{document}
|
|
\end{document}
|