Challenge
Your challenge is to design an interpreter for a lisp-like language, which will from hence forth be coined: GLisp. The program code for GLisp will consist of an arbitrary amount of nested expressions denoted by brackets, in the following form:
(func arg1 arg2 ...)
Note that the interpreter must allow for extraneous whitespace characters before and after brackets, functions, and arguments.
Types
You will implement four types, Integer, List, Boolean, and Function. Integers and Boolean values can be explicitly inserted into source code with their own syntax. Your interpreter must assume that a run of numeric characters denotes an Integer (you do not have to implement a syntax to explicitly insert negative integers). Your interpreter must also assume that true
and false
are designated Boolean values. Functions can not be explicitly defined by the user, and will always return a single value (a List of any length counts as a single value).
Functions
The following functions are required to be implemented, and are in the format Function, Arity. If an Arity n
is proceeded by a plus sign, then that denotes n
or more arguments. You may assume that all arguments given to a function are of the same type, unless specificed otherwise. You may also assume that if no behaviour is specified for a certian type, then you may assume that no argument of that function will ever be of that type. Arguments will be referred to as in the following diagram:
(func argument1 argument2 ... argumentn)
+, 2+
- If all arguments are of the type Integer, you must return the sum of the arguments
- If all arguments are of the type List, you must return the concatenation of the arguments in ascending order (
arg1+arg2+ ...
) - If all arguments are of the type Boolean, you must return the logical All of the sequence of arguments
(+ 1 2 3 4 5) -> 15
(+ (list 1 2) (list 3 4)) -> (list 1 2 3 4)
(+ true true true) -> true
-, 2+
- If all arguments are of the type Integer, you must return the difference of the arguments (
arg1-arg2- ...
) - If all arguments are of the type Boolean, you must return the logical Any of the sequence of arguments
(- 8 4 3) -> 1
(- 0 123) -> -123
(- true false false true false) -> true
- If all arguments are of the type Integer, you must return the difference of the arguments (
*, 2+
- If all arguments are of type Integer, you must return the product of the arguments
- If one argument is of type List and the other is of type Integer (you may assume that these will only be the only arguments given), you must return a new List with the items in
arg1
repeatedarg2
times. (* 1 2 3 4 5) -> 120
(* (list 1 2 3) 2) -> (list 1 2 3 1 2 3)
/, 2+
- If all arguments are of the type Integer, you must return the quotient of the arguments (
arg/arg2/ ...
) (you may assume that division is done sequentially, and that the decimal portion at every step is truncated) - If one argument is of type List and the other is of type Function, then you must return the resulting List after
arg2
has been mapped over every value (/ 100 10 3) -> 3
(/ (list 1 2 3) inc) -> (list 2 3 4)
- If all arguments are of the type Integer, you must return the quotient of the arguments (
%, 2
- If all arguments are of the type Integer, you must return the modulus of the arguments
(% 4 2) -> 0
=, 2+
- If both the type and value of all arguments is the same, you must return true. Otherwise, return false.
(= 0 0 0) -> true
(= 0 false (list)) -> false
list, 0+
- You must return a list of all arguments, regardless of type. If no arguments are given, then you must return an empty list
(list 3 4 (list 5)) -> (list 3 4 (list 5))
inc, 1
- If argument is of type Integer, you must return the Integer incremented by one
- If argument is of type List, you must return the List rotated clockwise a single rotation
(inc 1) -> 2
(inc (list 1 2 3)) -> (list 3 1 2)
dec, 1
- If argument is of type Integer, you must return the Integer decremented by one
- If argument is of type List, you must return the List rotated counter-clockwise a single rotation
(dec 1) -> 0
(dec (list 1 2 3)) -> (list 2 3 1)
if, 3
- If given three arguments of any type: If the truth value of
arg1
is true, returnarg2
, else returnarg3
(if (not (list 1)) 8 false) -> false
- If given three arguments of any type: If the truth value of
not, 1
- If given an argument of any type, if the truth value of
arg1
is False, returntrue
, else returnfalse
. (not (list)) -> true
- If given an argument of any type, if the truth value of
len, 1
- If given an argument of type List, return the length of
arg1
(len (list 4 2 true (list 3) (list))) -> 5
- If given an argument of type List, return the length of
Truth table:
0, (list), false -> false
, where (list)
denotes an empty list. Everything else is true
.
Your interpreter can either be a full program that reads the source input from stdin or a file, or a function which takes the source as a string and returns the output value.
If choosing the former, the output for Integers is simply numbers, for Booleans is true
or false
, and for lists is a space separated sequence of values enclosed in brackets (eg. (1 2 3 4 (5 6 7))
denotes (list 1 2 3 4 (list 5 6 7))
).
If choosing the latter, the value must be returned in the implementation language's corresponding type, or, if no similar type exists, a custom type. Lists can be returned as Arrays or Vectors if the language doesn't have a List type, Booleans should be returned as a Boolean type in the language, or a custom type if the language does not support them.
Test cases
(list 1 2 3 (list 4 5 true)) -> (1 2 3 (4 5 true))
(/ 4000 (+ 1 2 3 4 (* 5 8))) -> 80
(+ (not (- (len (list 5 6 7)) (/ 10 3))) true) -> true
(if ( len (list ) ) 4 (if (+ (= 8 8 8) (not (list 4))) 8 5)) -> 5
Clarifications
- Your interpreter may deal with invalid input in any way you choose, but it must not throw an exception (though, it may print an error message and exit smoothly)
- Functions will always evaluate arguments left to right
- Invalid input is any input which is syntactically incorrect. This includes, but is not limited to, mismatched brackets, division by zero, and partially applied functions (unless going for the bonus)
- For
=
, if any of the values are different or any of the types are different, returnfalse
Bonuses
- Score * 0.8 if you support partially applied functions. For example,
((+ 2) 3)
would be the same as(+ 2 3)
, but allows for things such as(/ (list 1 2 3) (+ 2))
. You may assume that a function is partially applied if it receives less than its minimum number of arguments - Score * 0.85 if you do not evaluate the arguments applied to
if
unless they are going to be returned
This is code-golf, so the interpreter with the lowest byte count wins!
(if (not (array 1)) 8 false) -> false
? \$\endgroup\$(+ 3 (if false 5))
? Generally speaking, what really is "returning nothing"? You didn't specify any unit type to be retuned \$\endgroup\$(+ bool bool...)
logical AND and(- bool bool...)
logical OR? The standard ring notation would use+
for OR and*
for AND. 2. Is "invalid input" intended to cover cases like(/ 2 0)
which are syntactically correct? 3. For=
, if the values are not all the same, should it returnfalse
? 4. The definition ofnot
appears to be backwards. 5. What are the tokens? You say that the interpreter must handle extra whitespace, but you don't say what whitespace it can rely on. For complex questions like this, you really should use the sandbox so that the spec can be checked. \$\endgroup\$((+ 2 3) 4)
equal to9
or an error? Notably, for var-arg functions, it isn't clear when one should consider the application partial. It gets even muddier with things like((if true (+ 2 3) (- 5)) 4)
\$\endgroup\$