kei-lisp 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/cli.cjs +211 -4
- package/dist/index.cjs +210 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +96 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +210 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -622,6 +622,11 @@ declare class Evaluator extends Object {
|
|
|
622
622
|
* Lisp-name to method-name dispatch map for special forms.
|
|
623
623
|
*/
|
|
624
624
|
static readonly buildInFunctions: Map<InterpretedSymbol, string>;
|
|
625
|
+
/**
|
|
626
|
+
* Marker symbol stored as the car of the Cons that represents a macro binding,
|
|
627
|
+
* distinguishing macros from ordinary `lambda` closures in the environment.
|
|
628
|
+
*/
|
|
629
|
+
static readonly macroMarker: InterpretedSymbol;
|
|
625
630
|
/**
|
|
626
631
|
* The variable binding environment used during evaluation.
|
|
627
632
|
*/
|
|
@@ -694,6 +699,54 @@ declare class Evaluator extends Object {
|
|
|
694
699
|
* @return the function name symbol
|
|
695
700
|
*/
|
|
696
701
|
defun(aCons: Cons): LispValue;
|
|
702
|
+
/**
|
|
703
|
+
* Implementation of the Lisp `defmacro` special form. Defines a macro: a
|
|
704
|
+
* transformer whose body receives its arguments unevaluated and returns a
|
|
705
|
+
* form that is then evaluated in the caller's environment.
|
|
706
|
+
* @param aCons the argument Cons containing the macro name, parameter list, and body
|
|
707
|
+
* @return the macro name symbol
|
|
708
|
+
*/
|
|
709
|
+
defmacro(aCons: Cons): LispValue;
|
|
710
|
+
/**
|
|
711
|
+
* Returns the macro transformer (a lambda Cons) bound to the given symbol, or
|
|
712
|
+
* null when the symbol is not bound to a macro. Special-form symbols are never
|
|
713
|
+
* treated as macros.
|
|
714
|
+
* @param car the operator position of a call form
|
|
715
|
+
* @return the macro's lambda Cons, or null
|
|
716
|
+
*/
|
|
717
|
+
lookupMacro(car: LispValue): Cons | null;
|
|
718
|
+
/**
|
|
719
|
+
* Expands a macro call exactly once by applying its transformer to the
|
|
720
|
+
* unevaluated argument forms in the macro's captured environment.
|
|
721
|
+
* @param form the call form whose car names the macro
|
|
722
|
+
* @param macroLambda the macro's transformer lambda Cons
|
|
723
|
+
* @return the expansion form
|
|
724
|
+
*/
|
|
725
|
+
expandMacro1(form: Cons, macroLambda: Cons): LispValue;
|
|
726
|
+
/**
|
|
727
|
+
* Expands a macro call once and evaluates the resulting form in the current
|
|
728
|
+
* environment.
|
|
729
|
+
* @param form the call form whose car names the macro
|
|
730
|
+
* @param macroLambda the macro's transformer lambda Cons
|
|
731
|
+
* @return the result of evaluating the expansion
|
|
732
|
+
*/
|
|
733
|
+
evalMacroCall(form: Cons, macroLambda: Cons): LispValue;
|
|
734
|
+
/**
|
|
735
|
+
* Implementation of the Lisp `macroexpand-1` special form. Evaluates its
|
|
736
|
+
* argument to obtain a form and, when that form is a macro call, expands it
|
|
737
|
+
* exactly once without evaluating the result.
|
|
738
|
+
* @param aCons the argument Cons whose car evaluates to the form to expand
|
|
739
|
+
* @return the once-expanded form, or the form unchanged when it is not a macro call
|
|
740
|
+
*/
|
|
741
|
+
macroexpand_1(aCons: Cons): LispValue;
|
|
742
|
+
/**
|
|
743
|
+
* Implementation of the Lisp `macroexpand` special form. Evaluates its
|
|
744
|
+
* argument to obtain a form and repeatedly expands it until the result is no
|
|
745
|
+
* longer a macro call, without evaluating the result.
|
|
746
|
+
* @param aCons the argument Cons whose car evaluates to the form to expand
|
|
747
|
+
* @return the fully expanded form
|
|
748
|
+
*/
|
|
749
|
+
macroexpand(aCons: Cons): LispValue;
|
|
697
750
|
/**
|
|
698
751
|
* Implementation of the Lisp `do` special form (parallel binding update).
|
|
699
752
|
* @param aCons the argument Cons containing bindings, termination clause, and body
|
|
@@ -852,6 +905,49 @@ declare class Evaluator extends Object {
|
|
|
852
905
|
* @return the quoted form
|
|
853
906
|
*/
|
|
854
907
|
quote(aCons: Cons): LispValue;
|
|
908
|
+
/**
|
|
909
|
+
* Implementation of the Lisp `quasiquote` (`` ` ``) special form. Returns the
|
|
910
|
+
* template with every `unquote` (`,`) and `unquote-splicing` (`,@`) at the
|
|
911
|
+
* matching nesting level replaced by the evaluation of its operand. Nested
|
|
912
|
+
* quasiquotes increase the level so inner unquotes are preserved.
|
|
913
|
+
* @param aCons the argument Cons whose car is the template
|
|
914
|
+
* @return the constructed form
|
|
915
|
+
*/
|
|
916
|
+
quasiquote(aCons: Cons): LispValue;
|
|
917
|
+
/**
|
|
918
|
+
* Recursively expands a quasiquote template at the given nesting level.
|
|
919
|
+
* @param template the template to expand
|
|
920
|
+
* @param level the current quasiquote nesting level (1 is the outermost)
|
|
921
|
+
* @return the expanded value
|
|
922
|
+
*/
|
|
923
|
+
quasiquoteExpand(template: LispValue, level: number): LispValue;
|
|
924
|
+
/**
|
|
925
|
+
* Expands the elements of a quasiquoted list, handling `unquote-splicing`
|
|
926
|
+
* (`,@`) elements and a possible dotted `unquote` (`,`) tail.
|
|
927
|
+
* @param template the list template to expand
|
|
928
|
+
* @param level the current quasiquote nesting level
|
|
929
|
+
* @return the constructed list
|
|
930
|
+
*/
|
|
931
|
+
quasiquoteList(template: Cons, level: number): LispValue;
|
|
932
|
+
/**
|
|
933
|
+
* Appends the elements of a spliced value (`,@`) onto the accumulator. The
|
|
934
|
+
* value must be a proper list (or nil); an atom or an improper (dotted) list
|
|
935
|
+
* is rejected rather than silently dropping the dotted tail.
|
|
936
|
+
* @param parts the accumulator of list elements
|
|
937
|
+
* @param value the value produced by an `unquote-splicing` operand
|
|
938
|
+
*/
|
|
939
|
+
spliceInto(parts: LispValue[], value: LispValue): null;
|
|
940
|
+
/**
|
|
941
|
+
* Implementation of the Lisp `unquote` (`,`) special form. Signals an error
|
|
942
|
+
* because unquote is only meaningful inside a `quasiquote` template.
|
|
943
|
+
*/
|
|
944
|
+
unquote(): never;
|
|
945
|
+
/**
|
|
946
|
+
* Implementation of the Lisp `unquote-splicing` (`,@`) special form. Signals
|
|
947
|
+
* an error because unquote-splicing is only meaningful inside a `quasiquote`
|
|
948
|
+
* template.
|
|
949
|
+
*/
|
|
950
|
+
unquoteSplicing(): never;
|
|
855
951
|
/**
|
|
856
952
|
* Implementation of the Lisp `rplaca` special form; destructively replaces the car of a Cons.
|
|
857
953
|
* @param args the argument Cons containing the target Cons expression and the new car value
|
package/dist/index.d.ts
CHANGED
|
@@ -622,6 +622,11 @@ declare class Evaluator extends Object {
|
|
|
622
622
|
* Lisp-name to method-name dispatch map for special forms.
|
|
623
623
|
*/
|
|
624
624
|
static readonly buildInFunctions: Map<InterpretedSymbol, string>;
|
|
625
|
+
/**
|
|
626
|
+
* Marker symbol stored as the car of the Cons that represents a macro binding,
|
|
627
|
+
* distinguishing macros from ordinary `lambda` closures in the environment.
|
|
628
|
+
*/
|
|
629
|
+
static readonly macroMarker: InterpretedSymbol;
|
|
625
630
|
/**
|
|
626
631
|
* The variable binding environment used during evaluation.
|
|
627
632
|
*/
|
|
@@ -694,6 +699,54 @@ declare class Evaluator extends Object {
|
|
|
694
699
|
* @return the function name symbol
|
|
695
700
|
*/
|
|
696
701
|
defun(aCons: Cons): LispValue;
|
|
702
|
+
/**
|
|
703
|
+
* Implementation of the Lisp `defmacro` special form. Defines a macro: a
|
|
704
|
+
* transformer whose body receives its arguments unevaluated and returns a
|
|
705
|
+
* form that is then evaluated in the caller's environment.
|
|
706
|
+
* @param aCons the argument Cons containing the macro name, parameter list, and body
|
|
707
|
+
* @return the macro name symbol
|
|
708
|
+
*/
|
|
709
|
+
defmacro(aCons: Cons): LispValue;
|
|
710
|
+
/**
|
|
711
|
+
* Returns the macro transformer (a lambda Cons) bound to the given symbol, or
|
|
712
|
+
* null when the symbol is not bound to a macro. Special-form symbols are never
|
|
713
|
+
* treated as macros.
|
|
714
|
+
* @param car the operator position of a call form
|
|
715
|
+
* @return the macro's lambda Cons, or null
|
|
716
|
+
*/
|
|
717
|
+
lookupMacro(car: LispValue): Cons | null;
|
|
718
|
+
/**
|
|
719
|
+
* Expands a macro call exactly once by applying its transformer to the
|
|
720
|
+
* unevaluated argument forms in the macro's captured environment.
|
|
721
|
+
* @param form the call form whose car names the macro
|
|
722
|
+
* @param macroLambda the macro's transformer lambda Cons
|
|
723
|
+
* @return the expansion form
|
|
724
|
+
*/
|
|
725
|
+
expandMacro1(form: Cons, macroLambda: Cons): LispValue;
|
|
726
|
+
/**
|
|
727
|
+
* Expands a macro call once and evaluates the resulting form in the current
|
|
728
|
+
* environment.
|
|
729
|
+
* @param form the call form whose car names the macro
|
|
730
|
+
* @param macroLambda the macro's transformer lambda Cons
|
|
731
|
+
* @return the result of evaluating the expansion
|
|
732
|
+
*/
|
|
733
|
+
evalMacroCall(form: Cons, macroLambda: Cons): LispValue;
|
|
734
|
+
/**
|
|
735
|
+
* Implementation of the Lisp `macroexpand-1` special form. Evaluates its
|
|
736
|
+
* argument to obtain a form and, when that form is a macro call, expands it
|
|
737
|
+
* exactly once without evaluating the result.
|
|
738
|
+
* @param aCons the argument Cons whose car evaluates to the form to expand
|
|
739
|
+
* @return the once-expanded form, or the form unchanged when it is not a macro call
|
|
740
|
+
*/
|
|
741
|
+
macroexpand_1(aCons: Cons): LispValue;
|
|
742
|
+
/**
|
|
743
|
+
* Implementation of the Lisp `macroexpand` special form. Evaluates its
|
|
744
|
+
* argument to obtain a form and repeatedly expands it until the result is no
|
|
745
|
+
* longer a macro call, without evaluating the result.
|
|
746
|
+
* @param aCons the argument Cons whose car evaluates to the form to expand
|
|
747
|
+
* @return the fully expanded form
|
|
748
|
+
*/
|
|
749
|
+
macroexpand(aCons: Cons): LispValue;
|
|
697
750
|
/**
|
|
698
751
|
* Implementation of the Lisp `do` special form (parallel binding update).
|
|
699
752
|
* @param aCons the argument Cons containing bindings, termination clause, and body
|
|
@@ -852,6 +905,49 @@ declare class Evaluator extends Object {
|
|
|
852
905
|
* @return the quoted form
|
|
853
906
|
*/
|
|
854
907
|
quote(aCons: Cons): LispValue;
|
|
908
|
+
/**
|
|
909
|
+
* Implementation of the Lisp `quasiquote` (`` ` ``) special form. Returns the
|
|
910
|
+
* template with every `unquote` (`,`) and `unquote-splicing` (`,@`) at the
|
|
911
|
+
* matching nesting level replaced by the evaluation of its operand. Nested
|
|
912
|
+
* quasiquotes increase the level so inner unquotes are preserved.
|
|
913
|
+
* @param aCons the argument Cons whose car is the template
|
|
914
|
+
* @return the constructed form
|
|
915
|
+
*/
|
|
916
|
+
quasiquote(aCons: Cons): LispValue;
|
|
917
|
+
/**
|
|
918
|
+
* Recursively expands a quasiquote template at the given nesting level.
|
|
919
|
+
* @param template the template to expand
|
|
920
|
+
* @param level the current quasiquote nesting level (1 is the outermost)
|
|
921
|
+
* @return the expanded value
|
|
922
|
+
*/
|
|
923
|
+
quasiquoteExpand(template: LispValue, level: number): LispValue;
|
|
924
|
+
/**
|
|
925
|
+
* Expands the elements of a quasiquoted list, handling `unquote-splicing`
|
|
926
|
+
* (`,@`) elements and a possible dotted `unquote` (`,`) tail.
|
|
927
|
+
* @param template the list template to expand
|
|
928
|
+
* @param level the current quasiquote nesting level
|
|
929
|
+
* @return the constructed list
|
|
930
|
+
*/
|
|
931
|
+
quasiquoteList(template: Cons, level: number): LispValue;
|
|
932
|
+
/**
|
|
933
|
+
* Appends the elements of a spliced value (`,@`) onto the accumulator. The
|
|
934
|
+
* value must be a proper list (or nil); an atom or an improper (dotted) list
|
|
935
|
+
* is rejected rather than silently dropping the dotted tail.
|
|
936
|
+
* @param parts the accumulator of list elements
|
|
937
|
+
* @param value the value produced by an `unquote-splicing` operand
|
|
938
|
+
*/
|
|
939
|
+
spliceInto(parts: LispValue[], value: LispValue): null;
|
|
940
|
+
/**
|
|
941
|
+
* Implementation of the Lisp `unquote` (`,`) special form. Signals an error
|
|
942
|
+
* because unquote is only meaningful inside a `quasiquote` template.
|
|
943
|
+
*/
|
|
944
|
+
unquote(): never;
|
|
945
|
+
/**
|
|
946
|
+
* Implementation of the Lisp `unquote-splicing` (`,@`) special form. Signals
|
|
947
|
+
* an error because unquote-splicing is only meaningful inside a `quasiquote`
|
|
948
|
+
* template.
|
|
949
|
+
*/
|
|
950
|
+
unquoteSplicing(): never;
|
|
855
951
|
/**
|
|
856
952
|
* Implementation of the Lisp `rplaca` special form; destructively replaces the car of a Cons.
|
|
857
953
|
* @param args the argument Cons containing the target Cons expression and the new car value
|
package/dist/index.js
CHANGED
|
@@ -671,6 +671,31 @@ var Parser = class Parser extends Object {
|
|
|
671
671
|
return 0;
|
|
672
672
|
}
|
|
673
673
|
/**
|
|
674
|
+
* Recognizes a backquote (`` ` ``), wraps the following form into `(quasiquote form)`, and returns the token number; invoked from NextState.
|
|
675
|
+
* @return 0
|
|
676
|
+
*/
|
|
677
|
+
quasiquote() {
|
|
678
|
+
const anObject = new Cons(this.nextToken(), Cons.nil);
|
|
679
|
+
this.token = new Cons(InterpretedSymbol.of("quasiquote"), anObject);
|
|
680
|
+
return 0;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Recognizes a comma and wraps the following form into `(unquote form)`, or
|
|
684
|
+
* `(unquote-splicing form)` when the comma is immediately followed by `@`
|
|
685
|
+
* (i.e. `,@`); invoked from NextState.
|
|
686
|
+
* @return 0
|
|
687
|
+
*/
|
|
688
|
+
unquote() {
|
|
689
|
+
let aSymbol = InterpretedSymbol.of("unquote");
|
|
690
|
+
if (this.peekChar() === "@") {
|
|
691
|
+
this.nextChar();
|
|
692
|
+
aSymbol = InterpretedSymbol.of("unquote-splicing");
|
|
693
|
+
}
|
|
694
|
+
const anObject = new Cons(this.nextToken(), Cons.nil);
|
|
695
|
+
this.token = new Cons(aSymbol, anObject);
|
|
696
|
+
return 0;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
674
699
|
* Returns the token number for a quote or for a 0-origin String-type (pseudo-Character); invoked from NextState.
|
|
675
700
|
* @return the next state number
|
|
676
701
|
*/
|
|
@@ -790,7 +815,7 @@ var Parser = class Parser extends Object {
|
|
|
790
815
|
aTable.set(String(41), this.nextState(-1, null));
|
|
791
816
|
aTable.set(String(42), this.nextState(8, "symbolToken"));
|
|
792
817
|
aTable.set(String(43), this.nextState(7, "sign"));
|
|
793
|
-
aTable.set(String(44), this.nextState(
|
|
818
|
+
aTable.set(String(44), this.nextState(0, "unquote"));
|
|
794
819
|
aTable.set(String(45), this.nextState(7, "sign"));
|
|
795
820
|
aTable.set(String(46), this.nextState(-1, null));
|
|
796
821
|
aTable.set(String(47), this.nextState(8, "symbolToken"));
|
|
@@ -802,7 +827,7 @@ var Parser = class Parser extends Object {
|
|
|
802
827
|
aTable.set(String(93), this.nextState(-1, null));
|
|
803
828
|
aTable.set(String(94), this.nextState(8, "symbolToken"));
|
|
804
829
|
aTable.set(String(95), this.nextState(8, "symbolToken"));
|
|
805
|
-
aTable.set(String(96), this.nextState(0, "
|
|
830
|
+
aTable.set(String(96), this.nextState(0, "quasiquote"));
|
|
806
831
|
for (const index of IntStream.rangeClosed(97, 122)) aTable.set(String(index), this.nextState(8, "symbolToken"));
|
|
807
832
|
aTable.set(String(123), this.nextState(-1, "parseList"));
|
|
808
833
|
aTable.set(String(124), this.nextState(8, "symbolToken"));
|
|
@@ -2851,6 +2876,11 @@ var Evaluator = class Evaluator extends Object {
|
|
|
2851
2876
|
*/
|
|
2852
2877
|
static buildInFunctions = Evaluator.setup();
|
|
2853
2878
|
/**
|
|
2879
|
+
* Marker symbol stored as the car of the Cons that represents a macro binding,
|
|
2880
|
+
* distinguishing macros from ordinary `lambda` closures in the environment.
|
|
2881
|
+
*/
|
|
2882
|
+
static macroMarker = InterpretedSymbol.of("macro");
|
|
2883
|
+
/**
|
|
2854
2884
|
* The variable binding environment used during evaluation.
|
|
2855
2885
|
*/
|
|
2856
2886
|
environment;
|
|
@@ -2996,6 +3026,85 @@ var Evaluator = class Evaluator extends Object {
|
|
|
2996
3026
|
return variable;
|
|
2997
3027
|
}
|
|
2998
3028
|
/**
|
|
3029
|
+
* Implementation of the Lisp `defmacro` special form. Defines a macro: a
|
|
3030
|
+
* transformer whose body receives its arguments unevaluated and returns a
|
|
3031
|
+
* form that is then evaluated in the caller's environment.
|
|
3032
|
+
* @param aCons the argument Cons containing the macro name, parameter list, and body
|
|
3033
|
+
* @return the macro name symbol
|
|
3034
|
+
*/
|
|
3035
|
+
defmacro(aCons) {
|
|
3036
|
+
const variable = aCons.car;
|
|
3037
|
+
const lambda = Evaluator.eval(new Cons(InterpretedSymbol.of("lambda"), aCons.cdr), new Table(this.environment), this.streamManager, this.depth, this.plugins);
|
|
3038
|
+
const macro = new Cons(Evaluator.macroMarker, new Cons(lambda, Cons.nil));
|
|
3039
|
+
this.environment.set(variable, macro);
|
|
3040
|
+
return variable;
|
|
3041
|
+
}
|
|
3042
|
+
/**
|
|
3043
|
+
* Returns the macro transformer (a lambda Cons) bound to the given symbol, or
|
|
3044
|
+
* null when the symbol is not bound to a macro. Special-form symbols are never
|
|
3045
|
+
* treated as macros.
|
|
3046
|
+
* @param car the operator position of a call form
|
|
3047
|
+
* @return the macro's lambda Cons, or null
|
|
3048
|
+
*/
|
|
3049
|
+
lookupMacro(car) {
|
|
3050
|
+
if (Cons.isNotSymbol(car) || Evaluator.buildInFunctions.has(car)) return null;
|
|
3051
|
+
const value = this.environment.get(car);
|
|
3052
|
+
if (Cons.isCons(value) && value.car === Evaluator.macroMarker) return value.nth(2);
|
|
3053
|
+
return null;
|
|
3054
|
+
}
|
|
3055
|
+
/**
|
|
3056
|
+
* Expands a macro call exactly once by applying its transformer to the
|
|
3057
|
+
* unevaluated argument forms in the macro's captured environment.
|
|
3058
|
+
* @param form the call form whose car names the macro
|
|
3059
|
+
* @param macroLambda the macro's transformer lambda Cons
|
|
3060
|
+
* @return the expansion form
|
|
3061
|
+
*/
|
|
3062
|
+
expandMacro1(form, macroLambda) {
|
|
3063
|
+
const capturedEnvironment = macroLambda.last().car;
|
|
3064
|
+
return Applier.apply(macroLambda, form.cdr, capturedEnvironment, this.streamManager, this.depth, this.plugins);
|
|
3065
|
+
}
|
|
3066
|
+
/**
|
|
3067
|
+
* Expands a macro call once and evaluates the resulting form in the current
|
|
3068
|
+
* environment.
|
|
3069
|
+
* @param form the call form whose car names the macro
|
|
3070
|
+
* @param macroLambda the macro's transformer lambda Cons
|
|
3071
|
+
* @return the result of evaluating the expansion
|
|
3072
|
+
*/
|
|
3073
|
+
evalMacroCall(form, macroLambda) {
|
|
3074
|
+
const expansion = this.expandMacro1(form, macroLambda);
|
|
3075
|
+
return Evaluator.eval(expansion, this.environment, this.streamManager, this.depth, this.plugins);
|
|
3076
|
+
}
|
|
3077
|
+
/**
|
|
3078
|
+
* Implementation of the Lisp `macroexpand-1` special form. Evaluates its
|
|
3079
|
+
* argument to obtain a form and, when that form is a macro call, expands it
|
|
3080
|
+
* exactly once without evaluating the result.
|
|
3081
|
+
* @param aCons the argument Cons whose car evaluates to the form to expand
|
|
3082
|
+
* @return the once-expanded form, or the form unchanged when it is not a macro call
|
|
3083
|
+
*/
|
|
3084
|
+
macroexpand_1(aCons) {
|
|
3085
|
+
const form = Evaluator.eval(aCons.car, this.environment, this.streamManager, this.depth, this.plugins);
|
|
3086
|
+
if (Cons.isNotCons(form)) return form;
|
|
3087
|
+
const macroLambda = this.lookupMacro(form.car);
|
|
3088
|
+
if (macroLambda == null) return form;
|
|
3089
|
+
return this.expandMacro1(form, macroLambda);
|
|
3090
|
+
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Implementation of the Lisp `macroexpand` special form. Evaluates its
|
|
3093
|
+
* argument to obtain a form and repeatedly expands it until the result is no
|
|
3094
|
+
* longer a macro call, without evaluating the result.
|
|
3095
|
+
* @param aCons the argument Cons whose car evaluates to the form to expand
|
|
3096
|
+
* @return the fully expanded form
|
|
3097
|
+
*/
|
|
3098
|
+
macroexpand(aCons) {
|
|
3099
|
+
let form = Evaluator.eval(aCons.car, this.environment, this.streamManager, this.depth, this.plugins);
|
|
3100
|
+
while (Cons.isCons(form)) {
|
|
3101
|
+
const macroLambda = this.lookupMacro(form.car);
|
|
3102
|
+
if (macroLambda == null) break;
|
|
3103
|
+
form = this.expandMacro1(form, macroLambda);
|
|
3104
|
+
}
|
|
3105
|
+
return form;
|
|
3106
|
+
}
|
|
3107
|
+
/**
|
|
2999
3108
|
* Implementation of the Lisp `do` special form (parallel binding update).
|
|
3000
3109
|
* @param aCons the argument Cons containing bindings, termination clause, and body
|
|
3001
3110
|
* @return the value of the termination clause's result form
|
|
@@ -3107,6 +3216,10 @@ var Evaluator = class Evaluator extends Object {
|
|
|
3107
3216
|
if (Cons.isNil(form) || Cons.isNotList(form)) return form;
|
|
3108
3217
|
const formCons = form;
|
|
3109
3218
|
if (Cons.isSymbol(formCons.car) && Evaluator.buildInFunctions.has(formCons.car)) return this.specialForm(formCons);
|
|
3219
|
+
if (Cons.isSymbol(formCons.car)) {
|
|
3220
|
+
const macroLambda = this.lookupMacro(formCons.car);
|
|
3221
|
+
if (macroLambda != null) return this.evalMacroCall(formCons, macroLambda);
|
|
3222
|
+
}
|
|
3110
3223
|
if (Cons.isSymbol(formCons.car) && this.plugins.length > 0) {
|
|
3111
3224
|
const symbol = formCons.car;
|
|
3112
3225
|
const plugin = this.plugins.find((p) => p.has(symbol));
|
|
@@ -3357,6 +3470,94 @@ var Evaluator = class Evaluator extends Object {
|
|
|
3357
3470
|
return aCons.car;
|
|
3358
3471
|
}
|
|
3359
3472
|
/**
|
|
3473
|
+
* Implementation of the Lisp `quasiquote` (`` ` ``) special form. Returns the
|
|
3474
|
+
* template with every `unquote` (`,`) and `unquote-splicing` (`,@`) at the
|
|
3475
|
+
* matching nesting level replaced by the evaluation of its operand. Nested
|
|
3476
|
+
* quasiquotes increase the level so inner unquotes are preserved.
|
|
3477
|
+
* @param aCons the argument Cons whose car is the template
|
|
3478
|
+
* @return the constructed form
|
|
3479
|
+
*/
|
|
3480
|
+
quasiquote(aCons) {
|
|
3481
|
+
return this.quasiquoteExpand(aCons.car, 1);
|
|
3482
|
+
}
|
|
3483
|
+
/**
|
|
3484
|
+
* Recursively expands a quasiquote template at the given nesting level.
|
|
3485
|
+
* @param template the template to expand
|
|
3486
|
+
* @param level the current quasiquote nesting level (1 is the outermost)
|
|
3487
|
+
* @return the expanded value
|
|
3488
|
+
*/
|
|
3489
|
+
quasiquoteExpand(template, level) {
|
|
3490
|
+
if (Cons.isNotCons(template)) return template;
|
|
3491
|
+
const aCons = template;
|
|
3492
|
+
if (aCons.car === InterpretedSymbol.of("unquote")) {
|
|
3493
|
+
if (level === 1) return Evaluator.eval(aCons.nth(2), this.environment, this.streamManager, this.depth, this.plugins);
|
|
3494
|
+
return new Cons(InterpretedSymbol.of("unquote"), new Cons(this.quasiquoteExpand(aCons.nth(2), level - 1), Cons.nil));
|
|
3495
|
+
}
|
|
3496
|
+
if (aCons.car === InterpretedSymbol.of("quasiquote")) return new Cons(InterpretedSymbol.of("quasiquote"), new Cons(this.quasiquoteExpand(aCons.nth(2), level + 1), Cons.nil));
|
|
3497
|
+
return this.quasiquoteList(aCons, level);
|
|
3498
|
+
}
|
|
3499
|
+
/**
|
|
3500
|
+
* Expands the elements of a quasiquoted list, handling `unquote-splicing`
|
|
3501
|
+
* (`,@`) elements and a possible dotted `unquote` (`,`) tail.
|
|
3502
|
+
* @param template the list template to expand
|
|
3503
|
+
* @param level the current quasiquote nesting level
|
|
3504
|
+
* @return the constructed list
|
|
3505
|
+
*/
|
|
3506
|
+
quasiquoteList(template, level) {
|
|
3507
|
+
const parts = [];
|
|
3508
|
+
let tail = Cons.nil;
|
|
3509
|
+
let current = template;
|
|
3510
|
+
while (Cons.isCons(current)) {
|
|
3511
|
+
if (current.car === InterpretedSymbol.of("unquote")) {
|
|
3512
|
+
tail = this.quasiquoteExpand(current, level);
|
|
3513
|
+
current = Cons.nil;
|
|
3514
|
+
break;
|
|
3515
|
+
}
|
|
3516
|
+
const head = current.car;
|
|
3517
|
+
if (Cons.isCons(head) && head.car === InterpretedSymbol.of("unquote-splicing")) if (level === 1) this.spliceInto(parts, Evaluator.eval(head.nth(2), this.environment, this.streamManager, this.depth, this.plugins));
|
|
3518
|
+
else parts.push(new Cons(InterpretedSymbol.of("unquote-splicing"), new Cons(this.quasiquoteExpand(head.nth(2), level - 1), Cons.nil)));
|
|
3519
|
+
else parts.push(this.quasiquoteExpand(head, level));
|
|
3520
|
+
current = current.cdr;
|
|
3521
|
+
}
|
|
3522
|
+
if (Cons.isNotNil(current)) tail = current;
|
|
3523
|
+
let result = tail;
|
|
3524
|
+
for (let index = parts.length - 1; index >= 0; index--) result = new Cons(parts[index], result);
|
|
3525
|
+
return result;
|
|
3526
|
+
}
|
|
3527
|
+
/**
|
|
3528
|
+
* Appends the elements of a spliced value (`,@`) onto the accumulator. The
|
|
3529
|
+
* value must be a proper list (or nil); an atom or an improper (dotted) list
|
|
3530
|
+
* is rejected rather than silently dropping the dotted tail.
|
|
3531
|
+
* @param parts the accumulator of list elements
|
|
3532
|
+
* @param value the value produced by an `unquote-splicing` operand
|
|
3533
|
+
*/
|
|
3534
|
+
spliceInto(parts, value) {
|
|
3535
|
+
if (Cons.isNil(value)) return null;
|
|
3536
|
+
if (Cons.isNotCons(value)) throw new EvalError(cannotApply("unquote-splicing", value));
|
|
3537
|
+
let current = value;
|
|
3538
|
+
while (Cons.isCons(current)) {
|
|
3539
|
+
parts.push(current.car);
|
|
3540
|
+
current = current.cdr;
|
|
3541
|
+
}
|
|
3542
|
+
if (Cons.isNotNil(current)) throw new EvalError(cannotApply("unquote-splicing", value));
|
|
3543
|
+
return null;
|
|
3544
|
+
}
|
|
3545
|
+
/**
|
|
3546
|
+
* Implementation of the Lisp `unquote` (`,`) special form. Signals an error
|
|
3547
|
+
* because unquote is only meaningful inside a `quasiquote` template.
|
|
3548
|
+
*/
|
|
3549
|
+
unquote() {
|
|
3550
|
+
throw new EvalError("unquote (\",\") is only valid inside a quasiquote (\"`\")");
|
|
3551
|
+
}
|
|
3552
|
+
/**
|
|
3553
|
+
* Implementation of the Lisp `unquote-splicing` (`,@`) special form. Signals
|
|
3554
|
+
* an error because unquote-splicing is only meaningful inside a `quasiquote`
|
|
3555
|
+
* template.
|
|
3556
|
+
*/
|
|
3557
|
+
unquoteSplicing() {
|
|
3558
|
+
throw new EvalError("unquote-splicing (\",@\") is only valid inside a quasiquote (\"`\")");
|
|
3559
|
+
}
|
|
3560
|
+
/**
|
|
3360
3561
|
* Implementation of the Lisp `rplaca` special form; destructively replaces the car of a Cons.
|
|
3361
3562
|
* @param args the argument Cons containing the target Cons expression and the new car value
|
|
3362
3563
|
* @return the modified Cons
|
|
@@ -3434,6 +3635,7 @@ var Evaluator = class Evaluator extends Object {
|
|
|
3434
3635
|
["apply", "apply_lisp"],
|
|
3435
3636
|
["bind", "bind"],
|
|
3436
3637
|
["cond", "cond"],
|
|
3638
|
+
["defmacro", "defmacro"],
|
|
3437
3639
|
["defun", "defun"],
|
|
3438
3640
|
["do", "do_"],
|
|
3439
3641
|
["dolist", "doList"],
|
|
@@ -3445,6 +3647,8 @@ var Evaluator = class Evaluator extends Object {
|
|
|
3445
3647
|
["lambda", "lambda"],
|
|
3446
3648
|
["let", "let"],
|
|
3447
3649
|
["let*", "letStar"],
|
|
3650
|
+
["macroexpand", "macroexpand"],
|
|
3651
|
+
["macroexpand-1", "macroexpand_1"],
|
|
3448
3652
|
["not", "not"],
|
|
3449
3653
|
["notrace", "notrace"],
|
|
3450
3654
|
["or", "or"],
|
|
@@ -3453,6 +3657,7 @@ var Evaluator = class Evaluator extends Object {
|
|
|
3453
3657
|
["princ", "princ"],
|
|
3454
3658
|
["print", "print"],
|
|
3455
3659
|
["push", "push_"],
|
|
3660
|
+
["quasiquote", "quasiquote"],
|
|
3456
3661
|
["quote", "quote"],
|
|
3457
3662
|
["rplaca", "rplaca"],
|
|
3458
3663
|
["rplacd", "rplacd"],
|
|
@@ -3462,6 +3667,8 @@ var Evaluator = class Evaluator extends Object {
|
|
|
3462
3667
|
["time", "time"],
|
|
3463
3668
|
["trace", "trace"],
|
|
3464
3669
|
["unless", "unless"],
|
|
3670
|
+
["unquote", "unquote"],
|
|
3671
|
+
["unquote-splicing", "unquoteSplicing"],
|
|
3465
3672
|
["when", "when"]
|
|
3466
3673
|
].map(([key, value]) => [InterpretedSymbol.of(key), value]));
|
|
3467
3674
|
} catch {
|
|
@@ -3656,7 +3863,7 @@ var LispInterpreter = class extends Object {
|
|
|
3656
3863
|
const aList = [];
|
|
3657
3864
|
const aTable = new Table();
|
|
3658
3865
|
aTable.setRoot(true);
|
|
3659
|
-
aList.push("abs", "add", "and", "apply", "assoc", "atom", "bind", "car", "cdr", "characterp", "cond", "ceiling", "concatenate", "cons", "consp", "copy", "cos", "count", "floatp", "floor", "defun", "divide", "do", "do*", "dolist", "doublep", "elt", "eq", "equal", "eval", "evenp", "every", "exit", "exp", "expt", "find", "format", "gc", "gensym", "if", "integerp", "lambda", "let", "let*", "last", "length", "list", "listp", "mapcan", "mapcar", "max", "member", "memq", "min", "minusp", "mod", "multiply", "napier", "neq", "nequal", "not", "notrace", "nth", "null", "numberp", "oddp", "or", "pi", "plusp", "pop", "princ", "print", "progn", "push", "quote", "random", "reduce", "round", "rplaca", "rplacd", "setq", "set-allq", "sin", "some", "sort", "sqrt", "string-downcase", "string-trim", "string-upcase", "stringp", "subseq", "substring", "subtract", "symbolp", "tan", "terpri", "time", "trace", "truncate", "unless", "when", "zerop", "1+", "1-", "+", "-", "*", "/", "//", "=", "==", "~=", "~~", "<", "<=", ">", ">=");
|
|
3866
|
+
aList.push("abs", "add", "and", "apply", "assoc", "atom", "bind", "car", "cdr", "characterp", "cond", "ceiling", "concatenate", "cons", "consp", "copy", "cos", "count", "floatp", "floor", "defmacro", "defun", "divide", "do", "do*", "dolist", "doublep", "elt", "eq", "equal", "eval", "evenp", "every", "exit", "exp", "expt", "find", "format", "gc", "gensym", "if", "integerp", "lambda", "let", "let*", "last", "length", "list", "listp", "macroexpand", "macroexpand-1", "mapcan", "mapcar", "max", "member", "memq", "min", "minusp", "mod", "multiply", "napier", "neq", "nequal", "not", "notrace", "nth", "null", "numberp", "oddp", "or", "pi", "plusp", "pop", "princ", "print", "progn", "push", "quasiquote", "quote", "random", "reduce", "round", "rplaca", "rplacd", "setq", "set-allq", "sin", "some", "sort", "sqrt", "string-downcase", "string-trim", "string-upcase", "stringp", "subseq", "substring", "subtract", "symbolp", "tan", "terpri", "time", "trace", "truncate", "unless", "unquote", "unquote-splicing", "when", "zerop", "1+", "1-", "+", "-", "*", "/", "//", "=", "==", "~=", "~~", "<", "<=", ">", ">=");
|
|
3660
3867
|
for (const each of aList) {
|
|
3661
3868
|
const aSymbol = InterpretedSymbol.of(each);
|
|
3662
3869
|
aTable.set(aSymbol, aSymbol);
|