Kotlin Language Features Related to Null Handling

Any software engineer with a Java background would find the null handling features in the Kotlin language interesting. Let's summarize this topic with some examples. Nullable types: In Kotlin, types are non-nullable by default. If you want a variable to be able to hold a null value, you need to explicitly declare its type as nullable using the Type? syntax. For example, String? denotes a nullable string, while String represents a non-nullable string. Safe calls (?.): Kotlin introduces the safe call operator (?.) for handling nullable types. It allows you to safely invoke a method or access a property on a nullable object. If the object is null, the expression returns null instead of throwing a NullPointerException. Example: data class Person(val name: String, val age: Int, val address: String?) fun main() {     // Create a person with a nullable address     val person1 = Person("John Doe", 25, "123 Main Street")     val person2 = Person("Jane Doe", 30,...

Interpreter for a Subset of Java Language (J-)

In this post we will have a look at the interpreter part of my project.

Before interpreting the source code, following things happened:
• Lexer built the tokens from source code.
• AST (Abstract Syntax Tree) was created.
• Parser traversed the AST and populated our Symbol Table

Interpreter should once again traverse the AST and execute the statements.

private Object exec(CommonTree statement) {

switch (statement.getType()) {
case ProgramWalker.BLOCK:
case ProgramWalker.MAINMETHOD:
block(statement);
break;
case ProgramWalker.PRINT:
print(statement);
sendSourceLineMessage(statement);
break;
case ProgramWalker.VARDECL:
currentSpace.put(statement.getChild(1).getText(), null);
sendSourceLineMessage(statement);
case ProgramWalker.METHOD:
break;
case ProgramWalker.INT:
return Integer.parseInt(statement.getText());
//...

When an AST node is a block of statements, then the following method is called:

private void block(CommonTree statement) {

List stats = statement.getChildren();

if (stats == null)
return;

for (int i = 0; i < stats.size(); ++i)
exec((CommonTree) stats.get(i));
}


These methods call each other until all root and leaf nodes are executed.

We could use the Symbol Table to set variable values and retreive them. But consider the situation where many methods call each other:
 
main() {
int a; a = 0;
foo(a);
print a;
}

int foo(int valFoo) {
int a; a = 10;
print a;
bar(666);
return 0;
}

int bar(int valBar) {
int a; a = 2;
print valBar;
print a;
return 0;
}

For any method call, scope of local variables are determined by the point of execution at runtime. A method can call itself recursively and on each call, we need to keep track of a new scope. This cannot be handled by Symbol Table. We need to keep track of “function spaces” and have a stack structure for them.

For the above code, the thread enters to the global space first. (main method)
Then it enters to the space of function “foo” and “bar”.

private Object exec(CommonTree statement) {

switch (statement.getType()) {
case ProgramWalker.ID:
MemorySpace memorySpace = getSpaceWithSymbol(statement.getText());

if (memorySpace == null) memorySpace = currentSpace;

return memorySpace.get(statement.getText());
//...
}

public MemorySpace getSpaceWithSymbol(String id) {
if (runtimeStack.size() > 0 && runtimeStack.peek().exists(id)) {
return runtimeStack.peek();
}

if (globals.exists(id)) return globals;

return null;
}
We clearly see the use of a stack of memory spaces which is sometimes called a “runtime stack”.

I use print statements to implement integration tests. Below is a test from j-minus:

@Test
public void shouldExecuteComplexProgram() throws Exception {

String sourceCode = "main() { " +
"int a; a = 5; " +
"while (a < 8) {" +
"show(a);" +
"a = a + 1;" +
"}" +
"}\n " +
"int show(int val) \n" +
"{ print val; val = val + 100; return 0;}";

ProgramWalker symbolTablePopulator = getProgramWalkerFor(sourceCode);
symbolTablePopulator.prog();

interpreter.setRoot(Util.getASTRoot(sourceCode));
interpreter.setSymTabStack(SymbolTablePopulator.instance().getSymTabStack());

interpreter.interpret();

verify(printStream, times(1)).println(5);
verify(printStream, times(1)).println(6);
verify(printStream, times(1)).println(7);
}
I should better read the source code from a text file. I ll do this soon.

Comments

Popular posts from this blog

Trie Data Structure and Finding Patterns in a Collection of Words

Virtual Memory

NOTES ON COMPUTER ARCHITECTURE: Some important concepts in computer architecture