KarolaScript

In my quest to better comprehend the fascinating intricacies of compilation process, compiler, and interpreter knowledge, I embarked on a journey of creating my own programming language - KarolaScript.

KarolaScript is a decidely bare-bones, yet enlightening endeavor, which brings the underpinnings of a language interpreter to the foreground. It doesn’t boast rich features like an interactive debugger or a sophisticated static analyzer, as its primary purpose is to serve as a learning playground.

KarolaScript, inspired by the fusion of Python and C, provides a procedurally-oriented, dynamically-typed programming environment. It distinguishes itself by recognizing classes and functions as first-class citizens; this allows for versatile usage scenarios such as assignment, passing as function parameters, and inclusion within complex data structures.

The present method of code execution in KarolaScript involves recursive evaluation of the AST. An upcoming plan includes developing a virtual machine and dedicated bytecode to enhance efficiency. Please note, the current feature list includes class and function operations, control flows, code blocks, and advanced parser capabilities. Updates will be made as progress continues.

Grammar

program           → declaration* EOF ;

declaration       → classDecl | funDecl | varDecl | statement ;


classDecl         → "clazz" IDENTIFIER ( "<" IDENTIFIER )?
                    "{" member* "}" ;

member            → classMethodDecl | methodDecl ;
classMethodDecl   → "class" IDENTIFIER "(" parameters? ")" block ;
methodDecl        → function ;

funDecl           → "funct" function ;

function          → IDENTIFIER "(" parameters? ")" block ;
parameters        → IDENTIFIER ( "," IDENTIFIER )* ;

anonFunDecl       → "funct" anonFunction ;
anonFunction      → "(" parameters? ")" block ;


varDecl           → "let" IDENTIFIER ( "=" expression )? ";" ;



statement         → exprStmt | forStmt | ifStmt | printStmt | returnStmt | whileStmt | block | breakStmt;

returnStmt        → "return" expression? ";" ;

forStmt           → "for" "(" ( varDecl | exprStmt | ";" )
                    expression? ";"
                    expression? ")" statement ;

whileStmt         → "while" "(" expression ")" statement ;

exprStmt          → expression ";" ;

ifStmt            → "if" "(" expression ")" statement
                  ( "else" statement )? ;

printStmt         → "console" expression ";" ;
block             → "{" declaration* "}" ;
breakStmt         → "break" ";" ;



expression        → anonFunDecl | assignment ;

assignment        → ( call "." )? IDENTIFIER "=" logic_or | commaExpr ;

commaExpr         → logic_or ( "," logic_or )* ;

logic_or          → logic_and ( "or" logic_and )* ;
logic_and         → ternaryExpr ( "and" ternaryExpr )* ;

ternaryExpr       → equality ( "?" equality ":" ternaryExpr )? ;



equality          → comparison ( ( "!=" | "==" ) comparison )* ;
comparison        → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
term              → factor ( ( "-" | "+" ) factor )* ;
factor            → unary ( ( "/" | "*" ) unary )* ;
unary             → unaryOperator* primary | call;

call              → primary ( "(" arguments? ")" | "." IDENTIFIER )* ;
arguments         → expression ( "," expression )* ;

primary           → errorPrimary | NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER | "super" "." IDENTIFIER ;
errorPrimary      → binaryOperator expression ;
unaryOperator     → ( "!" | "-" )
binaryOperator    → ( "!=" | "==" | ">" | ">=" | "<" | "<=" | "-" | "+" | "/" | "*" )

Standard library

KarolaScript stdlib consists of following functions:

  • console for standard output
  • input for standard input
  • clock for getting epoch time
  • sleep for thread sleep for n miliseconds
  • toUpper and toLower

There is also builtin Math clazz with static functions:

  • pwr takes first argument to the power of two
  • sqrr00t returns square root of a argument

Examples

Here are three code snippets with KarolaScript:

Basic operations

let a = 5;
let b = 2;

console "Hello World" + (a + b);

// console (10 / 0);

let name = "bug_digger";
name = toUpper(name);
console name;
name = toLower(name);
console name;

let suffix = "_hacker";
console name + suffix;


let truthy = false;
console (truthy ? a : (a + b));


console "== Loops section ==\n\n";

for (let i = 1; i < 10; i = i + 1) {
    console i;
}

let i = 0;
while (i <= 100) {
    console i;
    i = i + 10;
}

Functions and anonymous functions

let pass = false;

funct a () {
    if (pass) {
        console "Help from function A again!";
        return;
    }
    console "Hello from function A!";
    pass = true;
}

funct b() {
    a();
    console "Hello from function B!";
}

console "Regular hello";

a();
b();


console "\n\n\nAnonymous functions from here!!!\n\n\n";


funct test(fn) {
  for (let i = 1; i <= 10; i = i + 1) {
    fn(i);
  }
}

test(funct (a) {
  console a;
  console "Bug Digger is the best hacker!!!";
});

Classes and inheritance

clazz Vehicle {
  honk() {
    console "Tuut, tuut.";
  }

  engine() {
    console "Brm, brm.";
  }

  static startEngine() {
    console "Click, click!!!";
  }
}

clazz Car < Vehicle {
  engine() {
    console "Brrrrrmmm brmmmmmmmm brrrrmmmm";
  }
}

Vehicle.startEngine();

Car().engine();
Car().honk();

Metaclasses

Metaclasses in KarolaScript are a form of metaprogramming as they enable the creation, modification, or management of classes in runtime. So, a metaclass is essentially a class for a class – it defines how a class behaves. So, just as an ordinary object is an instance of a class, a class is an instance of a metaclass. Metaclasses allow you to change how classes are created, typically for API development or building a framework. However, they are generally considered complex to use in daily tasks. Therefore, they are not commonly used despite being a powerful tool. Most programmers prefer simpler, more comprehensible programming methods. Metaclasses can introduce unexpected behavior and complexity, which is why they are often avoided in typical programming tasks.

Currently exists only one metaclass called exactly MetaClazz and there is no way for user to create their own metaclass. That will change as project grows.

The motto “explicit is better than implicit” from the Zen of Python applies here. If metaprogramming tools like metaclasses are used, they should be applied with care and for a good reason.

Ideas for future enhancements

  • Number literals written in binary, hex, and octal
  • Pratt Parser for expressions (currently it works with recursive descent for both expressions and statements)
  • Arrays
  • Add more functions to stdlib and more stdlib classes
  • Improve error handling; Currently errors are only displayed on stderr, logging to a file will be a nice addition.
    • There is ErrorReporter.h file in utils folder. Some strategy design pattern can be used to select where to redirect errors and warnings.
  • Add VM with garbage collection and bytecode generation
  • Add lambda but make it work like in Python
  • Add possiblity for user to create his own metaclass; For example user-defined metaclasses can start with $clazz
  • Test cases
  • Allow multiple code files

Resources

https://github.com/realbugdigger/KarolaScript