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/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(8, "symbolToken"));
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, "quote"));
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);