Getting the manual going
This commit is contained in:
+15
-34
@@ -5,26 +5,12 @@
|
||||
(define-syntax (unless condition :rest body)
|
||||
`(if ,condition nil ,(pair begin body)))
|
||||
|
||||
;; (define-syntax defun (name arguments :rest body)
|
||||
;; ;; (type-assert arguments :pair)
|
||||
;; ;; `(define ,name (lambda ,arguments ,body))
|
||||
;; ;; TODO(Felix: I think we do not need to wrap the body of the lamba
|
||||
;; ;; in a begin
|
||||
|
||||
;; ;; see if we have a docstring
|
||||
;; (if (and (= (type (first body)) :string) (not (= (type (rest body)) :nil)))
|
||||
;; (list 'define name (list 'lambda arguments (first body) (pair 'begin (rest body))))
|
||||
;; (list 'define name (list 'lambda arguments (pair 'begin body)))))
|
||||
|
||||
|
||||
;; (define-syntax defspecial (name arguments :rest body)
|
||||
;; ;; (type-assert arguments :pair)
|
||||
;; ;; `(define ,name (lambda ,arguments ,body))
|
||||
|
||||
;; ;; see if we have a docstring
|
||||
;; (if (and (= (type (first body)) :string) (not (= (type (rest body)) :nil)))
|
||||
;; (list 'define name (list 'special-lambda arguments (first body) (pair 'begin (rest body))))
|
||||
;; (list 'define name (list 'special-lambda arguments (pair 'begin body)))))
|
||||
(define-syntax (n-times times action)
|
||||
"Executes action times times."
|
||||
(define (repeat times elem)
|
||||
(unless (> 1 times)
|
||||
(pair elem (repeat (- times 1) elem))))
|
||||
(pair 'begin (repeat times action)))
|
||||
|
||||
;; (define (fib n))
|
||||
;; (define-syntax define (name :rest value)
|
||||
@@ -41,7 +27,6 @@
|
||||
;; (print "\nbb\n")
|
||||
;; (pair 'define (pair name value)))))
|
||||
|
||||
;; TODO(Felix): else symbol
|
||||
(define-syntax (cond :rest clauses)
|
||||
(define (rec clauses)
|
||||
(if (= nil clauses)
|
||||
@@ -122,13 +107,6 @@ the (rest) of the last element of the sequence."
|
||||
with (pair elem nil)."
|
||||
(extend seq (pair elem nil)))
|
||||
|
||||
(define-syntax (extend! seq elem)
|
||||
"test"
|
||||
`(mutate ,seq (extend ,seq ,elem)))
|
||||
|
||||
(define-syntax (append! seq elem)
|
||||
`(mutate ,seq (append ,seq ,elem)))
|
||||
|
||||
(define (length seq)
|
||||
"Returns the length of the given sequence."
|
||||
(if (nil? seq)
|
||||
@@ -143,11 +121,6 @@ with (pair elem nil)."
|
||||
"Subtracts one from the argument."
|
||||
(- val 1))
|
||||
|
||||
;; (defmacro n-times (@times @action)
|
||||
;; "Executes @action @times times."
|
||||
;; (unless (<= (eval @times) 0)
|
||||
;; (eval @action)
|
||||
;; (apply n-times (list (list - @times 1) @action))))
|
||||
|
||||
;; (defmacro for (@symbol @from @to :rest @for-body)
|
||||
;; "Designed to resemble a C style for loop. It takes a symbol as
|
||||
@@ -226,6 +199,14 @@ added to a list, which in the end is returned."
|
||||
nil
|
||||
(pair (list (first l1) (first l2)) (zip (rest l1) (rest l2)))))
|
||||
|
||||
(define (enumerate seq)
|
||||
(define (enumerate-inner seq next-num)
|
||||
(when seq
|
||||
(pair (list (first seq) next-num)
|
||||
(enumerate-inner (rest seq) (+ 1 next-num)))))
|
||||
(enumerate-inner seq 0))
|
||||
|
||||
|
||||
(define (printf :keys sep :defaults-to " " end :defaults-to "\n" :rest args)
|
||||
"A wrapper for the built-in (print) that accepts a variable number
|
||||
of arguments and also provides keywords for specifying the printed
|
||||
@@ -244,4 +225,4 @@ las argument."
|
||||
(eval (pair printf-quoted (extend (list :@sep (eval sep) :@end (eval end)) args))))
|
||||
|
||||
(define-syntax (pe expr)
|
||||
`(printf ',expr "evaluates to" ,(eval expr)))
|
||||
`(printf ',expr "evaluates to" ,expr))
|
||||
|
||||
@@ -29,16 +29,16 @@ the (rest) of the last element of the sequence." (if (pair? seq) (begin (define
|
||||
(define (append seq elem) "Appends an element to a sequence, by extendeing the list
|
||||
with (pair elem nil)." (extend seq (pair elem nil)))
|
||||
|
||||
(define (length seq) "Returns the length of the given sequence." (if (nil? seq) 0.000000 (+ 1.000000 (length (rest seq)))))
|
||||
(define (length seq) "Returns the length of the given sequence." (if (nil? seq) 0 (+ 1 (length (rest seq)))))
|
||||
|
||||
(define (increment val) "Adds one to the argument." (+ val 1.000000))
|
||||
(define (increment val) "Adds one to the argument." (+ val 1))
|
||||
|
||||
(define (decrement val) "Subtracts one from the argument." (- val 1.000000))
|
||||
(define (decrement val) "Subtracts one from the argument." (- val 1))
|
||||
|
||||
(define (range :keys from :defaults-to 0.000000 to) "Returns a sequence of numbers starting with the number defined
|
||||
by the key 'from' and ends with the number defined in 'to'." (if (< from to) ([C-function] (pair from (range :from (+ 1.000000 from) :to to))) nil))
|
||||
(define (range :keys from :defaults-to 0 to) "Returns a sequence of numbers starting with the number defined
|
||||
by the key 'from' and ends with the number defined in 'to'." (if (< from to) ([C-function] (pair from (range :from (+ 1 from) :to to))) nil))
|
||||
|
||||
(define (range-while :keys from :defaults-to 0.000000 to) "Returns a sequence of numbers starting with the number defined
|
||||
(define (range-while :keys from :defaults-to 0 to) "Returns a sequence of numbers starting with the number defined
|
||||
by the key 'from' and ends with the number defined in 'to'." (define result (list (copy from))) (define head result) (mutate from (increment from)) (while (< from to) (begin (mutate head (pair (first head) (pair (copy from) nil))) (define head (rest head)) (mutate from (increment from)))) result)
|
||||
|
||||
(define (map fun seq) "Takes a function and a sequence as arguments and returns a new
|
||||
@@ -63,6 +63,8 @@ added to a list, which in the end is returned." (if seq ([C-function] (if (fun (
|
||||
|
||||
(define (zip l1 l2) (if (and (nil? l1) (nil? l2)) nil (pair (list (first l1) (first l2)) (zip (rest l1) (rest l2)))))
|
||||
|
||||
(define (enumerate seq) (define (enumerate-inner seq next-num) (if seq ([C-function] (pair (list (first seq) next-num) (enumerate-inner (rest seq) (+ 1 next-num)))) nil)) (enumerate-inner seq 0))
|
||||
|
||||
(define (printf :keys sep :defaults-to " " end :defaults-to "
|
||||
" :rest args) "A wrapper for the built-in (print) that accepts a variable number
|
||||
of arguments and also provides keywords for specifying the printed
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
(define (make-vector3 _x _y _z) "This is the handle to an object of the class vector3" (let ((x _x) (y _y) (z _z)) (define (get-x) x) (define (get-y) y) (define (get-z) z) (define (set-x new-x) (mutate x new-x)) (define (set-y new-y) (mutate y new-y)) (define (set-z new-z) (mutate z new-z)) (define (length) (** (+ (* x x) (* y y) (* z z)) 0.500000)) (define (scale fac) (mutate x (* fac x)) (mutate y (* fac y)) (mutate z (* fac z)) fac) (define (add other) (make-vector3 (+ x (other get-x)) (+ y (other get-y)) (+ z (other get-z)))) (define (subtract other) (make-vector3 (- x (other get-x)) (- y (other get-y)) (- z (other get-z)))) (define (scalar-product other) (+ (* x (other get-x)) (* y (other get-y)) (* z (other get-z)))) (define (cross-product other) (make-vector3 (- (* y (other get-z)) (* z (other get-y))) (- (* z (other get-x)) (* x (other get-z))) (- (* x (other get-y)) (* y (other get-x))))) (define (print) (printf :sep "" "[vector3] (" x y z ")")) (type-wrap (special-lambda (message :rest args) "This is the docs for the handle" (eval (extend (list message) args))) :vector3)))
|
||||
|
||||
(define v1 (make-vector3 1.000000 2.000000 3.000000))
|
||||
(define v1 (make-vector3 1 2 3))
|
||||
|
||||
(define v2 (make-vector3 3.000000 2.000000 1.000000))
|
||||
(define v2 (make-vector3 3 2 1))
|
||||
|
||||
(assert (= (type v1) (type v2) :vector3))
|
||||
|
||||
(assert (= (v1 scalar-product v2) 10.000000))
|
||||
(assert (= (v1 scalar-product v2) 10))
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
(define (make-counter) (let ((var 0.000000)) (lambda () (mutate var (+ 1.000000 var)) var)))
|
||||
(define (make-counter) (let ((var 0)) (lambda () (mutate var (+ 1 var)) var)))
|
||||
|
||||
(define counter1 (make-counter))
|
||||
|
||||
(assert (= (counter1) 1.000000))
|
||||
(assert (= (counter1) 1))
|
||||
|
||||
(define counter2 (make-counter))
|
||||
|
||||
(assert (= (counter2) 1.000000))
|
||||
(assert (= (counter2) 1))
|
||||
|
||||
(assert (= (counter2) 2.000000))
|
||||
(assert (= (counter2) 2))
|
||||
|
||||
(assert (= (counter1) 2.000000))
|
||||
(assert (= (counter1) 2))
|
||||
|
||||
(assert (= (counter1) 3.000000))
|
||||
(assert (= (counter1) 3))
|
||||
|
||||
(assert (= (counter2) 3.000000))
|
||||
(assert (= (counter2) 3))
|
||||
|
||||
|
||||
+515
-4
@@ -1,11 +1,516 @@
|
||||
#+title: The Slime 1.0 Manual
|
||||
|
||||
#+begin_abstract
|
||||
sad
|
||||
abstract
|
||||
#+end_abstract
|
||||
|
||||
\tableofcontents
|
||||
|
||||
* Lisp languages
|
||||
Lisp is not one language but rather a family of programming languages. The family is devided by some
|
||||
characteristics. There are Lisp-1 and Lisp-2 dialects and there is a difference between a Lisp with
|
||||
lexical scoping as opposed to dynamic scoping. These differences will be explained in later
|
||||
sections.
|
||||
|
||||
The Lisp language family is known to be highly flexible and applicable in all areas by creating
|
||||
domain specific languages in Lisp itself through a powerful macro system. The central data structure
|
||||
in Lisp is the list. The reason why lisp is so powerful is because the program source code itself is
|
||||
represented as lists. The nested lists make up the syntax tree of the lisp program. It is therfore
|
||||
computationally easy to parse lisp programs as the source code itself is already structured in the
|
||||
form of the syntax tree; allowing for parsing in linear time.
|
||||
|
||||
The macro system in Slime works by recognizing macros at parse-time and running them, and replacing
|
||||
the macro call in the program code with the return value of the macro and then checking if further
|
||||
macros have to be expanded in the replaced code. Therefore the macros can be used to pre-compute
|
||||
values or rewrite expressions (creating syntactic sugar) or themselves define macros.
|
||||
|
||||
* Lists
|
||||
As mentioned in [[Lisp languages]], the central data structure in all Lisps is the list. Lists are
|
||||
implemented as singly linked lists, made up of pairs (historically called =cons-cells=), each pair
|
||||
has two slots, the =first= and the =rest= (historically =car= and =cdr=). A linked lsit ist then
|
||||
constructed by the convention that the =first= field of a pair points to the first element of the
|
||||
list and the =rest= field points to the rest of the list. Following this description, the list is a
|
||||
recursive data structure. For the end of the list a special value =nil= is used in the =rest= field.
|
||||
|
||||
A helpful way to visualize lists made up of pairs is using box diagrams. A simple box diagram can be
|
||||
seen in [[simpleBoxDiagram]]. Each rectangle is divided in two. The left part represents the =first=
|
||||
field, the right part represents the =rest=. The arrows point to the values in these fields.
|
||||
|
||||
The diagram in [[simpleBoxDiagram]] shows a simple list containing the values 1, 2 and 3. The first pair
|
||||
stores the number 1 its =first= field and the =rest= points to the rest of the list. The last pair
|
||||
points to the special value =nil= in its =rest= to denote the end of the list.
|
||||
|
||||
{{{ditaa_header}}}
|
||||
#+begin_src ditaa :file diagrams/list123.eps
|
||||
+-----+-----+ +-----+-----+ +-----+-----+
|
||||
| | | | | | | | |
|
||||
| | |--->| | |--->| | / |
|
||||
| | | | | | | | |
|
||||
+-----+-----+ +-----+-----+ +-----+-----+
|
||||
| | |
|
||||
| | |
|
||||
V V V
|
||||
1 2 3
|
||||
#+end_src
|
||||
|
||||
#+name: simpleBoxDiagram
|
||||
#+caption: Box diagram showing the internal structure of a list containing the values 1, 2 and 3
|
||||
#+RESULTS:
|
||||
[[file:diagrams/list123.esp]]
|
||||
|
||||
|
||||
However the =rest= of a pair needs not to be a pair or nil, it could also point to any other value.
|
||||
By doing this the list is no longer "well formed" but rather "ill formed". Ill formed lsits can be
|
||||
used as an optimization when using the list for storing data. In [[illFormedList]] an ill formed list
|
||||
can be seen, that also contains the values 1, 2 and 3 but stores them using only two pairs instead
|
||||
of 3.
|
||||
|
||||
{{{ditaa_header}}}
|
||||
#+begin_src ditaa :file diagrams/list12.3.eps
|
||||
+-----+-----+ +-----+-----+
|
||||
| | | | | |
|
||||
| | |--->| | |--->3
|
||||
| | | | | |
|
||||
+-----+-----+ +-----+-----+
|
||||
| |
|
||||
| |
|
||||
V V
|
||||
1 2
|
||||
#+end_src
|
||||
|
||||
#+name: illFormedList
|
||||
#+caption: The internal structure of an ill formed list where the last pair points to the value 3
|
||||
#+RESULTS:
|
||||
[[file:diagrams/list12.3.eps]]
|
||||
|
||||
** representing lists in Lisp
|
||||
|
||||
In Slime and in most Lisps, lists are represented using round parenthesis where =(= denotes the
|
||||
start of the list and =)= denotes the end. Eeach element inside these parenthesis separated by one
|
||||
or more spaces will be interpreted as an element of that list. For example the list from
|
||||
[[simpleBoxDiagram]] would be represented as =(1 2 3)=. During parse time, the Lisp parser transforms
|
||||
the parenthesised list into the pairs that are in the end stored in memory.
|
||||
|
||||
To also be able to represent ill formed lists in Lisp there is a special syntax using the =.= (dot
|
||||
symbol). If the parser encounters a =.= inside of a list, it will treat the next element as the
|
||||
=rest=. If there is no or more than one element after the =.= an parsing error will be thrown. Using
|
||||
this syntax we can represent the ill formed list from [[illFormedList]] as =(1 2 . 3)=. We can also
|
||||
write well formed lists using the dot notation if we point the rest to another list. So the well
|
||||
formed list from [[simpleBoxDiagram]] can also be written as =(1 . (2 . (3)))=
|
||||
|
||||
** representing function calls in Lisp
|
||||
|
||||
If we tried to enter the Lisp representation of the lists like =(1 2 3)= discussed in [[representing
|
||||
lists in Lisp]] directly into an Lisp interpreter we would get an error. That doesn't mean that the
|
||||
explanation given in the section is wrong, it is in fact correct: the lisp parser will transform the
|
||||
lisp syntax into the pairs in memory. The reason we would get an error is, that when reading Lisp
|
||||
code, the Lisp interpreter first parses the code and then tries to evaluate it and return the result
|
||||
back to the user.
|
||||
|
||||
In Lisp by default, a list corresponds to a function call. As mentioned in [[Lisp languages]] Lisp
|
||||
represents lists and Lisp programms as lists. If a list is treated as a function call, the first
|
||||
element will be treated as the function and the rest of the elements will be the arguments to that
|
||||
function. If we would wnter =(1 2 3)= directly into the Lisp interpreter we would get an error
|
||||
saying it cannot find the function =1=.
|
||||
|
||||
If we would want to compute the sum of the numbers 5 and 3 we could do this by invoking the =+=
|
||||
function with 5 and 3 as its arguments. =(+ 5 3)= will evaluate to 8. We can also nest functions
|
||||
calls and use the return values as parameters to other functions: =(+ (- 12 4) (/ 24 4))= will
|
||||
evaluate to 14. The box diagramm showing the internal structure of that computation can be seen in
|
||||
[[moreComplexBoxDiagram]].
|
||||
|
||||
{{{ditaa_header}}}
|
||||
#+begin_src ditaa :file diagrams/simpleMath.eps
|
||||
+-----+-----+ +-----+-----+ +-----+-----+
|
||||
| | | | | | | | |
|
||||
| | |--->| | |--------------------------------------->| | / |
|
||||
| | | | | | | | |
|
||||
+-----+-----+ +-----+-----+ +-----+-----+
|
||||
| | |
|
||||
| | |
|
||||
V V V
|
||||
+ +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
|
||||
| | | | | | | | | | | | | | | | | |
|
||||
| | |--->| | |--->| | / | | | |--->| | |--->| | / |
|
||||
| | | | | | | | | | | | | | | | | |
|
||||
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
V V V V V V
|
||||
- 12 4 / 24 4
|
||||
|
||||
|
||||
|
||||
#+end_src
|
||||
|
||||
#+name: moreComplexBoxDiagram
|
||||
#+caption: The internal structure of the expression =(+ (- 12 4) (/ 24 4))=
|
||||
#+RESULTS:
|
||||
[[file:diagrams/simpleMath.eps]]
|
||||
|
||||
* Evaluation order
|
||||
As a first step of evaluation of a regular function, all its arguments are getting evaluated, and
|
||||
then the function is applied to the evaluated arguments. For example when evaluating the nested
|
||||
expression in [[code:complex-math]] the outermost function is the =+= function with three arguments: =(*
|
||||
3 4)=, =(- 100 (+ 12 13 14 15))= and =2=. So before the outhermost =+= gets invoked, the three
|
||||
arguments are getting evaluated recursively.
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:complex-math
|
||||
#+caption: A more complex nested arithmetic expression. Nested expressions can be written more
|
||||
#+caption: readable by aligning subsequent arguments vertically underneeth each other
|
||||
#+begin_src slime
|
||||
(print (+ (* 3 4)
|
||||
(- 100 (+ 12 13 14 15))
|
||||
2))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:complex-math
|
||||
: evaluates to =>
|
||||
: 60
|
||||
|
||||
** Special forms
|
||||
The given evaluation rule -- to evaluate all the arguments first and then allpying them to the
|
||||
funciton -- as described in [[Evaluation order]] is only valid for regular functions. There is a class
|
||||
of functions that do not follow this evaluation rule called *special forms*. Special forms are
|
||||
needed when you do not wish to evaluate all arguments. For example the built-in =if= function should
|
||||
only evaluate the "then-expression" if the condition evaluates to a truthy value and not otherwise.
|
||||
Consider the example in [[code:special-forms]]. The if expression only evaluates the then-expression. If
|
||||
the =if= function would follow the evaluation order of regular functions, first all three arguments
|
||||
=(< 1 2)=, =(print "I knew it!!\n")= but also =(print "Oh, it is not?!\n")= could get evaluated and
|
||||
so both messages would be printed. In the given =if= expression, the condition evaluates to a truthy
|
||||
value and only =I knew it!!= will be printed.
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:special-forms
|
||||
#+caption: The =if= function is a special form because it does not evaluate all of its arguments
|
||||
#+begin_src slime
|
||||
(if (< 1 2)
|
||||
(print "I knew it!!\n")
|
||||
(print "Oh, it is not?!\n"))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:special-forms
|
||||
: evaluates to =>
|
||||
: I knew it!!
|
||||
|
||||
The programmer can also define their own special forms using =special-lambda= and macros, which will
|
||||
be explained later.
|
||||
|
||||
* Symbols and keywords
|
||||
* Lambdas
|
||||
Slime allows for creating anonymous functions called *lambdas*. We did not talk about binding
|
||||
variables, we will do this in [[Define]], but we can still use lambdas now. Remember that Lisp
|
||||
interpretes the first argument of a list in the source code as a function and the rest as the
|
||||
arguments. The =lambda= special form evaluates to a *regular function object* that can then stand in
|
||||
the first position of the function call list. The basic syntax for the lambda special form is:
|
||||
\[\texttt{(lambda (arg1 arg2 ...) (body1) ...)}\] the first arguemnt to =lambda= is a list of the
|
||||
arguments. All the following arguments will be the body of the lambda. They will be executed when
|
||||
the lambda is invoked. The return value of a lambda is the value of the last evaluated expression in
|
||||
the body.
|
||||
|
||||
Probably the simplest function to write as a lambda is the identity function. It takes one argument
|
||||
and returns it. The identity lambda and a few other simple examples of lambdas can be seen in
|
||||
[[code:simple-lambdas]].
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:simple-lambdas
|
||||
#+caption: Some simle lambdas
|
||||
#+begin_src slime
|
||||
(printf ((lambda (x) x) 1))
|
||||
(printf ((lambda (x y) (+ x y)) 3 5))
|
||||
(printf ((lambda (x y z) (list x y z)) 1 2 3))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:simple-lambdas
|
||||
: evaluates to =>
|
||||
: 1
|
||||
: 8
|
||||
: (1 2 3)
|
||||
|
||||
Additionally Slime lambdas have the possibility to take *optional arguments* in the form of *keyword
|
||||
arguemnts* as well as a *rest argument* which allows for accepting any number of arguments. Since
|
||||
these concepts are most useful when the function is actually bound to a variable, they will be
|
||||
introduced when we learned how to do that in [[Define]].
|
||||
|
||||
** Special lambdas
|
||||
The =lambda= special form creates a function object that represents a regular function. So the basic
|
||||
evaluation rules count: when the lambda is invoked all it's arguments are evaluated and then the
|
||||
lambda is applied to the evaluated arguments. If this is not wanted in some rare cases, the
|
||||
programmer also has the possibility to define a special form using =special-lambda=, which, when
|
||||
invoked does not evaluare any argument. The programmer has to evaluate the arguments in the body
|
||||
themselves using =eval=. The rest of the syntax between =lambda= and =special-lambda= are the same.
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:special-lambdas
|
||||
#+caption: Special lambdas do not evaluate their arguments
|
||||
#+begin_src slime
|
||||
((lambda (x) (printf x)) (+ 1 2))
|
||||
((special-lambda (x) (printf x)) (+ 1 2))
|
||||
|
||||
;; Special lambdas make it possible to write
|
||||
;; code that inspects code
|
||||
((special-lambda (expr)
|
||||
(printf "The function to be called is"
|
||||
(first expr)
|
||||
"and the result is"
|
||||
(eval expr)))
|
||||
(+ 1 2))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:special-lambdas
|
||||
: evaluates to =>
|
||||
: 3
|
||||
: (+ 1 2)
|
||||
: The function to be called is + and the result is 3.
|
||||
|
||||
* Define
|
||||
To assign a value to a symbol you can use the =define= built-in special form. The syntax for
|
||||
=define= is: \[\texttt{(define symbol value)}\] and some usages can be seen in
|
||||
[[code:variable-defines]].
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:variable-defines
|
||||
#+caption: Simple definition of variables
|
||||
#+begin_src slime
|
||||
(define var1 1)
|
||||
(define var2 "Hello World")
|
||||
(define var3 (+ 1 2))
|
||||
|
||||
(printf var1 var2 var3)
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:variable-defines
|
||||
: evaluates to =>
|
||||
: 1 Hello World 3
|
||||
|
||||
** Defining functions
|
||||
In [[Lambdas]] we learned how to create function objects using the =lambda= built-in form. Using
|
||||
=define= every Lisp Object can be assigned to a symbol making no exception for the function objects.
|
||||
In [[code:lambda-defines]] you can see what that would look like.
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:lambda-defines
|
||||
#+caption: Definition of functions using lambdas
|
||||
#+begin_src slime
|
||||
(define hypothenuse
|
||||
(lambda (a b)
|
||||
(** (+ (* a a) (* b b)) 0.5)))
|
||||
|
||||
(printf (hypothenuse 3 4))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:lambda-defines
|
||||
: evaluates to =>
|
||||
: 5
|
||||
|
||||
Since defining functions is so common, there is a syntactic shorthand that does not require to write
|
||||
out the whole =lambda= definition. In this case the first argument to the call to =define= is a
|
||||
list. The frist element of the list is the name of the function to define and the other elemens are
|
||||
the arguments to that function. An example can be seen in [[code:function-defines]]. Note that the
|
||||
definition looks like a call to the function we are constructing, making it easier to see what a
|
||||
call to that function will look like.
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:function-defines
|
||||
#+caption: Definition of functions using the shorthand syntax of define
|
||||
#+begin_src slime
|
||||
(define (hypothenuse a b)
|
||||
(** (+ (* a a) (* b b)) 0.5))
|
||||
|
||||
(printf (hypothenuse 3 4))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:function-defines
|
||||
: evaluates to =>
|
||||
: 5
|
||||
|
||||
** Functions with keyword arguments
|
||||
A sometimes more convenient way of passing arguments to a function is using keyword arguments. Using
|
||||
keyword arguments a function call could look like this: \[\texttt{(function :arg1 value1 :arg2
|
||||
value2)}\] here the function accepts two arguments named =arg1= and =arg2=. The user of this
|
||||
function can see more clearly excatly which argument will be assigned wich value. This notation also
|
||||
allows for switching the argument order. The following function call is equivalent to the call
|
||||
above. \[\texttt{(function :arg2 value2 :arg1 value1)}\].
|
||||
|
||||
For this to work however, the function must be defined to accept these keyword arguments. To do this
|
||||
the special marker =:keys= has to be inserted into the argument list of a =lambda= or a function
|
||||
=define=. All following arguments *must* be supplied as keyword arguments, /unless/ they are also
|
||||
supplied with a default value, in which case they do not need to be supplied. To attach a default
|
||||
value to a keyword argument, insert =:defaults-to <value>= after the keyword argument name. An
|
||||
example of all of this can be seen in [[code:keyword-args]].
|
||||
|
||||
{{{slime_header}}}
|
||||
#+name: code:keyword-args
|
||||
#+caption: A more complex functoin definition using keyword arguments
|
||||
#+begin_src slime
|
||||
(define (complex required1 required2 :keys key1 key2 :defaults-to 3 key3)
|
||||
(* (+ required1 required2)
|
||||
key1
|
||||
key2
|
||||
key3))
|
||||
|
||||
(printf (complex 1 2 :key1 2 :key2 2 :key3 3))
|
||||
(printf (complex 1 2 :key1 2 :key3 3))
|
||||
(printf (complex 1 2 :key3 3 :key1 2))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS: code:keyword-args
|
||||
: evaluates to =>
|
||||
: 36
|
||||
: 54
|
||||
: 54
|
||||
|
||||
|
||||
|
||||
|
||||
** Functions with rest arguments
|
||||
|
||||
* Environments
|
||||
* Built-in functions
|
||||
This section provides a comprehensive list of the built in functions for Slime. Some of them are
|
||||
defined in =C++= source code, some are themselves written in Slime. The cool thing about Slime is
|
||||
that it is really easy to extend and adapt it for many purposes by writing new functions in =C++=
|
||||
that for example communicate with an already existing software system, so Slime can be used as an
|
||||
embedded scripting language.
|
||||
|
||||
** Arithmetic functions
|
||||
- =+= :: (=regular function [C++]=) Takes 0 or more numbers as arguments and returns the sum of all
|
||||
the numbers.
|
||||
- =-= :: (=regular function [C++]=) Takes 0 or more numbers as arguments. If only one number is
|
||||
supplied, its negation is returned, otherwise the difference of the first argument and the
|
||||
sum of the remaining arguments is returned:
|
||||
\[\texttt{(- 10 2 1)} \Rightarrow 10 - 2 - 1 = 10 - (2 + 1) = 7\]
|
||||
- =*= :: (=regular function [C++]=) Takes 0 or more numbers as arguments and returns the product of
|
||||
all the numbers.
|
||||
- =/= :: (=regular function [C++]=) Takes 0 or more numbers as arguments. If only one number is
|
||||
supplied, it is returned, otherwise the quotient of the first argument and the product of
|
||||
the remaining arguments is returned:
|
||||
\[\texttt{(/ 100 2 5)} \Rightarrow \frac{100}{\frac{2}{5}} = \frac{100}{2 \cdot 5} = 10\]
|
||||
- =**= :: (=regular function [C++]=) Takes 2 number arguments and returns the the first argument
|
||||
taken to the power of the second argument.
|
||||
- =%= :: (=regular function [C++]=) Takes 2 number arguments and rounds them down to integer values
|
||||
and then returns the remainder of the division of the first argument by the second.
|
||||
- =not= :: (=regular function [C++]=)
|
||||
- =and= :: (=regular function [C++]=)
|
||||
- =or= :: (=regular function [C++]=)
|
||||
- =increment= :: (=regular function [Slime]=)
|
||||
- =decrement= :: (=regular function [Slime]=)
|
||||
|
||||
** Comparison functions
|
||||
- === :: (=regular function [C++]=) Takes 0 or more arguments and returns =t= iff
|
||||
\[\forall\ \text{arg}_i \in \text{arguments}: \text{arg}_i = \text{arg}_{i+1}\]
|
||||
\indent and =()= otherwise.
|
||||
|
||||
{{{slime_header}}}
|
||||
#+begin_src slime
|
||||
;; numbers
|
||||
(pe (= 1 (+ -1 2)))
|
||||
(pe (= 0 (** 3 0)))
|
||||
;; strings
|
||||
(pe (= "abc" "abc"))
|
||||
(pe (= "abc" "abs"))
|
||||
;; symbols & keywords
|
||||
(pe (= 'sym1 'sym2))
|
||||
(pe (= :key1 :key1))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS:
|
||||
: evaluates to =>
|
||||
: (= 1 (+ -1 2)) evaluates to t
|
||||
: (= 0 (** 3 0)) evaluates to ()
|
||||
: (= abc abc) evaluates to t
|
||||
: (= abc abs) evaluates to ()
|
||||
: (= 'sym1 'sym2) evaluates to ()
|
||||
: (= :key1 :key1) evaluates to t
|
||||
|
||||
|
||||
- =>= :: (=regular function [C++]=) Takes 0 or more arguments and returns =t= iff
|
||||
\[\forall\ \text{arg}_i \in \text{arguments}: \text{arg}_i > \text{arg}_{i+1}\]
|
||||
\indent and =()= otherwise.
|
||||
- =>== :: (=regular function [C++]=) Takes 0 or more arguments and returns =t= iff
|
||||
\[\forall\ \text{arg}_i \in \text{arguments}: \text{arg}_i \ge \text{arg}_{i+1}\]
|
||||
\indent and =()= otherwise.
|
||||
- =<= :: (=regular function [C++]=) Takes 0 or more arguments and returns =t= iff
|
||||
\[\forall\ \text{arg}_i \in \text{arguments}: \text{arg}_i < \text{arg}_{i+1}\]
|
||||
\indent and =()= otherwise.
|
||||
- =<== :: (=regular function [C++]=) Takes 0 or more arguments and returns =t= iff
|
||||
\[\forall\ \text{arg}_i \in \text{arguments}: \text{arg}_i \le \text{arg}_{i+1}\]
|
||||
\indent and =()= otherwise.
|
||||
|
||||
** Controlflow
|
||||
+ =if= :: (=special form [C++]=)
|
||||
+ =cond= :: (=special form [Slime]=)
|
||||
+ =while= :: (=special form [C++]=)
|
||||
+ =n-times= :: (=special form [Slime]=)
|
||||
|
||||
+ =when= :: (=special form [Slime]=)
|
||||
+ =unless= :: (=special form [Slime]=)
|
||||
|
||||
** Functions for lists
|
||||
- =pair= :: (=regular function [C++]=)
|
||||
- =first= :: (=regular function [C++]=)
|
||||
- =rest= :: (=regular function [C++]=)
|
||||
- =list= :: (=regular function [C++]=)
|
||||
|
||||
- =end= :: (=regular function [Slime]=)
|
||||
- =last= :: (=regular function [Slime]=)
|
||||
- =extend= :: (=regular function [Slime]=)
|
||||
- =append= :: (=regular function [Slime]=)
|
||||
- =length= :: (=regular function [Slime]=)
|
||||
|
||||
- =range= :: (=regular function [Slime]=)
|
||||
- =range-while= :: (=regular function [Slime]=)
|
||||
- =zip= :: (=regular function [Slime]=)
|
||||
- =enumerate= :: (=regular function [Slime]=)
|
||||
|
||||
|
||||
- =map= :: (=regular function [Slime]=)
|
||||
- =filter= :: (=regular function [Slime]=)
|
||||
- =reduce= :: (=regular function [Slime]=)
|
||||
- =reduce-binary= :: (=regular function [Slime]=)
|
||||
|
||||
** Functions on types
|
||||
- =type= :: (=regular function [C++]=)
|
||||
- =set-type= :: (=regular function [C++]=)
|
||||
- =delete-type= :: (=regular function [C++]=)
|
||||
- =symbol->keyword= :: (=regular function [C++]=)
|
||||
- =string->symbol= :: (=regular function [C++]=)
|
||||
- =symbol->string= :: (=regular function [C++]=)
|
||||
|
||||
** Help and debugging
|
||||
- =break= :: (=regular function [C++]=)
|
||||
- =memstat= :: (=regular function [C++]=)
|
||||
- =info= :: (=regular function [C++]=)
|
||||
- =show= :: (=regular function [C++]=)
|
||||
- =pe= :: (=special form [Slime]=)
|
||||
|
||||
** I/O
|
||||
- =print= :: (=regular function [C++]=)
|
||||
- =read= :: (=regular function [C++]=)
|
||||
- =printf= :: (=regular function [Slime]=)
|
||||
|
||||
** Errors
|
||||
- =try= :: (=regular function [C++]=)
|
||||
- =error= :: (=regular function [C++]=)
|
||||
|
||||
** no category
|
||||
- =eval= :: (=regular function [C++]=)
|
||||
- =apply= :: (=regular function [C++]=)
|
||||
- =lambda= :: (=regular function [C++]=)
|
||||
- =special-lambda= :: (=regular function [C++]=)
|
||||
|
||||
- =copy= :: (=regular function [C++]=)
|
||||
- =import= :: (=regular function [C++]=)
|
||||
- =load= :: (=regular function [C++]=)
|
||||
- =exit= :: (=regular function [C++]=)
|
||||
- =let= :: (=regular function [C++]=)
|
||||
- =quote= :: (=regular function [C++]=)
|
||||
- =quasiquote= :: (=regular function [C++]=)
|
||||
- =unquote= :: (=regular function [C++]=)
|
||||
- =mutate= :: (=regular function [C++]=)
|
||||
- =define= :: (=regular function [C++]=)
|
||||
- =assert= :: (=regular function [C++]=)
|
||||
* testbox :noexport:
|
||||
#+BEGIN_SRC ditaa :file diagrams/test.eps :cmdline --no-separation --no-shadows
|
||||
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
|
||||
@@ -67,25 +572,31 @@ sad
|
||||
#+options: H:2 toc:nil
|
||||
|
||||
#+macro: slime_header (eval (concat "#+header: :exports both" "\n" "#+attr_latex: :options keywordstyle=\\color{slimeKeyword}, commentstyle=\\color{slimeComment}, stringstyle=\\color{slimeString}"))
|
||||
#+macro: ditaa_header (eval (concat "#+header: :exports results :cmdline --no-separation --no-shadows"))
|
||||
|
||||
#+latex_class:article
|
||||
|
||||
#+latex_header: \usepackage[german]{babel}
|
||||
#+latex_header: \usepackage{xcolor}
|
||||
#+latex_header: \usepackage{listings}
|
||||
#+latex_header: \usepackage[pageanchor=false]{hyperref}
|
||||
|
||||
#+latex_header: \definecolor{slimeKeyword}{HTML}{B58900}
|
||||
#+latex_header: \definecolor{slimeString}{HTML}{2AA198}
|
||||
#+latex_header: \definecolor{slimeComment}{HTML}{839496}
|
||||
|
||||
|
||||
#+latex_header: \lstdefinelanguage{slime}
|
||||
#+latex_header: {
|
||||
#+latex_header: % list of keywords
|
||||
#+latex_header: morekeywords={
|
||||
#+latex_header: =, >, >=, <, <=, +, -, *, /, **, assert, define, define-syntax, mutate, if, quote, quasiquote, and, or, not, while, let, lambda, special-lambda, eval, begin, list, pair, first, rest, set-type, delete-type, type, info, show, print, read, exit, break, memstat, try, load, copy, error, symbol->keyword, string->symbol, symbol->string, concat-strings
|
||||
#+latex_header: },
|
||||
#+latex_header: otherkeywords = {+,=,>,>=,<,<=,-,*,/,**},
|
||||
#+latex_header: morekeywords={+,=,>,>=,<,<=,-,*,/,**,assert,define,define-syntax,mutate,if,quote,quasiquote,and,or,not,while,let,lambda,special,eval,begin,list,pair,first,rest,set-type,delete-type,type,info,show,print,read,exit,break,memstat,try,load,copy,error,symbol->keyword,string->symbol,symbol->string,concat-strings},
|
||||
#+latex_header: basicstyle=\ttfamily\small,
|
||||
#+latex_header: showstringspaces=false,
|
||||
#+latex_header: sensitive=true, % keywords are not case-sensitive
|
||||
#+latex_header: morecomment=[l]{;}, % l is for line comment
|
||||
#+latex_header: morestring=[b]" % defines that strings are enclosed in double quotes
|
||||
#+latex_header: }
|
||||
|
||||
#+latex_header:\AtBeginDocument{\renewcommand{\lstlistingname}{Code}}
|
||||
#+latex_header:\AtBeginDocument{\renewcommand{\ref}[1]{\autoref{#1}}}
|
||||
|
||||
+14
-2
@@ -230,8 +230,11 @@ proc load_built_ins_into_environment(Environment* env) -> void {
|
||||
defun("-", cLambda {
|
||||
int arguments_length;
|
||||
try arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
try assert_type(arguments->value.pair.first, Lisp_Object_Type::Number);
|
||||
|
||||
if (arguments_length == 0)
|
||||
return Memory::create_lisp_object_number(0);
|
||||
|
||||
try assert_type(arguments->value.pair.first, Lisp_Object_Type::Number);
|
||||
double difference = arguments->value.pair.first->value.number;
|
||||
|
||||
if (arguments_length == 1) {
|
||||
@@ -250,6 +253,11 @@ proc load_built_ins_into_environment(Environment* env) -> void {
|
||||
defun("*", cLambda {
|
||||
int arguments_length;
|
||||
try arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
|
||||
if (arguments_length == 0) {
|
||||
return Memory::create_lisp_object_number(1);
|
||||
}
|
||||
|
||||
try assert_type(arguments->value.pair.first, Lisp_Object_Type::Number);
|
||||
|
||||
double product = arguments->value.pair.first->value.number;
|
||||
@@ -266,6 +274,11 @@ proc load_built_ins_into_environment(Environment* env) -> void {
|
||||
defun("/", cLambda {
|
||||
int arguments_length;
|
||||
try arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
|
||||
if (arguments_length == 0) {
|
||||
return Memory::create_lisp_object_number(1);
|
||||
}
|
||||
|
||||
try assert_type(arguments->value.pair.first, Lisp_Object_Type::Number);
|
||||
|
||||
double quotient = arguments->value.pair.first->value.number;
|
||||
@@ -890,7 +903,6 @@ proc load_built_ins_into_environment(Environment* env) -> void {
|
||||
|
||||
});
|
||||
defun("copy", cLambda {
|
||||
|
||||
// TODO(Felix): if we are copying string nodes, then
|
||||
// shouldn't the string itself also get copied??
|
||||
try evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
||||
|
||||
+1
-1
@@ -462,7 +462,7 @@ proc interprete_file (char* file_name) -> Lisp_Object* {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
print(result);
|
||||
// print(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+35
-1
@@ -243,7 +243,12 @@ proc print(Lisp_Object* node, bool print_quotes = false, FILE* file = stdout) ->
|
||||
switch (Memory::get_type(node)) {
|
||||
case (Lisp_Object_Type::Nil): fputs("()", file); break;
|
||||
case (Lisp_Object_Type::T): fputs("t", file); break;
|
||||
case (Lisp_Object_Type::Number): fprintf(file, "%f", node->value.number); break;
|
||||
case (Lisp_Object_Type::Number): {
|
||||
if (abs(node->value.number - (int)node->value.number) < 0.000001f)
|
||||
fprintf(file, "%d", (int)node->value.number);
|
||||
else
|
||||
fprintf(file, "%f", node->value.number);
|
||||
} break;
|
||||
case (Lisp_Object_Type::Keyword): fputs(":", file); // NOTE(Felix): intentionall fallthough
|
||||
case (Lisp_Object_Type::Symbol): fprintf(file, "%s", Memory::get_c_str(node->value.symbol.identifier)); break;
|
||||
case (Lisp_Object_Type::CFunction): fputs("[C-function]", file); break;
|
||||
@@ -268,6 +273,35 @@ proc print(Lisp_Object* node, bool print_quotes = false, FILE* file = stdout) ->
|
||||
} break;
|
||||
case (Lisp_Object_Type::Pair): {
|
||||
Lisp_Object* head = node;
|
||||
|
||||
// first check if it is a quotation form, in that case we want
|
||||
// to print it prettier
|
||||
if (Memory::get_type(head->value.pair.first) == Lisp_Object_Type::Symbol) {
|
||||
String* identifier = head->value.pair.first->value.symbol.identifier;
|
||||
|
||||
// TODO(Felix): Lisp_Node* symbol = head->value.pair.first;
|
||||
// TODO(Felix): if (symbol == Memory::get_or_create_symbol("quote"))
|
||||
if (string_equal(identifier, "quote") ||
|
||||
string_equal(identifier, "unquote"))
|
||||
{
|
||||
putc((string_equal(identifier, "quote"))
|
||||
? '\''
|
||||
: ',', file);
|
||||
|
||||
assert_type(head->value.pair.rest, Lisp_Object_Type::Pair);
|
||||
assert(head->value.pair.rest->value.pair.rest == Memory::nil);
|
||||
|
||||
print(head->value.pair.rest->value.pair.first, print_quotes, file);
|
||||
break;
|
||||
}
|
||||
else if (string_equal(identifier, "quasiquote")) {
|
||||
putc('`', file);
|
||||
assert_type(head->value.pair.rest, Lisp_Object_Type::Pair);
|
||||
print(head->value.pair.rest->value.pair.first, print_quotes, file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
putc('(', file);
|
||||
|
||||
// NOTE(Felix): We cold do a while true here, however in case
|
||||
|
||||
Reference in New Issue
Block a user