Getting the manual going
This commit is contained in:
+15
-34
@@ -5,26 +5,12 @@
|
|||||||
(define-syntax (unless condition :rest body)
|
(define-syntax (unless condition :rest body)
|
||||||
`(if ,condition nil ,(pair begin body)))
|
`(if ,condition nil ,(pair begin body)))
|
||||||
|
|
||||||
;; (define-syntax defun (name arguments :rest body)
|
(define-syntax (n-times times action)
|
||||||
;; ;; (type-assert arguments :pair)
|
"Executes action times times."
|
||||||
;; ;; `(define ,name (lambda ,arguments ,body))
|
(define (repeat times elem)
|
||||||
;; ;; TODO(Felix: I think we do not need to wrap the body of the lamba
|
(unless (> 1 times)
|
||||||
;; ;; in a begin
|
(pair elem (repeat (- times 1) elem))))
|
||||||
|
(pair 'begin (repeat times action)))
|
||||||
;; ;; 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 (fib n))
|
;; (define (fib n))
|
||||||
;; (define-syntax define (name :rest value)
|
;; (define-syntax define (name :rest value)
|
||||||
@@ -41,7 +27,6 @@
|
|||||||
;; (print "\nbb\n")
|
;; (print "\nbb\n")
|
||||||
;; (pair 'define (pair name value)))))
|
;; (pair 'define (pair name value)))))
|
||||||
|
|
||||||
;; TODO(Felix): else symbol
|
|
||||||
(define-syntax (cond :rest clauses)
|
(define-syntax (cond :rest clauses)
|
||||||
(define (rec clauses)
|
(define (rec clauses)
|
||||||
(if (= nil clauses)
|
(if (= nil clauses)
|
||||||
@@ -122,13 +107,6 @@ the (rest) of the last element of the sequence."
|
|||||||
with (pair elem nil)."
|
with (pair elem nil)."
|
||||||
(extend seq (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)
|
(define (length seq)
|
||||||
"Returns the length of the given sequence."
|
"Returns the length of the given sequence."
|
||||||
(if (nil? seq)
|
(if (nil? seq)
|
||||||
@@ -143,11 +121,6 @@ with (pair elem nil)."
|
|||||||
"Subtracts one from the argument."
|
"Subtracts one from the argument."
|
||||||
(- val 1))
|
(- 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)
|
;; (defmacro for (@symbol @from @to :rest @for-body)
|
||||||
;; "Designed to resemble a C style for loop. It takes a symbol as
|
;; "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
|
nil
|
||||||
(pair (list (first l1) (first l2)) (zip (rest l1) (rest l2)))))
|
(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)
|
(define (printf :keys sep :defaults-to " " end :defaults-to "\n" :rest args)
|
||||||
"A wrapper for the built-in (print) that accepts a variable number
|
"A wrapper for the built-in (print) that accepts a variable number
|
||||||
of arguments and also provides keywords for specifying the printed
|
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))))
|
(eval (pair printf-quoted (extend (list :@sep (eval sep) :@end (eval end)) args))))
|
||||||
|
|
||||||
(define-syntax (pe expr)
|
(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
|
(define (append seq elem) "Appends an element to a sequence, by extendeing the list
|
||||||
with (pair elem nil)." (extend seq (pair elem nil)))
|
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
|
(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.000000 from) :to to))) nil))
|
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)
|
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
|
(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 (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 "
|
(define (printf :keys sep :defaults-to " " end :defaults-to "
|
||||||
" :rest args) "A wrapper for the built-in (print) that accepts a variable number
|
" :rest args) "A wrapper for the built-in (print) that accepts a variable number
|
||||||
of arguments and also provides keywords for specifying the printed
|
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 (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 (= (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))
|
(define counter1 (make-counter))
|
||||||
|
|
||||||
(assert (= (counter1) 1.000000))
|
(assert (= (counter1) 1))
|
||||||
|
|
||||||
(define counter2 (make-counter))
|
(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
|
#+title: The Slime 1.0 Manual
|
||||||
|
|
||||||
#+begin_abstract
|
#+begin_abstract
|
||||||
sad
|
abstract
|
||||||
#+end_abstract
|
#+end_abstract
|
||||||
|
|
||||||
\tableofcontents
|
\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:
|
* testbox :noexport:
|
||||||
#+BEGIN_SRC ditaa :file diagrams/test.eps :cmdline --no-separation --no-shadows
|
#+BEGIN_SRC ditaa :file diagrams/test.eps :cmdline --no-separation --no-shadows
|
||||||
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
|
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
|
||||||
@@ -67,25 +572,31 @@ sad
|
|||||||
#+options: H:2 toc:nil
|
#+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: 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_class:article
|
||||||
|
|
||||||
#+latex_header: \usepackage[german]{babel}
|
#+latex_header: \usepackage[german]{babel}
|
||||||
#+latex_header: \usepackage{xcolor}
|
#+latex_header: \usepackage{xcolor}
|
||||||
#+latex_header: \usepackage{listings}
|
#+latex_header: \usepackage{listings}
|
||||||
|
#+latex_header: \usepackage[pageanchor=false]{hyperref}
|
||||||
|
|
||||||
#+latex_header: \definecolor{slimeKeyword}{HTML}{B58900}
|
#+latex_header: \definecolor{slimeKeyword}{HTML}{B58900}
|
||||||
#+latex_header: \definecolor{slimeString}{HTML}{2AA198}
|
#+latex_header: \definecolor{slimeString}{HTML}{2AA198}
|
||||||
#+latex_header: \definecolor{slimeComment}{HTML}{839496}
|
#+latex_header: \definecolor{slimeComment}{HTML}{839496}
|
||||||
|
|
||||||
|
|
||||||
#+latex_header: \lstdefinelanguage{slime}
|
#+latex_header: \lstdefinelanguage{slime}
|
||||||
#+latex_header: {
|
#+latex_header: {
|
||||||
#+latex_header: % list of keywords
|
#+latex_header: % list of keywords
|
||||||
#+latex_header: morekeywords={
|
#+latex_header: otherkeywords = {+,=,>,>=,<,<=,-,*,/,**},
|
||||||
#+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: 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: },
|
|
||||||
#+latex_header: basicstyle=\ttfamily\small,
|
#+latex_header: basicstyle=\ttfamily\small,
|
||||||
#+latex_header: showstringspaces=false,
|
#+latex_header: showstringspaces=false,
|
||||||
#+latex_header: sensitive=true, % keywords are not case-sensitive
|
#+latex_header: sensitive=true, % keywords are not case-sensitive
|
||||||
#+latex_header: morecomment=[l]{;}, % l is for line comment
|
#+latex_header: morecomment=[l]{;}, % l is for line comment
|
||||||
#+latex_header: morestring=[b]" % defines that strings are enclosed in double quotes
|
#+latex_header: morestring=[b]" % defines that strings are enclosed in double quotes
|
||||||
#+latex_header: }
|
#+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 {
|
defun("-", cLambda {
|
||||||
int arguments_length;
|
int arguments_length;
|
||||||
try arguments = eval_arguments(arguments, env, &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;
|
double difference = arguments->value.pair.first->value.number;
|
||||||
|
|
||||||
if (arguments_length == 1) {
|
if (arguments_length == 1) {
|
||||||
@@ -250,6 +253,11 @@ proc load_built_ins_into_environment(Environment* env) -> void {
|
|||||||
defun("*", cLambda {
|
defun("*", cLambda {
|
||||||
int arguments_length;
|
int arguments_length;
|
||||||
try arguments = eval_arguments(arguments, env, &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);
|
try assert_type(arguments->value.pair.first, Lisp_Object_Type::Number);
|
||||||
|
|
||||||
double product = arguments->value.pair.first->value.number;
|
double product = arguments->value.pair.first->value.number;
|
||||||
@@ -266,6 +274,11 @@ proc load_built_ins_into_environment(Environment* env) -> void {
|
|||||||
defun("/", cLambda {
|
defun("/", cLambda {
|
||||||
int arguments_length;
|
int arguments_length;
|
||||||
try arguments = eval_arguments(arguments, env, &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);
|
try assert_type(arguments->value.pair.first, Lisp_Object_Type::Number);
|
||||||
|
|
||||||
double quotient = arguments->value.pair.first->value.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 {
|
defun("copy", cLambda {
|
||||||
|
|
||||||
// TODO(Felix): if we are copying string nodes, then
|
// TODO(Felix): if we are copying string nodes, then
|
||||||
// shouldn't the string itself also get copied??
|
// shouldn't the string itself also get copied??
|
||||||
try evaluated_arguments = eval_arguments(arguments, env, &arguments_length);
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
print(result);
|
// print(result);
|
||||||
return 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)) {
|
switch (Memory::get_type(node)) {
|
||||||
case (Lisp_Object_Type::Nil): fputs("()", file); break;
|
case (Lisp_Object_Type::Nil): fputs("()", file); break;
|
||||||
case (Lisp_Object_Type::T): fputs("t", 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::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::Symbol): fprintf(file, "%s", Memory::get_c_str(node->value.symbol.identifier)); break;
|
||||||
case (Lisp_Object_Type::CFunction): fputs("[C-function]", file); 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;
|
} break;
|
||||||
case (Lisp_Object_Type::Pair): {
|
case (Lisp_Object_Type::Pair): {
|
||||||
Lisp_Object* head = node;
|
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);
|
putc('(', file);
|
||||||
|
|
||||||
// NOTE(Felix): We cold do a while true here, however in case
|
// NOTE(Felix): We cold do a while true here, however in case
|
||||||
|
|||||||
Reference in New Issue
Block a user