|
LispBM
|
Adds up an aribtrary number of values. The form of a + expression is (+ expr1 ... exprN).
Subtract an arbitrary number of values from a value. The form of a - expression is (- expr1 ... exprN)
Multiplying an arbitrary number of values. The form of a * expression is (* expr1 ... exprN)
Division. The form of a / expression is (/ expr1 ... exprN).
Modulo operation. The form of a mod expression is (mod expr1 ... exprN).
Compare expressions for equality. The = implements structural equality. The for of an = expression is (= expr1 ... exprN)
(+ 1 2) with 3. The result of this comparison is t. t nil representing false. t. The num-eq operation can only be used on numerical arguments. For numerical arguments num-eq behaves like =. If you know you are comparing numbers, it will be more efficient to use num-eq.
num-eq comparison. Greater than comparison. A greater than comparison has the form (> expr1 ... exprN) and evaluates to t if expr1 is greater than all of expr2 ... exprN.
Less than comparison. A less than comparison has the form (> expr1 ... exprN) and evaluates to t if expr1 is less than all of expr2 ... exprN.
Boolean and operation between n arguments. The form of an and expression is (and expr1 ... exprN). This operation treats all non-nil values as true. Boolean and is "shirt-circuiting" and only evaluates until a false is encountered.
t Boolean or operation between n arguments. The form of an or expression is (or expr1 ... exprN). This operation treats all non-nil values as true. Boolean or is "short-circuiting" and only evaluates until a true is encountered.
t. Boolean not takes one argument. The form of a not expression is (not expr). All non-nil values are considered true.
t The shift left operation takes two arguments. The first argument is a value to shift and the second argument is the number of bit positions to shift the value.
The shift right operation takes two arguments. The first argument is a value to shift and the second argument in the number of bit positions to shift the value.
Performs the bitwise and operation between two values. The type of the result is the same type as the first of the arguments.
Performs the bitwise or operation between two values. The type of the result is the same type as the first of the arguments.
Performs the bitwise xor operation between two values. The type of the result is the same type as the first of the arguments.
Performs the bitwise not operations on a value. The result is of same type as the argument.
The encode-i32 function converts a list of four (byte sized) values into an i32 value.
The encode-u32 function converts a list of four (byte sized) values into an u32 value.
The encode-float function converts a list four (byte sized) values into a float value.
The decode function decodes a value into a list of four (byte sized) values.
Represents the empty list. The nil value is also considered to be false by conditionals
All non nil values are considered true in conditionals. t should be used in cases where an explicit true makes sense.
Code and data share the same representation, it is only a matter of how you look at it. The tools for changing how your view are the quotation and quasiquotation operations.
Usages of the ' quote symbol in input code is replaced with the symbol quote by the reader.
Evaluating a quoted expression, (quote a), results in a unevaluated.
'(+ 1 2) gets read into the heap as the list (quote (+ 1 2)).(quote (+ 1 2)) results in the value (+ 1 2).The backwards tick ‘ is called the quasiquote. It is similar to the ’ but allows splicing in results of computations using the , and the ,@ operators.
'(+ 1 2) and `(+ 1 2) are similar in effect. Both result in the result value of (+ 1 2), that is a list containing +, 1 and 2. `(+ 1 2) is read into the heap it is expanded into the expression (append (quote (+)) (append (quote (1)) (append (quote (2)) (quote nil)))) which evaluates to the list (+ 1 2).The comma is used to splice the result of a computation into a quasiquotation.
`(+ 1 ,(+ 1 1)) is expanded by the reader into (append (quote (+)) (append (quote (1)) (append (list (+ 1 1)) (quote nil)))). Evaluating the expression above results in the list (+ 1 2).The comma-at operation is used to splice in the result of a computation (that returns a list) into a list.
(9 6 5 1 2 3 4 5).Evaluate data as an expression. The data must represent a valid expression.
Evaluate a list of data where each element represents an expression.
The type-of function returns a symbol that indicates what type the argument is. The form of a type-of expression is (type-of expr).
type-float. The sym-to-str function converts a symbol to its string representation. The resulting string is a copy of the original so you cannot destroy built in symbols using this function.
"lambda". The str-to-sym function converts a string to a symbol.
hello. The sym-to-u function returns the numerical value used by the runtime system for a symbol.
The u-to-sym function returns the symbol associated with the numerical value provided. This symbol may be undefined in which case you get as result a unnamed symbol.
The is-funamental function returns true for built-in functions.
Conditionals are written as (if cond-expr then-expr else-expr). If the cond-expr evaluates to nil the else-expr will be evaluated. for any other value of cond-expr the then-expr will be evalated.
You create an anonymous function with lambda. The function can be given a name by binding the lambda expression using define or let. A lambda expression has the form (lambda param-list body-expr).
(inc 10) computes the result 11.A lambda expression evaluates into a closure which is very similar to a lambda but extended with a captured environment for any names unbound in the param-list appearing in the body-expr. The form of a closure is (closure param-list body-exp environment).
(a . 1) appears in the closure. Local environments are created using let. The let binding in lispbm allows for mutually recursive bindings. The form of a let is (let list-of-bindings body-expr) and evaluating this expression means that body-expr is evaluted in an environment extended with the list-of-bindings.
You can give names to values in a global scope by using define. The form of define is (define name expr). The expr is evaluated and it is the result of the evaluated expr that is stored in the environment. In lispbm you can redefine already defined values.
The progn special form allows you to sequence a number of expressions. The form of a progn expression is:
The evaluation result of a progn sequence is the value that the last exprN evaluated to. This is useful for sequencing of side-effecting operations.
(a 10) and (b 20) created using define.Parses a string resulting in either an expression or the read_error in case the string can not be parsed into an expression. The form of a read expression is (read string).
Parses a string containing multiple sequenced expressed. The resulting list of expressions can be evaluated as a program using eval-program. The form of a read-program expression is (read-program string).
(apa 1).Lists are build using cons cells. A cons cell is represented by the lbm_cons_t struct in the implementation and consists of two fields named the car and the cdr. There is no special meaning associated with the car and the cdr each can hold a lbm_value. See cons and list for two ways to create structures of cons cells on the heap.
Use car to access the car field of a cons cell. A car expression has the form (car expr).
car of a number of symbol type is in general a type_error. The following program results in type_error. car operation accesses the head element of a list. The following program evaluates to 9. Use cdr to access the cdr field of a cons cell. A cdr expression has the form (cdr expr).
cdr operation gives you the rest of a list. The example below evaluates to the list (8 7). The cons operation allocates a cons cell from the heap and populates the car and the cdr fields of this cell with its two arguments. The form of a cons expression is (cons expr1 expr2).
(1 2 3) using cons. nil terminates a proper list. (+ . 1) using cons. The dot, ., operation creates a pair. The form of a dot expression is (expr1 . expr2). By default the evaluator will attempt to evaluate the result of (expr1 . expr2) unless it is prefixed with '.
The list function is used to create proper lists. The function takes n arguments and is of the form (list expr1 ... exprN).
The append function combines two lists into a longer list. An append expression is of the form (append expr1 expr2).
Index into a list using the ix. the form of an ix expression is (ix list-expr index-expr). Indexing starts from 0 and if you index out of bounds the result is nil.
The set-car is a destructive update of the car field of a cons-cell.
apa to be the pair (1 . 2) apa pair is now (42 . 2).The set-cdr is a destructive update of the cdr field of a cons-cell.
apa to be the pair (1 . 2) apa pair is now (1 . 42).Read one or many elements from an array. The form of an array-read expression is either (array-read array-expr index-expr) of (array-read array-expr start-index-expr end-index-expr) for reading a range of values into a list.
(#e #l #l).The array-write function performs a destructive update of an array.
Pattern-matching is expressed using match. The form of a match expression is (match expr (pat1 expr1) ... (patN exprN)). Pattern-matching compares the shape of an expression to each of the pat1 ... patN and evaluates the expression exprM of the pattern that matches. In a pattern you can use a number of match-binders or wildcards: _, ?, ?i28, ?u28, ?float, ?cons.
The underscore pattern matches anything.
i-dont-know The ? pattern matches anything and binds that anything to variable. Using the ? pattern is done as (? var) and the part of the expression that matches is bound to var.
The ?i28 pattern matches any i28 and binds that value to a variable. Using the ?i28 pattern is done as (?i28 var) and the part of the expression that matches is bound to the var.
not-an-i28. The ?u28 pattern matches any u28 and binds that value to a variable. Using the ?u28 pattern is done as (?u28 var) and the part of the expression that matches is bound to the var.
The ?float pattern matches any float and binds that value to a variable. Using the ?float pattern is done as (?float var) and the part of the expression that matches is bound to the var.
Use spawn to spawn a concurrent task. The concurrency implemented by LispBM is called cooperative concurrency and it means that processes must sleep using yield or they will starve out other processes. The form of a spawn expression is (spawn closure arg1 ... argN) The return value is a process ID.
Use wait to wait for a spawned process to finish. The argument to wait should be a process id. The wait blocks until the process with the given process id finishes.
To put a process to sleep, call yield. The argument to yield is number indicating at least how many microseconds the process should sleep.
Messages can be sent to a process by using send. The form of a send expression is (send pid msg). The message, msg, can be any LispBM value.
To receive a message use the recv command. A process will block on a recv until there is a matching message in the mailbox.
The recv syntax is very similar to match.
lispBM macros are created using the macro keyword. A macro is quite similar to lambda in lispBM except that arguments are passed in unevaluated. Together with the code-splicing capabilities given by quasiquotation, this provides a powerful code-generation tool.
A macro application is run through the interpreter two times. Once to evaluate the body of the macro on the unevaluated arguments. The result of this first application should be a program. The resulting program then goes through the interpreter again to compute final values.
Given this repeated evaluation, macros are not a performance boost in lispbm. Macros are really a feature that should be used to invent new programming abstractions in cases where it is ok to pay a little for the overhead for benefits in expressivity.
The form of a macro expression is: (macro args body)
defun operation for defining functions with a bit less typing. The example below defines a defun macro. inc that adds 1 to its argument can be defined as: "Call with current continuation" is called call-cc in LBM. Call with current continuation saves the "current continuation", which encodes what the evaluator will do next, into an object in the language. This encoded continuation object behaves as a function taking one argument.
The call-cc should be given a function, f, as the single argument. This function, f, should also take a single argument, the continuation. At any point in the body of f the continuation can be applied to a value, in essense replacing the entire call-cc with that value. All side-effecting operations operations up until the application of the continuation will take effect.
From within a call-cc application it is possible to bind the continuation to a global variable which will allow some pretty arbitrary control flow.
progn facility that allows returning at an arbitrary point. do below makes use of print which is not a built-in feature of lispBM. There are just to many different ways a programmer may want to implement print on an microcontroller. Use the lispBM extensions framework to implement your own version of print Unparsable symbols cannot be written into a program. The unparsable symbols signals different kinds of error conditions that may point at something being wrong in the code (or that it is exhausting all resources).
The no_match symbol is returned from pattern matching if no case matches the expression.
The read_error symbol is returned if the reader cannot parse the input code.
The type_error symbol is returned byt built-in functions if the values passed in are of incompatible types.
The eval_error symbol is returned if evaluation could not proceed to evaluate the expression. This could be because the expression is malformed.
The out_of_memory symbol is returned if the heap is full and running the garbage collector was not able to free any memory up. The program uses more memory than the size of the heap. Make the heap larger.
The fatal_error symbol is returned in cases where the LispBM runtime system cannot proceed. Something is corrupt and it is not safe to continue.
The out_of_stack symbol is returned if the evaluator runs out of continuation stack (this is its runtime-stack). You are most likely writing a non-tail-recursive function that is exhausting all the resources.
The division_by_zero symbol is returned when dividing by zero.
The variable_not_bound symbol is returned when evaluating a variable (symbol) that is neighter bound nor special (built-in function).
1.8.17