Jedi

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

A Jedi Session

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 Packages

Jedi consists of three interdependent packages:

Jedi Expressions

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.

Jedi Context

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.

Jedi Values

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.