Crafting a visualizer for an interpreter of a toy langguage



Intro

Linkg to the demo: Interpereter visualizer

I experimented with Dart for desktop and web development, and I wanted to build a non-trivial application to stress-test whether it could handle a more complex project. For quite some time, I’d had an idea to build a visualizer for a programming language, and it seemed like a project with an appropriate difficulty level for this purpose.

For this project, there were a few possible languages to visualize. Some were eliminated due to their complexity, and others were too dynamic. Lua was a good candidate, but it didn’t feel quite right. Instead, we’ll use a toy language named Lox. Its grammar is stable and well-defined, it covers most of the core concepts found in modern popular languages, and—most importantly—there’s a book by Robert Nystrom that describes how to build your own implementation if you’re interested.

I aimed to keep the language exactly as described in the book, with code very similar to Robert’s version. Below, I’ll share the grammar and a brief explanation of how everything fits together.

For now, I’ll leave it here. This grammar is primarily to share information about the language with my friends and explain how to use it. Later, I’ll expand this article with more details.

Grammar

program        → declartion* EOF ;
declaration    → classDecl
               | funDecl
               | varDecl
               | statement ;
classDecl      → "class" IDENTIFIER ( "<" IDENTIFIER )?
                 "{" function* "}" ;
funDecl        → "fun" function ;
function       → IDENTIFIER "(" parameters? ")" block ;
parameters     → IDENTIFIER ( "," IDENTIFIER )* ;
statement      → exprStmt
               | forStmt
               | ifStmt
               | printStmt
               | returnStmt
               | whileStmt
               | block ;
returnStmt     → "return" expression? ";" ; 
forStmt        → "for" "(" ( varDecl | exprStmt | ";" )
                 expression? ";"
                 expression? ")" statement ;
whileStmt      → "while" "(" expression ")" statement ;
ifStmt         → "if" "(" expression ")" statement
               ( "else" statement )? ;
block          → "{" declaration* "}" ;
exprStmt       → expression ";" ;
printStmt      → "print" expression ";" ;
varDecl        → "var" IDENTIFIER ( "=" expression )? ";" ;



expression     → assignment ;
assignment     → ( call "." )? IDENTIFIER "=" assignment
               | logic_or ;
logic_or       → logic_and ( "or" logic_and )* ;
logic_and      → equality ( "and" equality )* ;
equality       → comparison ( ( "!=" | "==" ) comparison )* ;
comparison     → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
term           → factor ( ( "-" | "+" ) factor )* ;
factor         → unary ( ( "/" | "*" ) unary )* ;
unary          → ( "!" | "-" ) unary | call ;
call           → primary ( "(" arguments? ")" | "." IDENTIFIER )* ;
arguments      → expression ( "," expression )* ;
primary        → "true" | "false" | "nil" | "this"
               | NUMBER | STRING | IDENTIFIER | "(" expression ")"
               | "super" "." IDENTIFIER ;

Statements

StmtVar
StmtWhile
StmtBlock
StmtClass
StmtExpression
StmtFunction
StmtIf
StmtPrint
StmtReturn

Expressions

ExprLiteral
ExprBinary
ExprVariable
ExprAssign
ExprCall
ExprGet
ExprGrouping
ExprLogical
ExprSet
ExprThis
ExprSuper
ExprUnary

Sample code

var a = 3.1415;
var b = -1;
var c = "abc and xyz";

for(var i = 0; i < 10; i = i + 1) {
  print i;
}

var counter = 0;
while(counter < 10) {
    counter "Counting";
    counter = counter + 1;
}

if(a < 10) {
  print "there is more to go";
} else {
  print "we are over our budget";
}

fun generator(n) {
    int someNumber = 17;
    fun count() {
        return ((someNumber * n) - 11) / 7) * n;
    }
    return count;
}
var someNumber1 = generator(117);
var someNumber2 = generator(25);

print someNumber1();
print someNumber2();



class Animal {
    act() {
        print "Acting like an animal";
    }
}

class Dog {
    init(name) {
        this.name = name;
    }

    act() {
        super.act();
        print "Just barking";
        print "And people call me" + this.name;
    }
}

var dog = Dog("Charlie");
dog.act();