From 03a2c81aa5a3e63be845ce024d4f7dd9b72d639b Mon Sep 17 00:00:00 2001 From: Felix Brendel Date: Thu, 16 May 2019 16:52:37 +0200 Subject: [PATCH] Getting the manual going --- bin/pre.slime | 49 +-- bin/pre.slime.expanded | 14 +- bin/tests/class_macro.slime.expanded | 6 +- bin/tests/lexical_scope.slime.expanded | 14 +- manual/manual.org | 519 ++++++++++++++++++++++++- src/built_ins.cpp | 16 +- src/eval.cpp | 2 +- src/io.cpp | 36 +- 8 files changed, 598 insertions(+), 58 deletions(-) diff --git a/bin/pre.slime b/bin/pre.slime index 14b99aa..dbeec26 100644 --- a/bin/pre.slime +++ b/bin/pre.slime @@ -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)) diff --git a/bin/pre.slime.expanded b/bin/pre.slime.expanded index 254d9d6..0c1178b 100644 --- a/bin/pre.slime.expanded +++ b/bin/pre.slime.expanded @@ -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 diff --git a/bin/tests/class_macro.slime.expanded b/bin/tests/class_macro.slime.expanded index ce801c4..465ab21 100644 --- a/bin/tests/class_macro.slime.expanded +++ b/bin/tests/class_macro.slime.expanded @@ -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)) diff --git a/bin/tests/lexical_scope.slime.expanded b/bin/tests/lexical_scope.slime.expanded index ec91232..e8daca2 100644 --- a/bin/tests/lexical_scope.slime.expanded +++ b/bin/tests/lexical_scope.slime.expanded @@ -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)) diff --git a/manual/manual.org b/manual/manual.org index d1d58fe..98771c0 100644 --- a/manual/manual.org +++ b/manual/manual.org @@ -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 = 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}}} diff --git a/src/built_ins.cpp b/src/built_ins.cpp index 081a947..51df303 100644 --- a/src/built_ins.cpp +++ b/src/built_ins.cpp @@ -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); diff --git a/src/eval.cpp b/src/eval.cpp index bb40648..6cb3252 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -462,7 +462,7 @@ proc interprete_file (char* file_name) -> Lisp_Object* { return nullptr; } - print(result); + // print(result); return result; } diff --git a/src/io.cpp b/src/io.cpp index ed45a87..a376be5 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -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