Jedi is an experimental language that supports many features found in modern programming languages:
· Lambdas, closures, and static scoping
· Eager and lazy execution
· Blocks
· Variables
· Objects
· Dynamic type checking
Jedi's read-execute-print loop (REPL) repeatedly:
1. reads an expression string entered by the user,
2. parses it into an expression object,
3. executes the object,
4. then prints the resulting value
Here's a sample session:
-> 2 + 3 * 5
17
-> 2.0 + 3 * 5
17.0
-> def pi = 3.14
ok
-> pi
3.14
-> def square = lambda(x) x * x
ok
-> square(7)
49
-> def area = lambda(r) pi * square(r)
ok
-> area(10)
314.0
-> pi < 3 && z < 10
false
-> z
Undefined identifier: z
-> if (pi < 3) x else 100
100
-> x
Undefined identifier: x
-> def count = var(0)
ok
-> count
Variable(0.0)
-> [count]
0.0
-> count = [count] + 1
done
-> [count]
1.0
-> while([count] < 100) count = [count] + 1
done
-> [count]
100.0
-> write("Hello world")
Hello world
done
-> quit
bye
Notes:
· Multiplication has higher precedence than addition
· Conjunction (&&) and disjunction (||) use short-circuit execution (a form of lazy execution)
· Conditionals (if) use conditional execution (another form of lazy execution)
Jedi consists of three interdependent packages:
Notes:
· Executing a Jedi expression requires an environment (i.e. linked hash tables) and produces a value.
· FunCalls (i.e., function calls or applications) use eager execution by default-- all operands (i.e., inputs) are executed, even if they don't need to. However, flags allow users to change this to pass-by-name or pass-by-text, two forms of lazy execution in which only needed operands are executed.
· Special forms use custom execution algorithms such as short-circuit or conditional execution.
· Declarations (i.e., definitions) are special forms that add bindings (i.e., name-value associations) to the environment.
· Identifiers are names of values. To execute an identifier we simply look it up in the environment.
· The value of a literal is literally itself.
Executing a Jedi program requires a virtual machine:
Notes:
· The console singleton provides the read-execute-print loop, the global environment, and a main method.
· The global environment is a hash table that associates identifiers to values. (E.g., pi = 3.14.) Initially it is empty. Executing declarations at the console's prompt will add bindings to the global environment.
· The console requires parsers to parse expression strings into expression objects (i.e., objects having the expression trait defined above).
· The console must be able to handle the different kinds of exceptions execution might throw.
· The ALU singleton provides Jedi's built-in functions for doing arithmetic, logic, and simple I/O.
Notes:
· Executing an expression relative to an environment produces a value.
· Closures are functions that remember their defining environments.
· A variable is a value that contains another value.
· A store is a buffer that contains values.
· Notifications are acknowledgements (e.g., "ok", "done", "unspecified") returned by expressions that produce side effects such as updating a variable, store, or environment.
· Literals are values and expressions. The value of a literal is itself.
· Literals encapsulate Scala values such as Int, Double, String, and Boolean.