A lisp like interpreter coding challenge I developed for my company's 2025 campus recruitment hackathon.
It's built using Scala, Kotlin and SpringBoot and uses GitLab CI for continuous integration. Can also find my own solution to the problem inside ex-gen
(short for expression generator).
Note: Students highly appreciated the problem as it forced them to think outside their usual imperative programming approach.
You are given multiple lines of code written in a made up lisp-like programming language. Your task is to write a simple interpreter given the definition of the available functions, interpret the code we will provide and return to us everything printed to the console. In case a program terminates with an error, return everything printed to the console before the error and the printed error message. Error message is printed to the console automatically when error is thrown.
Assumptions:
- The input consists only functions defined in this document.
- Each function invocation is wrapped in brackets (see examples below).
- There is a space between function name and argument(s). Function arguments are space separated.
- Functions never mutate original arguments but instead return a copy.
- Calling a function with an incorrect number of arguments will result in an error.
- If a single expression raises multiple errors, you can treat it as a single error.
- No semicolons at the end of the line.
//
in the examples is used to denote a comment. It will not appear in the actual code.
- String: provided in double quotes (
"
) - Boolean:
true
andfalse
- Number: can be integer or decimal.
- null
Can be done with method puts
which accepts a single String argument. Providing an argument of any type other than String will result in an error.
This function returns null
Example:
Input:
(puts "hello world")
Output:
hello world
set
function accepts a constant variable name and a single argument of any type. Returns null
.
Variable names will be given in camel case containing only alphabets.
Example:
(set x 5)
Assigning a new value to an existing variable will result in an error. Incorrect order of the arguments will result in an error.
concat
function accepts 2 arguments of String type. Passing argument of any other type will result in an error.
Returns a new String created by appending second argument to the first.
Example:
(concat "ab" "c")
// returns: "abc"
lowercase
function returns a copy of an input String converted to lower case. Providing an argument of any type other than String will result in an error.
Example:
(lowercase "ABC")
// returns "abc"
uppercase
function returns a copy of an input String converted to upper case. Providing an argument of any other type than string will result in an error.
Example:
(uppercase "abc")
// returns "ABC"
replace
function returns a new String obtaining by replacing each substring of target in source with replacement String. Source String remains unchanged
Arguments:
- source: String
- target: String
- replacement: String
If target String is not present in the source, returns source String
Providing argument(s) of any type other than String will result in an error.
Example:
(replace "abcdef" "abc" "123")
// returns "123def"
(replace "abc" "xyz" "123")
// returns "abc"
substring
function returns a substring specified by given range indices with start inclusive and end index exclusive.
Arguments:
- source: String
- start: Non-negative number, inclusive
- end: Non-negative number, exclusive
First character of String is at index 0.
Example:
(substring "abcdef" 0 3)
// returns "abc"
Providing argument(s) of incorrect type will result in an error. When at least one of the indices is out of bounds an error is thrown.
NOTE: Each function below should return result accurate to 4 dps.
add
function accepts at least 2 arguments of numeric type and returns a new number by adding all arguments.
Example:
(add 1 2)
// returns 3
(add 1 2 3 4 5)
// returns 15
Providing at least one argument of a type other than number will result in an error.
subtract
function accepts at 2 arguments of numeric type and returns a new number by subtracting the second argument from the first one.
Example:
(subtract 10 2)
// returns 8
(subtract 1 2)
// returns -1
Providing at least one argument of a type other than number will result in an error.
multiply
function accepts at least 2 arguments of numeric type and returns a new number by multiplying all arguments.
Example:
(multiply 2 3)
// returns 6
(multiply 2.0 3)
// returns 6.0
(multiply 2 2.5)
// returns 5.0
(multiply 1 2 3 4 5)
// returns 120
Providing at least one argument of a type other than number will result in an error.
divide
function accepts two arguments of numeric type: dividend and divisor. Providing at least one argument of a type other than number will result in an error. If both operands are integer then it should perform integer division. Division by zero will result in an error.
Example:
(divide 6 2)
// returns 3
(divide 1 2)
// returns 0
(divide 1.0 2)
// returns 0.5
abs
function accepts a single argument of numeric type. Returns an absolute value of the provided argument. Providing an argument of a type other than number will result in an error.
Example:
(abs -1)
// returns 1
(abs 1)
// returns 1
max
function accepts a variable number of arguments (at least 2) of numeric type. Returns the largest number among the provided arguments.
Providing no arguments will result in an error.
Providing an argument of a type other than number will result in an error.
Example:
(max 1 -2)
// returns 1
(max 1 2 3 4 5)
// returns 5
min
function accepts a variable number of arguments (at least 2) of numeric type. Returns the smallest number among the provided arguments.
Providing no arguments will result in an error.
Providing an argument of a type other than number will result in an error.
Example:
(min 1 2)
// returns 1
(min 5 4 3 2 1)
// returns 1
gt
function accepts 2 numeric arguments. Returns true
if the first argument has greater value than the second, otherwise returns false
.
Providing no arguments will result in an error.
Providing an argument of a type other than number will result in an error.
Example:
(gt 1 2)
// returns false
(gt 2 1)
// returns true
lt
function accepts 2 numeric arguments. Returns true
if the first argument has smaller value than the second, otherwise returns false
.
Providing an argument of a type other than number will result in an error.
Providing no arguments will result in an error.
Providing an argument of a type other than number will result in an error.
Example:
(lt 1 2)
// returns true
(lt 2 1)
// returns false
For Number operations involving decimals please keep your console outputs to minimum required decimal points (no trailing 0s except for the first) with a max of 4. For example:
(add 10.0 10) should return 20.0 (not 20)
(divide 10 3.0) should return 3.3333
(divide 10 4.0) should return 2.5
Can be performed on String, Number and null
equal
returns true if value and type of two arguments are equal.
Arguments:
- first: String | Number | Boolean | null
- second: String | Number | Boolean | null
Examples:
(equal 2 2.0)
// returns true
(equal 2 "2")
// returns false
(equal null null)
// returns true
not_equal
returns true if value or type of two arguments are different.
Arguments:
- first: String | Number | Boolean | null
- second: String | Number | Boolean | null
Examples:
(not_equal 2 2.0)
// returns false
(not_equal 2 "2")
// returns true
(not_equal null 5)
// returns true
str
function accepts a single argument of String, Number, Boolean types or null and converts it to String.
Examples:
(str 5)
// returns "5"
(str null)
// returns "null"
All errors are unrecoverable (i.e. the evaluation stops on the first error). Once the error occurs, the error message is printed to the console.
The error message contains a String ERROR at line
and line number where error was raised.
Example:
Input:
(divide 1 0) // line 1
Output:
ERROR at line 1
Your application must expose /lisp-parser endpoint which will accept POST requests in the same format as specified in the examples below.
(puts "Hello World")
(puts (str 5))
(puts (concat "ABC " (str true)))
Expected result:
Hello World
5
ABC true
(set v "Hello World")
(puts (concat v ", Student"))
Expected result:
Hello World, Student
Your application must be able to accept HTTP POST request with body containing JSON payload in the following format:
{
"expressions": ["String array"]
}
As shown in the problem definition, the input code can span multiple lines. You will receive input as a JSON key/value pair,
where value is a String array, as in this example. Keep in mind that quotes("
) will be escaped in the input your application will receive:
{
"expressions": [
"(puts \"Hello\")",
"(puts \"World!\")"
]
}
Your application HTTP response body must contain JSON in the following format:
{
"output": ["String array"]
}
As shown in the problem definition, console output may contain multiple lines. You must provide output as a JSON key/value pair, where value is a String array as in this example:
{
"output": [
"Hello",
"World!"
]
}