class Data abstract
Interpreter Backend
1 Objective
2 Data
Scalar types: String, numbers
Aggregated types: List, dictionary, records
object NullData: Data() {
override fun toString(): String = "NULL"
}
NullData
NULL
class IntData(val value:Int): Data() {
override fun toString(): String = "Int:$value"
}
(12) IntData
Int:12
class StringData(val value:String): Data() {
override fun toString(): String =
if(value.length > 10) {
"String:\"${value.substring(0, 10)}...\""
} else {
"String:\"$value\""
}
}
("Hello world, again and again") StringData
String:"Hello worl..."
class BooleanData(val value:Boolean): Data() {
override fun toString() =
"Boolean:${value}"
}
(false) BooleanData
Boolean:false
3 Runtime
The runtime is the environment in which the program can execute. It provides the following services:
Storage of data. This is typically lookup table, known as the symbol table. The symbol table corresponds to the concept of scopes
Access to hardware. Runtime provides function calls that allow controlled access to hardware such as filesystem and networking.
Runtime can make copy of itself when creating sub-scopes, or sandboxed environments.
class Runtime() {
val symbolTable:MutableMap<String, Data> = mutableMapOf()
fun subscope(bindings:Map<String, Data>):Runtime {
val parentSymbolTable = this.symbolTable
return Runtime().apply {
.putAll(parentSymbolTable)
symbolTable.putAll(bindings)
symbolTable}
}
override fun toString():String =
.map {
symbolTable-> "${entry.key} = ${entry.value}"
entry }.joinToString("; ")
}
()
Runtime.subscope(
(
mapOf"a" to IntData(42),
"b" to StringData("Hello")
)
)
a = Int:42; b = String:"Hello"
4 Programming starts here…
Programming is instructing the runtime environment to perform certain tasks. We will cover each of the programming constructs and show how they are represented as classes and objects.
4.1 Expression
class Expr {
abstract fun eval(runtime: Runtime): Data
abstract }
object NullExpr: Expr() {
override fun eval(runtime:Runtime): Data = NullData
}
4.2 Literals
class IntLiteral(val lexeme:String): Expr() {
override fun eval(runtime:Runtime): Data
= IntData(Integer.parseInt(lexeme))
}
("42").eval(Runtime()) IntLiteral
Int:42
class StringLiteral(val lexeme:String): Expr() {
override fun eval(runtime:Runtime): Data
= StringData(lexeme)
}
("Hello world").eval(Runtime()) StringLiteral
String:"Hello worl..."
class BooleanLiteral(val lexeme:String): Expr() {
override fun eval(runtime:Runtime): Data
= BooleanData(lexeme.equals("true"))
}
("true").eval(Runtime()) BooleanLiteral
Boolean:true
4.3 Arithmetic operations
enum class Operator {
,
Add,
Sub,
Mul
Div}
class Arithmetics(
val op:Operator,
val left:Expr,
val right:Expr
): Expr() {
override fun eval(runtime:Runtime): Data {
val x = left.eval(runtime)
val y = right.eval(runtime)
if(x !is IntData || y !is IntData) {
throw Exception("cannot handle non-integer")
}
return IntData(
when(op) {
.Add -> x.value + y.value
Operator.Sub -> x.value - y.value
Operator.Mul -> x.value * y.value
Operator.Div -> {
Operatorif(y.value != 0) {
.value / y.value
x} else {
throw Exception("cannot divide by zero")
}
}
else -> throw Exception("Unknown operator")
}
)
}
}
(
Arithmetics.Mul,
Operator("42"),
IntLiteral("2")
IntLiteral)
.eval(Runtime())
Int:84
(
Arithmetics.Mul,
Operator(
Arithmetics.Add,
Operator("20"),
IntLiteral(
Arithmetics.Div,
Operator("10"),
IntLiteral("5"),
IntLiteral)
),
("3")
IntLiteral)
.eval(Runtime())
Int:66
4.4 Accessing the scope
4.4.1 update symbol table
class Assign(val symbol:String, val expr:Expr): Expr() {
override fun eval(runtime:Runtime): Data
= expr.eval(runtime).apply {
.symbolTable.put(symbol, this)
runtime}
}
val expr = Assign(
"x",
(Operator.Add,
Arithmetics("1"),
IntLiteral("2"))
IntLiteral)
val runtime = Runtime()
("Before: $runtime")
println.eval(runtime)
expr("After: $runtime") println
Before:
After: x = Int:3
= 1 + 2 x
4.4.2 reading from symbol table
class Deref(val name:String): Expr() {
override fun eval(runtime:Runtime):Data {
val data = runtime.symbolTable[name]
if(data == null) {
throw Exception("$name is not assigned.")
}
return data
}
}
(
Arithmetics.Mul,
Operator("x"),
Deref("2"),
IntLiteral)
.eval(runtime)
Int:6
* 2 x
4.5 Code blocks
class Block(val exprList: List<Expr>): Expr() {
override fun eval(runtime:Runtime): Data {
var result:Data = NullData
.forEach {
exprList= it.eval(runtime)
result }
return result
}
}
(
Block(
listOf("x", IntLiteral("42")),
Assign("y", IntLiteral("2")),
Assign("z",
Assign(
Arithmetics.Mul,
Operator("x"),
Deref("y")
Deref)
),
("z")
Deref)
)
.eval(Runtime())
Int:84
= 42
x = 2
y = x * 2
z z
5 More expressions: Flow control constructs
enum class Comparator {
,
LT,
LE,
GT,
GE,
EQ,
NE}
class Compare(
val comparator: Comparator,
val left: Expr,
val right: Expr
): Expr() {
override fun eval(runtime:Runtime): Data {
val x = left.eval(runtime)
val y = right.eval(runtime)
if(x is IntData && y is IntData) {
return BooleanData(
when(comparator) {
.LT -> x.value < y.value
Comparator.LE -> x.value <= y.value
Comparator.GT -> x.value > y.value
Comparator.GE -> x.value >= y.value
Comparator.EQ -> x.value == y.value
Comparator.NE -> x.value != y.value
Comparator}
)
} else {
throw Exception("Non-integer data in comparison")
}
}
}
(
Compare.LT,
Comparator("2"),
IntLiteral("1")
IntLiteral)
.eval(Runtime())
Boolean:false
5.1 If-else
class Ifelse(
val cond:Expr,
val trueExpr:Expr,
val falseExpr:Expr
): Expr() {
override fun eval(runtime:Runtime): Data {
val cond_data = cond.eval(runtime)
if(cond_data !is BooleanData) {
throw Exception("need boolean data in if-else")
}
return if(cond_data.value) {
return trueExpr.eval(runtime)
} else {
return falseExpr.eval(runtime)
}
}
}
(
Block(
listOf("x", IntLiteral("2")),
Assign("y", IntLiteral("1")),
Assign(
Ifelse(
Compare.LT,
Comparator("x"),
Deref("y")
Deref),
("x < y"),
StringLiteral("y < x")
StringLiteral)
)
)
.eval(Runtime())
String:"y < x"
= 2
x = 1
y
if x < y:
"x < y"
else:
"y < x"
5.2 While loops
class While(val cond:Expr, val body:Expr): Expr() {
override fun eval(runtime:Runtime): Data {
var flag = cond.eval(runtime) as BooleanData
var result:Data = NullData
var iter:Int = 1_000_000
while(flag.value) {
= body.eval(runtime)
result = cond.eval(runtime) as BooleanData
flag if(iter == 0) {
("MAX_ITER reached")
println(runtime)
printlnreturn NullData
}
--
iter }
return result
}
}
(
Block(
listOf("x", IntLiteral("0")),
Assign("i", IntLiteral("0")),
Assign(
While(Comparator.LE, Deref("i"), IntLiteral("10")),
Compare(
Block(
listOf(
Assign"x",
(Operator.Add, Deref("x"), Deref("i"))
Arithmetics),
(
Assign"i",
(Operator.Add, Deref("i"), IntLiteral("1")
Arithmetics)
),
)
)
),
("x")
Deref)
).
(Runtime()) eval
Int:55
= 0
x = 0
i while i < 10:
= x + i
x = i + 1
i x
5.3 Function Declaration
class FuncData(
val name: String,
val params: List<String>,
val body: Expr
): Data() {
override fun toString()
= params.joinToString(", ").let {
"$name($it) { ... }"
}
}
("add", listOf("i", "j"), NullExpr) FuncData
add(i, j) { ... }
class Declare(
val name: String,
val params: List<String>,
val body: Expr
): Expr() {
override fun eval(runtime:Runtime):Data
= FuncData(name, params, body).also {
.symbolTable[name] = it
runtime}
}
val r = Runtime()
(listOf(
Block("add", listOf("x", "y"), NullExpr)
Declare))
.eval(r)
r
add = add(x, y) { ... }
5.4 Invoke function
class Invoke(val name:String, val args:List<Expr>):Expr() {
override fun eval(runtime:Runtime):Data {
val func:Data? = runtime.symbolTable[name]
if(func == null) {
throw Exception("$name does not exist")
}
if(func !is FuncData) {
throw Exception("$name is not a function.")
}
if(func.params.size != args.size) {
throw Exception(
"$name expects ${func.params.size} arguments "
+ "but received ${args.size}"
)
}
val r = runtime.subscope(
.params.zip(args.map {it.eval(runtime)}).toMap()
func)
return func.body.eval(r)
}
}
(
Block(
listOf("x", IntLiteral("10")),
Assign("y", IntLiteral("20")),
Assign("add",
Declare("i", "j"),
listOf(Operator.Add, Deref("i"), Deref("j"))),
Arithmetics("add",
Invoke(Deref("x"), Deref("y")))
listOf)
).eval(Runtime())
Int:30
= 10
x = 20
y
def add(i, j):
return i + j
add(x, y)