This manual is for SL (version 0.2.0), a Simple Lisp interpreter.
Copyright © 2024 8dcc.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
A copy of the license should be included along with the manual. If not, see https://www.gnu.org/licenses/.
In order to compile the interpreter, the following dependencies have to be installed:
- GNU Coreutils, for
mkdir,rmandinstall. - A C11 compiler[fn::The program can actually be compiled with a C99
compiler that supports
-std=gnu99, or theSL_STATIC_ASSERTmacro could be modified to avoid using_Static_assertaltogether.], such asgccorclang. - GNU Make.
Furthermore, since the interpreter has Valgrind support, it is required
as a compile-time dependency, unless SL_NO_POOL_VALGRIND is defined.
Some Lisp examples are included in this repository, and can be tested by
running the run-tests.sh script. This script has some additional
dependencies, such as:
- GNU Bash.
- GNU Diffutils.
- GNU sed.
The following preprocessor macros can be defined at compile-time to alter the behavior of the program:
SL_NO_POOL_VALGRIND: When defined, the interpreter’s pool allocator won’t be integrated with Valgrind.SL_CALLSTACK_ON_ERR: When defined, the interpreter will print the current call stack tostderrwhenever a Lisp error occurs[fn::Note that the callstack is printed whenever the error occurs, which usually happens before the actual error message is printed.].SL_NO_COLOR: When defined, the interpreter won’t use ANSI escape sequences for printing colored messages.SL_DEBUG_MAX_CALLSTACK: When defined, specifies the number of maximum nested calls that the interpreter should support before raising a stack overflow error.
This section will explain some important concepts about the Lisp syntax, and about the interpreter itself.
All expressions have a single type that determines the kind of value it is currently storing. Types are usually capitalized and, in the manual, slanted.
See also Type-checking primitives and Type conversion primitives.
Some functions accept multiple numeric types, so in order to operate on them, they need to be internally converted to a Generic Number Type. Depending on the types and the values being converted, some information might be lost in the process (e.g. when converting really big integers).
Currently, the generic number type is Float, that is, a C double. For
example, most arithmetic function accept arguments of any numeric type,
so if the list of arguments is not homogeneous, they are converted, and
a Generic Number Type is returned.
(+ 5 2.0)
⇒ 7.0The C functions for handling Generic Number Types are defined as inline
in the expr.h header.
Lists are data structures used to combine Lisp expressions. They are built by joining together pairs.
A proper list is a list whose last cdr is the symbol nil; and an
improper lists is a list whose last cdr is not the symbol nil.
It is important to understand that lists like (a b c) are syntactic
sugar for (a . (b . (c . nil)))~[fn::See [[https://web.mit.edu/6.001/6.037/sicp.pdf#subsection.2.2.1][section 2.2.1]] of /Structure and
Interpretation of Computer Programs/.], and that ~() is syntactic sugar
for the symbol nil, described below (nil).
These variables are defined by default in the global environment.
These symbols are bound to a constant value, and can’t be redefined.
- Variable: `nil’
- <<nil>>
Represents the empty list, but is also used to denote the logical value false.
nil ⇒ nil 'nil ⇒ nil (equal? 'nil nil) ⇒ tru (eval nil) ⇒ nil
Internally,
nilis just a normal symbol that is bound to itself in the global environment. This symbol is used to terminate proper lists (see Lists), as the last cdr.(cons 1 (cons 2 nil)) ⇒ (1 2)
This symbol is treated by most primitives as a list, so it can be used in most functions that accept pairs. For convenience, the
carandcdrofnilis alsonil.(car nil) ⇒ nil (cdr nil) ⇒ nil
As explained in Lists, an empty list with the form
()is syntactic sugar for the symbolnil. This conversion is made in the parser, so()isn’t a function call because it’s not a pair[fn::To be a function call, the expression needs to be a proper list with one or more elements, so the smallest possible call is(f)or(f . nil).].'() ; Same as (quote nil) ⇒ nil () ; Same as nil, evaluates to itself ⇒ nil (type-of nil) ⇒ Symbol (type-of '()) ⇒ Symbol
- Variable: `tru’
- <<tru>>
Symbol that evaluates to itself, used for representing explicit truth in procedures returning predicates (see Logical primitives). There is no need for this symbol, since any non-nil expression represents truth, but it’s convenient.
tru ⇒ tru (eval tru) ⇒ tru
These variables are used by the interpreter itself for debugging purposes.
- Variable: `*debug-trace*’
- <<*debug-trace*>>
List of expressions that are traced when called. It’s not advised to change the value of
*debug-trace*directly withdefine, but instead use the standard library functiontrace(see =trace=).(defun fact (n) (if (= n 0) 1 (* n (fact (- n 1))))) (define *debug-trace* (list fact)) (fact 3) 0: (fact 3) 1: (fact 2) 2: (fact 1) 3: (fact 0) 3: 1 2: 1 1: 2 0: 6 ⇒ 6
In the previous example, notice how the function itself is added to the list, not the symbol
fact. This allows debugging anonymous functions and macros, as long as they match with theequal?primitive (see =equal?=).(defun identity (e) e) ⇒ <lambda> > (equal? identity (lambda (e) e)) ⇒ tru (trace (lambda (e) e)) ⇒ "Trace enabled." (identity 5) 0: (identity 5) 0: 5 ⇒ 5
This section explains the different primitive procedures in SL. Primitive procedures are implemented in C.
These primitives are special forms, that is, special procedures whose arguments are not evaluated before the call. This way the procedures can operate on the un-evaluated expressions, before evaluating them manually if needed. The C primitives for this section are defined in prim_special.c.
A list is interpreted as a special form call when its first element is a special form symbol. Special form symbols are specified by an environment flag that can’t be currently set by the user. Special form symbols are also constant, so they can’t be redefined.
(defun special-form-symbol? (sym)
;; TODO: Check the symbol's flags in the environment.
...)
(defun special-form? (e)
;; Non-empty list whose first element is a special symbol.
(and (list? e)
(not (null? e))
(symbol? (car e))
(special-form-symbol? (car e))))For more information on special forms, see Section 4.1.1 of /Structure and Interpretation of Computer Programs/[fn:: https://web.mit.edu/6.001/6.037/sicp.pdf#subsection.4.1.1].
- Special Form: quote expr
- <<quote>>
Return the argument, effectively delaying its evaluation.
(quote x) ⇒ x 'x ⇒ x (quote (+ 1 2)) ⇒ (+ 1 2)
Note that
'expris just syntactic sugar for(quote expr). This is handled in parser.c. - Special Form: backquote expr
- <<backquote>>
Return the argument, while allowing selective evaluation. Without using special unquote arguments, described below, it’s behavior is identical to
quote. Note that multiple symbols are bound to the C primitive in the global environment:backquoteand =`=[fn::That is, the grave accent character (ASCII code 96).].As mentioned, the backquote is pretty special because it lets the user evaluate parts of the argument expression. There are two symbols that can be used as a procedure call for specifying which parts should be evaluated. The
,symbol[fn::That is, the comma character (ASCII code 44).] is used for unquoting and the,@symbol[fn::That is, the comma character (ASCII code 44) followed by the at sign (ASCII code 64).] is used for splicing.If an expression is unquoted (e.g
,expror(, expr)), it will be evaluated bybackquote. If an expression is spliced (e.g,@expror(,@ expr)), it will be evaluated just like when unquoting, but instead of returning the list itself, the contents of the resulting list will be appended to an outer list. Therefore, you can only splice an expression if it evaluates to a list, and if the splice call was made within another list.The
,and,@symbols are bound in the global environment to note that they are reserved, but they cannot be used outside of abackquoteargument.Again, just like with
quote, note that`expris just syntactic sugar for(` expr), and,expris syntactic sugar for(, expr). They are all handled in parser.c.`sym ⇒ sym ;; For showing how the parser expands them. (quote `(a ,b c d)) ⇒ (` (a (, b) c d)) (define var 123) `(a ,var b c) ⇒ (a 123 b c) `(a (b ,var) c ,var) ⇒ (a (b 123) c 123) (define my-list '(1 2 3)) `(a b ,@my-list c d) ⇒ (a b 1 2 3 c d) `(a b ,@(list 'X 'Y 'Z) c) ⇒ (a b X Y Z c)
Since the backquote evaluates each unquoted expression normally, you can nest backquotes without any special syntax:
`(hi ; "hi" quoted by the outer backquote. ,(if (< var 30) ; "if" Evaluated by the outer backquote. (+ 100 var) ; "+" evaluated depending on the "if". `(abc ; "abc" quoted by the inner backquote. ,var ; "var" evaluated by the inner backquote. xyz)) ; "xyz" quoted by the inner backquote. bye) ; "bye" quoted by the outer backquote.
In the previous example, if
varwas7, the backquote would return(hi 107 bye), but ifvarwas35, it would return(hi (abc 35 xyz) bye).Also note that none of this unquote functionality is available inside
quotearguments, justbackquote:'(,a b (c ,d) e) ⇒ ((, a) b (c (, d)) e) (define var 123) ⇒ 123 (define my-backquote-call '`,var) ⇒ (` (, var)) (eval my-backquote-call) ⇒ 123
- Special Form: define symbol expr
- <<define>>
Bind a symbol to a value in the current environment.
It binds the first argument (a symbol) to the result of evaluating the second argument. Returns the evaluated expression.
n ⇒ Unbound symbol: `n'. (define n 123) ⇒ 123 n ⇒ 123
As mentioned, it only operates on the current environment.
(define n 123) ⇒ 123 (define f (lambda () (define n 999) (list "Finished:" n))) (f) ⇒ ("Finished:" 999) n ⇒ 123
It is a special form because the first argument is not evaluated. This way, it doesn’t have to be quoted by the caller.
- Special Form: define-global symbol expr
- <<define-global>>
Bind a symbol to a value in the top-most environment. For more information, see =define=.
(define n 123) ⇒ 123 (define f (lambda () (define-global n 999) (list "Finished:" n))) (f) ⇒ ("Finished:" 999) n ⇒ 999
- Special Form: lambda formals body…
- <<lambda>>
Return a new anonymous procedure.
The
lambdaprimitive expects a list of formal arguments (which must be symbols) and one or more expressions (of any type) for the body.Expressions of type Lambda evaluate to themselves. When calling a lambda, each argument is evaluated and bound to its formal symbol, and each expression in the body of the function is evaluated in order, returning the last one.
(lambda (x) (* x 3)) ⇒ <lambda> ((lambda (x) (* x 3)) 5) ⇒ 15 (define f (lambda (x) (+ x 5))) ⇒ <lambda> (f 3) ⇒ 8
A keyword symbol
&restfollowed by a single symbol S, can be used in the formal argument list to indicate that the caller can provide extra non-mandatory arguments, and they will be stored in a list bound to the symbol S when making the call. If no extra arguments are provided when making the call, S is bound to the empty listnil.(define f (lambda (a b &rest other) (list a b other))) ⇒ <lambda> (f 1 2 3 4 5) ⇒ (1 2 (3 4 5))
- Special Form: macro formals body…
- <<macro>>
Return a new anonymous macro.
The
macroprimitive expects a list of formal arguments (which must be symbols) and one or more expressions (of any type) for the body.Expressions of type Macro evaluate to themselves. Macros are generally similar to lambdas, but there are some key differences:
- When a macro is called, the arguments are not evaluated before applying it, so the macro can operate on the un-evaluated expressions directly, instead of on the values they compute. The first step of a macro call is binding the un-evaluated arguments to the formals.
- Macros don’t directly compute values, they instead build Lisp expressions that will be used to compute the actual values. The second step of a macro call is the macro expansion (see =macroexpand=). In this step, the macro is called just like a lambda, returning a Lisp expression.
- The last step of a macro call is evaluating the expanded expression, which will be used to compute the actual value returned by the macro.
In other words the general process when calling a lambda is:
Evaluate arguments -> Bind arguments -> Evaluate body `-----------------------------´ (Apply)While the call process of a macro is:
Bind arguments -> Evaluate body -> Evaluate expansion `-----------------------------´ (Expand)While the process of calling a macro is:
(macro (name) (list 'define name 123)) ⇒ <macro> (define my-macro (macro (name) (list 'define name 123))) ⇒ <macro> (my-macro some-name) ⇒ 123 (macroexpand '(my-macro some-name)) ⇒ (define some-name 123) some-name ⇒ 123
In the previous example, notice how we don’t have to quote
some-namewhen callingmy-macro. This is because, since macro arguments are not evaluated, the symbolsome-nameis passed to the macro, not the value bound to it. The macro is expanded to the list(define some-name 123), and then it’s evaluated.The special form
backquotecan be really useful in macros. See =backquote=.;; Without using backquote (defmacro my-macro (x y) (list 'if x (list 'func (list 'quote 'abc)) (list '+ '1 '2 y))) ;; Using backquote (defmacro my-macro (x y) `(if ,x (func 'abc) (+ 1 2 ,y)))
Just like lambdas, macros support the use of the
&restkeyword in the formal argument list.For more information on how macros behave in this Lisp, see the Emacs Lisp manual.
- Special Form: begin &rest exprs
- <<begin>>
Evaluate each argument in order, and return the last result.
This primitive is a special form for various reasons. When making a normal procedure call, the arguments are not required to be evaluated in order, when calling
begin, they are. The fact that it has to evaluate the expressions is helpful when combined with something likeapplyand a quoted expression (see =apply=).;; Arguments not evaluated because it's a special form. (begin (define n 123) (+ 1 2)) ⇒ 3 n ⇒ 123 ;; Arguments not evaluated because the list is quoted. (apply begin '((define n 456) (+ 1 2))) ⇒ 3 n ⇒ 456
Furthermore, it could be defined as a macro using
lambda, with some limitations. For example, in the following macro version, calls todefinewould bind the variables in thelambdaenvironment, which does not happen in the special form version.(defmacro my-begin (&rest exprs) `((lambda () ,@exprs))) ⇒ <macro> (my-begin (define my-var 123) ; Only defined in body 'ignored-sym (+ 1 2 3)) ⇒ 6 my-var ⇒ Unbound symbol: `my-var'.
- Special Form: if predicate consequent alternative
- <<if>>
Return evaluated consequent or alternative depending on whether or not predicate evaluated to non-nil or not, respectively. See also =nil= and =tru=.
(if tru 'abc 'xyz) ⇒ abc (if nil 'abc 'xyz) ⇒ xyz (if (> 5 3) (+ 10 20) (- 60 50)) ⇒ 30
Note that the predicate is always evaluated, but only the consequent or the alternative is evaluated afterwards. This is a good example on why special forms are necessary, since a normal function call would have to evaluate the 3 arguments before applying
ifto them. - Special Form: or &rest exprs
- <<or>>
Evaluates each argument expression in order, and once it finds a non-nil result, it stops evaluating and returns it. Returns
nilif all of them evaluated tonil, or when called with no arguments.(or (> 1 2) (> 3 4) (> 5 6)) ⇒ nil (or (> 1 2) (> 3 4) 'hello) ⇒ hello (or) ⇒ nil
Note that this primitive does not need to be a special form, since it can be built with a macro and
if. Note that I decided to useletfor readability, but we could have called a lambda instead[fn::For more information on the variants of this macro, see my blog article /Replacing conditional Lisp primitives with macros/.](defmacro my-or (&rest exprs) (if (null? exprs) nil ;; TODO: Don't overwrite "result", generate unique symbol. `(let ((result ,(car exprs))) (if result result (my-or ,@(cdr exprs))))))
- Special Form: and &rest exprs
- <<and>>
Evaluates each argument expression in order, and if it finds a
nilresult, it stops evaluating and returnsnil. If all arguments evaluated to non-nil, returns the last result. Returnstruwhen called with no arguments.(and (> 1 2) (> 3 4) (> 5 6)) ⇒ nil (and (> 4 3) (> 2 1) 'hello) ⇒ hello (and) ⇒ tru
Just like with
or, this primitive does not need to be a special form. Just like withlet, note that I usedcondfor readability, but you could nest twoifcalls instead.(defmacro my-and (&rest exprs) (cond ((null? exprs) tru) ((null? (cdr exprs)) (car exprs)) ;; TODO: Don't overwrite "result", generate unique symbol. (tru `(let ((result ,(car exprs))) (if result (my-and ,@(cdr exprs)) nil)))))
These primitives don’t fit into other categories. They are defined in prim_general.c.
- Function: eval expr
- <<eval>>
Evaluate the specified expression.
Different expression types have different evaluation rules:
- The empty list (
nil) evaluates to itself. - Non-empty lists are evaluated as procedure calls.
- If the (un-evaluated)
carof the list is a special form symbol (see *Special Forms), it passes the un-evaluatedcdrto the corresponding special form primitive. - If the (evaluated)
carof the list is a macro, the macro is called with the un-evaluatedcdrof the list. - Otherwise, the arguments are evaluated and the procedure is called. If one argument fails to evaluate, evaluation stops.
- If the (un-evaluated)
- Symbols evaluate to their bound values in the current environment[fn::See also Section 3.2 of Structure and Interpretation of Computer Programs.].
- Other expression types (numbers, strings, functions, etc.) evaluate to themselves.
Keep in mind that, since
evalis a normal procedure, its arguments will be evaluated before the actual function call is made, so the user might need to use thequotespecial form.(define var 123) ;; We are evaluating 123, which evaluates to itself. (eval var) ;; We are evaluating the symbol "var", which evaluates to 123. (eval (quote var))
The C primitive is called
prim_eval, but the actual evaluation process is performed by the C functioneval, defined in eval.c. - The empty list (
- Function: apply function arg-list
- <<apply>>
Apply a function to a list of arguments.
The first argument must be an applicable expression, that is, a Primitive, Lambda or Macro; and the second argument must be a list.
Again,
applyis a normal procedure, so its arguments will be evaluated before the call. However, even thought the user might need to quote the argument list, the first argument must be a procedure, not a symbol.(apply '+ '(1 2 3)) ⇒ Error: Expected a procedure as the first argument, got 'Symbol'. (apply + '(1 2 3)) ⇒ 6
Just like with
eval, the C primitive is calledprim_apply, but it’s just a wrapper for the C functionapply, defined in eval.c. It checks the type of thefunctionexpression, and dispatches the call to the appropriate function for performing the actual application process. For more information, see =lambda= and =macro=. - Function: macroexpand quoted-expr
- <<macroexpand>>
Expand
quoted-expr, a list representing a macro call. The evaluatedcarof the list must be an expression of type Macro. The expansion of a macro is the expression returned by that macro before being evaluated. The expansion step of a macro call is the same as a normal lambda call, but the arguments are not evaluated before calling it.(defmacro inc (sym) (list 'define sym (list '+ sym 1))) ⇒ <macro> ;; Alternative, using backquote (defmacro inc (sym) `(define ,sym (+ ,sym 1))) ⇒ <macro> (define my-var 5) ⇒ 5 (macroexpand '(inc my-var)) ⇒ (define my-var (+ my-var 1))
Notice how the macro body just returns a list. That is the macro expansion. Calling a macro simply means evaluating the expanded expression. See also =macro=.
- Function: set destination source
- <<set>>
Set the value of
destinationto the value ofsource, and return thedestination.Note that the value is copied, but the references themselves remain unchanged. In the following code, we bind the symbol
my-varto a list, and then we overwrite the value (and expression type) of the second element of the list. In other words, we are not overwriting the pointer in the list to point to a new expression, we are modifying the value that the pointer is currently pointing to.(define my-var (list 1 2 3)) ⇒ (1 2 3) (set (cadr my-var) 'foo) ⇒ foo my-var ⇒ (1 foo 3)
The following code shows another example that illustrates how the values are modified, not the references.
(define foo 123) (define bar 456) (set foo bar) ⇒ 456 (set bar 789) ⇒ 789 foo ⇒ 456
Keep in mind that this is not a special form, so the arguments are evaluated normally, before the call is made.
- Function: clone expr
- <<clone>>
Return a newly allocated clone of the specified expression, with the same value but in a different address.
(define foo 123) ⇒ 123 (define bar (clone foo)) ⇒ 123 (set foo 456) ⇒ 456 bar ⇒ 123
- Function: random limit
- <<random>>
Return a random number between zero and
limit. The argument type must be numeric, and the returned number will share the same type.(random 5) ⇒ 4 (random 5.0) ⇒ 2.261398 (type-of (random 1)) ⇒ Integer (type-of (random 1.0)) ⇒ Float
- Function: set-random-seed seed
- <<set-random-seed>>
Set the random seed to the specified integer argument. Returns
tru.(set-random-seed 1337) ⇒ tru (random 1000) ⇒ 136 (set-random-seed 1337) ⇒ tru (random 1000) ⇒ 136
These primitives are used to check for logical truth. They usually
return a predicate, that is, an expression whose value is meant to be
interpreted as either true or false. In SL, the empty list nil is used
to denote false, and other values denote true implicitly (see
=nil=). Usually, these functions return either nil or the explicit truth
symbol tru.
- Function: equal? a b &rest rest
- <<equal?>>
Return
truif the structure of all arguments is equal,nilotherwise. In other words, if they are isomorphic. As a rule of thumb, two expressions are isomorphic ifwrite-to-strreturns the same string for both of them (see =write-to-str=). Isomorphism for different types will be expanded below.The primitive doesn’t require arguments of the same type, but the equality will usually fail if they don’t share a common one.
Important exceptions:
- The symbol
niland the empty list()are interchangeable, and therefore equal. This is an exception, and is explained in more detail in =nil=.
Equality for different types:
- Two non-empty lists are equal if they have the same number of
elements, and if each expression in the first list is equal to the
corresponding expression in the second list, according to this
function
equal?. - Two numbers are equal according to this function if they share the same type, and if they have the same value. General numeric equality can be checked with ===.
- Two strings are equal if they have the same length, and if all of their characters match.
- Symbols are handled just like strings, but comparing the two types
will always returns
nil. - Two expressions of type Primitive are equal if they point to the same C function in memory.
- Two lambda functions are equal if they have the same number of
formals, their formals have the same names, and all of the
expressions in their body match according to this function
equal?. - Macros are handled just like lambdas, but, just like symbols and strings, they are not equal according to this function because they don’t share the same expression type.
Some examples:
(equal? 123 123) ⇒ tru (equal? 5 5.0) ⇒ nil (equal? 'abc "abc") ⇒ nil (defun foo (x) x) ⇒ <lambda> (equal? foo (lambda (x) x)) ⇒ tru (equal? foo (lambda (y) y)) ⇒ nil (defmacro bar (x) x) ⇒ <macro> (equal? foo bar) ⇒ nil
- The symbol
- Function: = a b &rest rest
- <<=>>
Returns
truif the value of all numeric arguments is equal,nilotherwise. The value of two numeric expressions is equal, according to this function, if their values are the same after being converted to a Generic Number Type. See Generic Number Type.Some examples:
(= 1 1) ⇒ tru (= 1 1.0) ⇒ tru (= 1 1.0 2) ⇒ nil
This function is a primitive because the conversion to the generic number type is done from C, but a similar function could be written using
equal?and type-conversion primitives:(defun my-num-equal (a b) (defun to-common-type (n) (cond ((flt? n) n) ((int? n) (int->flt n)) (tru (error "Invalid type.")))) (equal? (to-common-type a) (to-common-type b))) (my-num-equal 1 1.0) ⇒ tru (my-num-equal 1 2.0) ⇒ nil
- Function: < a b &rest rest
- <<lt>>
Return
truif all arguments are monotonically increasing, that is,$a<b<…<n$ ;nilotherwise. Predicates are therefore transitive, that is,$a<c$ .Just like with equality, two expressions will increase or decrease depending on their type. These are the different conditions required for two expressions to be increasing or decreasing:
- Two numbers are increasing or decreasing if the value of second is greater or smaller than the value of the first, respectively. Numbers can be compared if they don’t share the same type, but will be converted to a Generic Number Type, just like with ===.
- Two strings are increasing or decreasing if the first differing
character in the strings is greater or smaller on the second string
than on the first[fn::This is checked using the C function
strcmp.], respectively. - Symbols are handled just like strings, but comparing the two types
will always returns
nil. - Other expression types can’t be compared using this function.
Some examples:
(< 1 2) ; tru (< 10 20 30) ; tru (< 10 20 5) ; nil
- Function: > a b &rest rest
- <<gt>>
Return
truif all arguments are monotonically decreasing, that is,$a>b>…>n$ ;nilotherwise. Predicates are therefore transitive, that is,$a>c$ . For more information on this function, see =<=.Some examples:
(> 2 1) ; tru (> 30 20 10) ; tru (> 30 20 40) ; nil
These primitives are used for checking the type of an expression. Note
that most of these type? functions don’t need to be primitives, since we
could check the symbol returned by type-of. The primitives in this
section are defined in prim_type.c.
See also Types.
- Function: type-of expr
- <<type-of>>
Return a symbol representing the type of the specified expression.
(type-of 1) ⇒ Integer (type-of 1.0) ⇒ Float (type-of 'foo) ⇒ Symbol (type-of "Bar") ⇒ String (type-of '(a b c)) ⇒ Pair (type-of +) ⇒ Primitive (type-of (lambda (x) x)) ⇒ Lambda (type-of (macro (x) x)) ⇒ Macro
- Function: int? expr
- <<int?>>
Returns
truif the argument is an Integer number,nilotherwise.(int? 1) ⇒ tru (int? 1.0) ⇒ nil
- Function: flt? expr
- <<flt?>>
Returns
truif the argument is a Float number,nilotherwise.(flt? 1.0) ⇒ tru (flt? 1) ⇒ nil
- Function: symbol? expr
- <<symbol?>>
Returns
truif the argument is a Symbol,nilotherwise.(define foo 123) ⇒ 123 (symbol? 'foo) ⇒ tru (symbol? foo) ; 123 is checked ⇒ nil (symbol? "Bar") ⇒ nil
Note that
nilis a symbol that is bound to itself, so it’s a symbol even when not quoted.(symbol? 'nil) ⇒ tru (symbol? nil) ⇒ tru
- Function: string? expr
- <<string?>>
Returns
truif the argument is a String,nilotherwise.(string? "Foo") ⇒ tru (string? 'bar) ⇒ nil
- Function: pair? expr
- <<pair?>>
Returns
truif the argument is a Pair,nilotherwise.(pair? '(a . b)) ⇒ tru (pair? (+ 1 2)) ; Arguments are evaluated first, so 3 is checked ⇒ nil
Note that, since the
(a b c)list syntax is syntactic sugar for a chain of pairs, the following is true. See Lists.;; Syntactic sugar for (a . (b . (c . nil)) (pair? '(a b c)) ⇒ tru
Note that the empty list
()is converted to the symbolnilin the parser, so it’s not a pair. See nil.(pair? nil) ⇒ nil ;; Syntactic sugar for (quote nil) (pair? '()) ⇒ nil
- Function: list? expr
- <<list?>>
Returns
truif the argument is a proper list,nilotherwise. See Lists.(list? '(a b c)) ⇒ tru (list? '(a . (b . (c . nil)))) ⇒ tru (list? nil) ⇒ tru
Note that improper lists (i.e. lists whose last cdr is not the symbol
nil) are not considered valid to this function.(list? '(a b . c)) ⇒ nil
This function doesn’t need to be a primitive, it can be defined in Lisp.
(defun my-list? (e) (cond ((null? e) tru) ((pair? e) (my-list? (cdr e))) (tru nil))) (my-list? '(a b c)) ⇒ tru (my-list? '(a b . c)) ⇒ nil
- Function: primitive? expr
- <<primitive?>>
Returns
truif the argument is a C Primitive,nilotherwise.(primitive? +) ⇒ tru (defun foo (x) x) ⇒ <lambda> (primitive? foo) ⇒ nil
- Function: lambda? expr
- <<lambda?>>
Returns
truif the argument is a Lambda function,nilotherwise.(defun foo (x) x) ⇒ <lambda> (defmacro bar (x) x) ⇒ <macro> (lambda? foo) ⇒ tru (lambda? bar) ⇒ nil (lambda? +) ⇒ nil
- Function: macro? expr
- <<macro?>>
Returns
truif the argument is a Macro function,nilotherwise.(defun foo (x) x) ⇒ <lambda> (defmacro bar (x) x) ⇒ <macro> (lambda? foo) ⇒ nil (lambda? bar) ⇒ tru (lambda? +) ⇒ nil
These primitives are used for converting between expression types. The primitives in this section are defined in prim_type.c.
- Function: int->flt expr
- <<int-to-flt>>
Converts the specified Integer into a Float.
(int->flt 1) ⇒ 1.000000
- Function: flt->int expr
- <<flt-to-int>>
Converts the specified Float into an Integer.
(flt->int 1.0) ⇒ 1
- Function: int->str expr
- <<int-to-str>>
Converts the specified Integer into a String. See also =write-to-str=.
(int->str 1) ⇒ "1"
- Function: flt->str expr
- <<flt-to-str>>
Converts the specified Float into a String.
(flt->str 1.0) ⇒ "1.000000"
- Function: str->int expr
- <<str-to-int>>
Converts the specified String into an Integer.
(str->int "1") ⇒ 1 (str->int "1abc") ⇒ 1 (str->int "abc1") ; Invalid input ⇒ 0
- Function: str->flt expr
- <<str-to-flt>>
Converts the specified String into a Float.
(str->flt "1.0") ⇒ 1.000000 (str->flt "1.0abc") ⇒ 1.000000 (str->flt "1") ⇒ 1.000000 (str->flt "1abc") ⇒ 1.000000 (str->flt "abc1") ; Invalid input ⇒ 0.000000
These primitives are related to the construction, modification and information of lists. The primitives in this section are defined in prim_list.c.
- Function: list &rest exprs
- <<list>>
Construct a list from the specified arguments. All elements remain in the top level, even if they are other lists.
(list 1 2 3) ⇒ (1 2 3) (list 'a '(b c) 'd) ⇒ (a (b c) d) (list 'a 'b '() nil) ⇒ (a b nil nil)
This function doesn’t need to be a primitive, since we could just use
cons:(defun my-list (&rest elts) (if (null? elts) nil (cons (car elts) (apply my-list (cdr elts))))) (my-list 'a 'b 'c) ⇒ (a b c) ;; Alternatively, taking advantage of '&rest'. (defun my-list (&rest elts) elts)
- Function: cons a b
- <<cons>>
Construct a new Pair whose car is
aand whose cdr isb=[fn::For more information on the history of =cons, see John McCarthy (1979) /History of Lisp/].(cons 'a 'b) ⇒ (a . b) (cons 'a nil) ⇒ (a) (cons 'a '(b c d)) ⇒ (a b c d) (cons '(a b) '(c d)) ⇒ ((a b) c d)
- Function: car pair
- <<car>>
Return the first element of the specified /Pair/[fn::For historical reasons,
carstands for “Contents of the Address (part) of Register”.]. Since lists are built using pairs, thecarof a list is its first element.The
carofnilis alwaysnil, even though it is a symbol. See nil.(car '(a . b)) ⇒ a (car '(a b c)) ⇒ a (car '((a b) c d)) ⇒ (a b) (car nil) ; Special case ⇒ nil
- Function: cdr pair
- <<cdr>>
Return the second element of the specified /Pair/[fn::For historical reasons,
cdrstands for “Contents of the Decrement (part) of Register”.]. Since lists are built using pairs, thecdrof a list is the part of the list that follows the first element[fn::In other languages like Haskell, the function for removing the first element of a list is calledtail. Note, however, that lists in Lisp are built using pairs, and that thecdrfunction only operates with pairs.].The
cdrofnilis alwaysnil, even though it is a symbol. See nil.(cdr '(a . b)) ⇒ b (cdr '(a b c)) ⇒ (b c) (cdr '((a b) c d)) ⇒ (c d) (cdr '(a (b c) d)) ⇒ ((b c) d) (cdr nil) ; Special case ⇒ nil
- Function: nth position list
- <<nth>>
Return the element at the specified position in a list. The
positionargument is one-indexed, and it must be smaller or equal than the length of the list.(nth 1 '(a b c)) ⇒ a (nth 2 '(a b c)) ⇒ b
This function does not need to be a primitive, and it could be defined in Lisp. The following version is not optimized, since we could check if the
positionis within bounds only once (instead of every iteration).(defun my-nth (position lst) (cond ((= position 1) (car lst)) ((or (< position 1) (> position (length lst))) (error "Invalid position.")) (tru (my-nth (- position 1) (cdr lst))))) (my-nth 2 '(a b c)) ⇒ b
- Function: length sequence
- <<length>>
Return the number of elements in a sequence, that is, a proper list or a String.
(length '(a b c)) ⇒ 3 (length "abc") ⇒ 3 (length nil) ⇒ 0 (length "") ⇒ 0
This function needs to be a primitive for getting the length of the strings, but we could write our own Lisp function for calculating the length of a list:
(defun my-list-length (list) (if (null? list) 0 (+ 1 (my-list-length (cdr list))))) (my-list-length '(1 2 3)) ⇒ 3
- Function: append &rest sequences
- <<append>>
Attach one sequence to another, that is, a proper list or String. Note that all arguments must share the same type, so you can’t append a list to a string.
(append '(1 2 3) '(a b c) '(4 5 6)) ⇒ (1 2 3 a b c 4 5 6) (append '(a b c)) ⇒ (a b c) (append "foo" "bar") ⇒ "foobar"
This function doesn’t modify its arguments directly, it returns a new list.
(define my-list '(a b c)) ⇒ (a b c) (append my-list '(x y z)) ⇒ (a b c x y z) my-list ⇒ (a b c)
When called with no arguments,
appendreturnsnil.(append) ⇒ nil
This function does not need to be a primitive, and it could be defined in Lisp. In the following example, we would need to use a
make-copyprimitive, sinceappenddoesn’t modify (i.e. it doesn’t cons) its arguments directly.(defun make-copy (expr) ;; ... expr) (defun my-append (&rest lists) (defun my-append-two (a b) (if (null? a) b (cons (car a) (my-append-two (cdr a) b)))) (if (null? lists) nil (my-append-two (make-copy (car lists)) (apply my-append (cdr lists))))) (my-append '(a b) '(c) '(d e)) ⇒ (a b c d e)
These primitives are related to the construction, modification and information of strings. The primitives in this section are defined in prim_string.c.
Note that some functions in List-related primitives operate on sequences in general, not just lists, so they can be used with strings.
- Function: write-to-str expr
- <<write-to-str>>
Returns a string that represents the specified expression. The format of the returned string must contain enough information to be parsed into the original expression using =read=.
See also =write=.
(write-to-str 1) ⇒ "1" (write-to-str 'hello) ⇒ "hello" (write-to-str (lambda (x) (* x 2))) ⇒ "(lambda (x) (* x 2))" (write-to-str "Hello, world\n") ⇒ "\"Hello, world\\n\""
It might be a bit hard to understand what is really escaped, and what is only escaped “visually”. First, note that the user input is “un-escaped” by the lexer, so the interpreter always works with the real string (i.e. the interpreter would write
0xAto the internal string, not[0x5C, 0x6E]). Then, sincewrite-to-strmust return a valid string forread, it manually escapes it, normally resulting in what the user typed in the first place. However, note that the print step of the REPL also escapes strings before printing them (that’s what I meant by “only escaped visually”). To view the “raw” output ofwrite-to-str, it’s best to use something likeprint-str(See =print-str=).(begin (print-str (write-to-str "Hello, world\n")) (print-str "\n") (print-str "\"Hello, world\\n\"") ; Returned (print-str "\n") 'done) → "Hello, world\n" → "Hello, world\n" ⇒ done
- Function: format format-string &rest exprs
- <<format>>
Returns a string with the specified format. This function is similar to C’s
sprintf(3).The
formatfunction produces a string from theformat-string, copying all characters literally, except the percent sign%, which is used to indicate the start of a format specifier. Format specifiers are used to indicate how its corresponding expression (obtained from theexprslist) should be converted and appended to the final string.This function expects the number of
exprsto match the format specifiers in theformat-string; the function will fail if the user didn’t supply enough arguments, but will not check if the user supplied more. Furthermore, the function will make sure that each supplied argument matches the type required by the format specifier.These are the currently supported format specifiers:
s- Format an expression of type String. Each character is printed
literally, nothing is escaped, similar to
print-str. d- Format an expression of type Integer.
u- Format an expression of type Integer as unsigned.
x- Format an expression of type Integer as unsigned, in
hexadecimal format with a
0xprefix. f- Format an expression of type Float.
%- Used to represent the literal percent sign
%. This format specifier does not need a matching expression in theexprslist.
The function will fail if the user supplied an unknown format specifier.
(format "%s, %s!" "Hello" "world") ⇒ "Hello, world!" (format "%d / %d = %d (%f)" 5 2 (quotient 5 2) (/ 5 2)) ⇒ "5 / 2 = 2 (2.500000)"
- Function: substring string &optional from to
- <<substring>>
Return a new string whose contents are a substring of
string. Paraphrasing the Emacs Lisp manual:The returned string consists of the characters between index
from(inclusive) and indexto(exclusive) ofstring. Thefromandtoarguments are zero-indexed: 0 means the first character ofstring.Negative values are counted from the end of
string, so -1 represents the last character in the string.If
fromis nil, the substring starts at index 0; and iftois nil, the substring runs to the end ofstring.Some examples:
(substring "abcdef") ⇒ "abcdef" (substring "abcdef" 0 2) ⇒ "ab" (substring "abcdef" 1 nil) ⇒ "bcdef" (substring "abcdef" -1 nil) ⇒ "f" (substring "abcdef" 1 -1) ⇒ "bcde" (substring "abcdef" -3 -1) ⇒ "de"
- Function: re-match-groups regexp string &optional ignore-case
- <<re-match-groups>>
Try to match every group in
regexpagainststring, and return a list with the matches. Each match in the returned list is a Pair with the form(START . END), whereSTARTandENDare integers that indicate the start and end index of that match inside thestring. Therefore, the structure of the returned list is:((START . END) (START . END) ... (START . END))
The first match in the returned list corresponds to the entire
regexp, and the remaining elements correspond to each parenthesized group, if any. If theregexpdidn’t matchstring, the function returnsnil.By default, the search is case-sensitive, but this can be overwritten by specifying a non-nil argument for the optional parameter
ignore-case=[fn::When non-nil, the C function =regcompis called with theREG_ICASEflag. For more information, see the manual page for =regcomp=.].The function uses POSIX regular expression syntax, more specifically Extended Regular Expression (ERE) syntax[fn::For more information, see IEEE Std 1003.1, Section 9, /Regular Expressions/; and the
sedmanual, /Overview of extended regular expression syntax/ as well as /Character Classes and Bracket Expressions/.].Some examples:
(define str "abc XYZ 123") (re-match-groups "abc" str) ⇒ ((0 . 3)) (re-match-groups "xyz" str) ⇒ nil (re-match-groups "xyz" str tru) ⇒ ((4 . 7)) (re-match-groups "^(abc) ([A-Z]+) ([[:digit:]]+)$" str) ⇒ ((0 . 11) (0 . 3) (4 . 7) (8 . 11))
Note that this function only returns information about the first match of
regexpinstring, not about all the possible matches:;; Not ((0 . 1) (1 . 2) (2 . 3)) (re-match-groups "a" "aaa") ⇒ ((0 . 1))
Also note that, since any non-nil argument can be used for the
ignore-caseparameter, sometimes it might be a good idea to use a descriptive symbol:(re-match-groups "abc" "ABC" 'ignore-case) ⇒ ((0 . 3))
These primitives are used for performing arithmetical operations on numbers. The primitives in this section are defined in prim_arith.c. See also Bit-wise primitives.
- Function: + &rest numbers
- <<+>>
Add the specified numbers. If the arguments don’t share a common type, they are converted to a common type (see Generic Number Type). Returns 0 when called with no arguments.
(+) ⇒ 0 (+ 1 2 3) ⇒ 6 (+ 1 2.0 3) ⇒ 6.000000
- Function: - &rest numbers
- <<->>
Subtract the specified numbers in order. If the arguments don’t share a common type, they are converted to a common type (see Generic Number Type). When called with just one argument, it’s negated. Returns 0 when called with no arguments.
(-) ⇒ 0 (- 5) ⇒ -5 (- 5 2 1) ⇒ 2 (- 5 2.0 1) ⇒ 2.000000
- Function: * &rest numbers
- <<*>>
Multiply the specified numbers. If the arguments don’t share a common type, they are converted to a Generic Number Type. Returns 1 when called with no arguments.
(*) ⇒ 1 (* 1 2 3) ⇒ 6 (* 1 2.0 3) ⇒ 6.000000
- Function: / dividend &rest divisors
- <</>>
Divide the
dividendby each divisor in order. The arguments are always converted to a common type, even if the arguments share a common type (see Generic Number Type). For integer division, see =quotient=. Trying to divide by zero results in an error.(/ 10) ⇒ 10.000000 (/ 10 2) ⇒ 5.000000 (/ 10 0) ⇒ Error: Trying to divide by zero. (/ 10 3) ⇒ 3.333333 (/ 10 2 2) ⇒ 2.500000
- Function: mod dividend &rest divisors
- <<mod>>
Return the modulus of
dividendby each divisor in order. Just like/, this function converts all arguments to a common type before operating on them (see Generic Number Type). This function allows floating-point and negative inputs[fn::For more details on a possible implementation of a floating-pointmod, see the article on my blog.]. Trying to divide by zero results in an error.Similarly to how the Emacs Lisp manual describes
mod, the following expression should be equal to thedividend:(+ (mod dividend divisor) (* (floor (/ dividend divisor)) divisor))
Note that, although the behavior of the
modfunction is the same in SL and in Emacs Lisp, the behavior of thefloorand/functions is not. See =floor=.Some examples of
mod:(mod 10) ⇒ 10.000000 (mod 10 2) ⇒ 0.000000 (mod 10 3) ⇒ 1.000000
- Function: quotient dividend &rest divisors
- <<quotient>>
Divide the
dividendby each divisor in order. Unlike/, this function only operates with integers. Trying to divide by zero results in an error.(quotient 10) ⇒ 10 (quotient 10 2) ⇒ 5 (quotient 10 0) ⇒ Error: Trying to divide by zero. (quotient 10 3) ⇒ 3
The behavior is identical to integer division in C, that is, the result is always truncated towards zero; in other words, rounded towards the smallest absolute value. Dividing using
quotientis not the same as usingflooron a floating point/division:(floor (/ -5 2)) ⇒ -3.000000 (quotient -5 2) ⇒ -2
- Function: remainder dividend &rest divisors
- <<remainder>>
Return the remainder of
dividendby each divisor in order. Unlikemod, this function only operates with integers. Trying to divide by zero results in an error.The
remainderfunction in SL works like theremainderfunction in Scheme. The following expression should be equal to thedividend:(+ (remainder dividend divisor) (* (quotient dividend divisor) divisor))
Again, note the difference between
(floor (/ ...))and(quotient ...). See =quotient= and =floor=.Some examples of
remainder:(remainder 10) ⇒ 10 (remainder 10 2) ⇒ 0 (remainder 10 3) ⇒ 1
The behavior of this function is identical to the
%operator in C. Note that, in SL, the remainder and the modulo of two numbers is not the same:(remainder -5 2) ⇒ -1 (mod -5 2) ⇒ 1.000000
- Function: round number
- <<round>>
Round
numberto nearest integer. Halfway cases are rounded away from zero. The type of the returned value always matches the type of the input.(round 5) ⇒ 5 (round 5.3) ⇒ 5.000000 (round 5.5) ⇒ 6.000000 (round 5.6) ⇒ 6.000000 (round -5.3) ⇒ -5.000000 (round -5.5) ⇒ -6.000000 (round -5.6) ⇒ -6.000000
- Function: floor number
- <<floor>>
Return the largest integral value not greater than
number. In other words, round the specifiednumbertowards negative infinity. The type of the returned value always matches the type of the input.(floor 5) ⇒ 5 (floor 5.0) ⇒ 5.000000 (floor 5.7) ⇒ 5.000000 (floor -5.0) ⇒ -5.000000 (floor -5.7) ⇒ -6.000000
Note how
floordoes not round towards zero for negative values. See also =truncate=. - Function: ceiling number
- <<ceiling>>
Return the smallest integral value not less than
number. In other words, round the specifiednumbertowards positive infinity. The type of the returned value always matches the type of the input.(ceiling 5) ⇒ 5 (ceiling 5.0) ⇒ 5.000000 (ceiling 5.3) ⇒ 6.000000 (ceiling -5.0) ⇒ -5.000000 (ceiling -5.3) ⇒ -5.000000
- Function: truncate number
- <<truncate>>
Round the specified
numberto an integer, towards zero. In other words, return thenumberwith the fractional part set to zero. The type of the returned value always matches the type of the input.(truncate 5) ⇒ 5 (truncate 5.3) ⇒ 5.000000 (truncate 5.6) ⇒ 5.000000 (truncate -5.3) ⇒ -5.000000 (truncate -5.6) ⇒ -5.000000
These primitives are related to the manipulation of bits. The primitives in this section are defined in prim_bitwise.c.
- Function: bit-and integer &rest rest
- <<bit-and>>
Perform a bit-wise and operation with each integer argument. That is, each bit in the result is set if that bit was set in all of the arguments.
0b11110000 0xF0 240 0b11001100 0xCC 204 ---------- ---- --- (AND) 0b11000000 0xC0 192Some examples:
(bit-and 0xF0 0xCC) ⇒ 192 (bit-and 0xF0 0xCC 0x03) ⇒ 0
- Function: bit-or integer &rest rest
- <<bit-or>>
Perform a bit-wise or operation with each integer argument. That is, each bit in the result is set if that bit was set in at least one of the arguments.
0b11110000 0xF0 240 0b11001100 0xCC 204 ---------- ---- --- (OR) 0b11111100 0xFC 252Some examples:
(bit-or 0xF0 0xCC) ⇒ 252 (bit-or 0xF0 0xCC 0x03) ⇒ 255
- Function: bit-xor integer &rest rest
- <<bit-xor>>
Perform a bit-wise xor operation with each integer argument. That is, each bit in the result is set if that bit was set in an odd number of arguments.
0b11110000 0xF0 240 0b11001100 0xCC 204 ---------- ---- --- (XOR) 0b00111100 0x3C 60Some examples:
(bit-xor 0xF0 0xCC) ⇒ 60 (bit-xor 0xF0 0xCC 0x03) ⇒ 63
- Function: bit-not integer
- <<bit-not>>
Perform a bit-wise not operation on
integer. That is, if a bit was set (1) in the input, it becomes unset; and if the bit was unset (0), it becomes set.0b11001100 0xCC 204 ---------- ---- --- (NOT) 0b00110011 0x33 51Some examples:
(bit-not 0xCC) ⇒ -205 ; Shown as signed (format "%x" (bit-not 0xCC)) ⇒ "0xffffffffffffff33" (bit-not 0) ⇒ -1 (format "%x" (bit-not 0)) ⇒ "0xffffffffffffffff"
- Function: shr integer n-bits
- <<shr>>
Shift the specified
integern bits to the right. Sets the high-order bits to zero. It is equivalent to the>>C operator, and to theshrx86assembly instruction[fn::It is not equivalent to therorinstruction.].Some examples:
(shr 0xF0 4) ⇒ 15 (format "%x" (shr 0xF0 4)) ⇒ "0xf" (shr 0xF0 6) ⇒ 3 (format "%x" (shr 0xF0 6)) ⇒ "0x3"
- Function: shl integer n-bits
- <<shl>>
Shift the specified
integern bits to the left. Sets the low-order bits to zero. It is equivalent to the<<C operator, and to theshlx86assembly instruction[fn::It is not equivalent to therolinstruction.].Some examples:
(shl 0x0F 4) ⇒ 240 (format "%x" (shl 0x0F 4)) ⇒ "0xf0" (shl 0x0F 6) ⇒ 960 (format "%x" (shl 0x0F 6)) ⇒ "0x3c0"
These primitives are related to reading and writing data to the outside world. They are defined in prim_io.c.
- Function: read
- <<read>>
Read a single expression from the standard input, parse it, and return it as a Lisp expression. It’s the first step in the REPL, which consists of reading a string from the standard input, tokenizing it, and parsing it into a Lisp expression.
In the following example, note that the inputs are shown literally, so in the input
"Hello\nWorld\n", the user typed the quotes,\andn.(read) ;; Input: foo bar ⇒ foo (type-of (read)) ;; Input: foo ⇒ Symbol (read) ;; Input: "Hello\nWorld\n" ⇒ "Hello\nWorld\n" (print-str (read)) ;; Input: "Hello\nWorld\n" → Hello → World (eval (read)) ;; Input: (+ 1 2) ⇒ 3
Note the difference with =scan-str=.
- Function: write expr
- <<write>>
Write an expression in such a way that it can be parsed into the original expression using =read=. This function returns
truon success. For more information, see =write-to-str=.(write 123) → 123 ⇒ tru (write 'sym) → sym ⇒ tru (write "foo\nbar") → "foo\nbar" ⇒ tru (write "foo bar baz") → "foo\nbar\nbaz" ⇒ tru
- Function: scan-str &optional delimiters
- <<scan-str>>
Read user input into a string. This function reads from the standard input until one of the following is found:
- End-of-file (
EOF). - Null character (
'\0'). - A character in the
delimitersstring.
By default, the
delimitersstring is"\n", so the function stops reading as soon as the received character is a newline. Note that the final delimiter is not included in the returned string.Unlike
read, this function doesn’t read or parse a single Lisp expression. See =read=.In the following example,
<RET>and<TAB>are used to indicate that the user pressed the return and tab keys, respectively.(scan-str) ;; Input: foo<RET> ⇒ "foo" (type-of (scan-str)) ;; Input: foo<RET> ⇒ String (scan-str) ;; Input: Hello<TAB>World<RET> ⇒ "Hello\tWorld" (scan-str "._-\n") ;; Input: Hello. World.<RET> ⇒ "Hello"
- End-of-file (
- Function: print-str string
- <<print-str>>
Print the specified string literally to standard output. Returns its argument.
(print-str "Hello, world.\n") → Hello, world. ⇒ "Hello, world.\n" (print-str "I am \"escaping\" the quotes...\n") → I am "escaping" the quotes... ⇒ "I am \"escaping\" the quotes...\n"
Unlike
write, it only operates on strings, does not print the double-quotes, and doesn’t escape anything implicitly.(print-str "123 \"abc\" 456\n") → 123 "abc" 456 ⇒ "123 \"abc\" 456\n" (write "123 \"abc\" 456\n") → "123 \"abc\" 456\n" ⇒ tru
Note that
writeis doing the escaping before printing;print-strdoesn’t “un-escape” anything, the user input is converted by the lexer. See =write-to-str=. - Function: error string
- <<error>>
TODO
TODO
TODO
- Function: trace function
- <<trace>>
TODO