Skip to content

harunsasmaz/loxpp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lox++

A C++ Implementation of Lox language

For more details about Lox, please check Crafting Interpreters Book Website.

Also, there is a repository of the book mentioned above, you can visit here.

Compile & Run

$ cmake .
$ make
$ ./loxpp samples/fib.lox

Lox Language

I will start with describing the syntax of the language, you can also find the abstract grammar at the end.

Hello Lox

Classic introduction to the language; here is your very first program:

print "Hello World!";

Data Types

Booleans

There are two Boolean values, obviously, and a literal for each one:

true;
false;

Numbers

Lox only has one kind of number: double-precision floating point. For simplicity, it only supports integers and decimal numbers.

1234;
12.34;

Strings

Like most languages, strings are enclosed in double quotes:

"I am a string";
"";    // The empty string.
"123"; // This is a string, not a number

Nil

The representation of "no value", NULL, like many other languages have.

Expressions

Arithmetic

Lox supports basic arithmetic operations as they common in all languages;

add + me;
subtract - me;
multiply * me;
divide / me;
-negateMe;

All operations are applicable on numeric values, except addition. You can concatanate two strings with addition operation.

Comparison & Equality

You can compare different types of values, not restricted to numbers.

less < than;
lessThan <= orEqual;
greater > than;
greaterThan >= orEqual;
equal == than;
not != equal;

You can compare strings;

"cat" != "dog"; // true.

Or even a string with a number;

123 == "123"; // false.

Logical Operators

!true;  // false.
!false; // true.
true and false; // false.
true and true;  // true.
false or false; // false.
true or false;  // true.

Precedence & Grouping

You can group stuff to ensure correct precedence that you want to see:

var average = (min + max) / 2;

Statements

You have seen some statements already:

print "Hello World!";

Lox also have a special type of statement called expression statement. It is basically an expression followed by a semicolon.

"Hello World!";

You can group statements in a block, but this will effect the scope of variables which will be discussed later.

{
    print "Hello World! 1";
    print "Hello World! 2";
}

Variables

You declare variables using var statements. If you omit the initializer, the variable’s value defaults to nil:

var imAVariable = "here is my value";
var iAmNil;

You can use variables in function calls and statements after defining:

var breakfast = "bagels";
print breakfast; // "bagels".
breakfast = "beignets";
print breakfast; // "beignets"

Control Flow

An if statement executes one of two statements based on some condition:

if (condition) {
  print "yes";
} else {
  print "no";
}

A while loop executes the body repeatedly as long as the condition expression evaluates to true:

var a = 1;
while (a < 10) {
  print a;
  a = a + 1;
}

Finally, we have for loops:

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

Functions

A function call expression looks the same as it does in C:

makeBreakfast(bacon, eggs, toast);

You can also call a function without passing anything to it:

makeBreakfast();

In Lox, you declare your own functions with fun:

fun printSum(a, b) {
  print a + b;
}

The body of a function is always a block. Inside it, you can return a value using a return statement:

fun returnSum(a, b) {
  return a + b;
}

If execution reaches the end of the block without hitting a return, it implicitly returns nil.

Closures

Functions are first class in Lox, which just means they are real values that you can get a reference to, store in variables, pass around, etc. This works:

fun addPair(a, b) {
  return a + b;
}

fun identity(a) {
  return a;
}

print identity(addPair)(1, 2); // Prints "3".

Since function declarations are statements, you can declare local functions inside another function:

fun outerFunction() {
  fun localFunction() {
    print "I'm local!";
  }

  localFunction();
}

If you combine local functions, first-class functions, and block scope, you run into this interesting situation:

fun returnFunction() {
  var outside = "outside";

  fun inner() {
    print outside;
  }

  return inner;
}

var fn = returnFunction();
fn();

Classes

class Breakfast {
  cook() {
    print "Eggs a-fryin'!";
  }

  serve(who) {
    print "Enjoy your breakfast, " + who + ".";
  }
}

// Store it in variables.
var someVariable = Breakfast;

// Pass it to functions.
someFunction(Breakfast);

var breakfast = Breakfast();
print breakfast; // "Breakfast instance".

Instantiation & Initialization

breakfast.meat = "sausage";
breakfast.bread = "sourdough";

Assigning to a field creates it if it doesn’t already exist.

If you want to access a field or method on the current object from within a method, you use good old this:

class Breakfast {
  serve(who) {
    print "Enjoy your " + this.meat + " and " +
        this.bread + ", " + who + ".";
  }

  // ...
}

Part of encapsulating data within an object is ensuring the object is in a valid state when it’s created. To do that, you can define an initializer. If your class has a method named init(), it is called automatically when the object is constructed. Any parameters passed to the class are forwarded to its initializer:

class Breakfast {
  init(meat, bread) {
    this.meat = meat;
    this.bread = bread;
  }

  // ...
}

var baconAndToast = Breakfast("bacon", "toast");
baconAndToast.serve("Dear Reader");
// "Enjoy your bacon and toast, Dear Reader."

Inheritance

class Brunch < Breakfast {
  drink() {
    print "How about a Bloody Mary?";
  }
}

Here, Brunch is the derived class or subclass, and Breakfast is the base class or superclass. Every method defined in the superclass is also available to its subclasses:

var benedict = Brunch("ham", "English muffin");
benedict.serve("Noble Reader");

As in Java, you can use super:

class Brunch < Breakfast {
  init(meat, bread, drink) {
    super.init(meat, bread);
    this.drink = drink;
  }
}

Lox Grammar

Syntax Grammar

Program

program        → declaration* EOF ;

Declaration

declaration    → classDecl
               | funDecl
               | varDecl
               | statement ;

classDecl      → "class" IDENTIFIER ( "<" IDENTIFIER )?
                 "{" function* "}" ;
funDecl        → "fun" function ;
varDecl        → "var" IDENTIFIER ( "=" expression )? ";" ;

Statement

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

exprStmt       → expression ";" ;
forStmt        → "for" "(" ( varDecl | exprStmt | ";" )
                           expression? ";"
                           expression? ")" statement ;
ifStmt         → "if" "(" expression ")" statement ( "else" statement )? ;
printStmt      → "print" expression ";" ;
returnStmt     → "return" expression? ";" ;
whileStmt      → "while" "(" expression ")" statement ;
block          → "{" declaration* "}" ;

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     → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;
addition       → multiplication ( ( "-" | "+" ) multiplication )* ;
multiplication → unary ( ( "/" | "*" ) unary )* ;

unary          → ( "!" | "-" ) unary | call ;
call           → primary ( "(" arguments? ")" | "." IDENTIFIER )* ;
primary        → "true" | "false" | "nil" | "this"
               | NUMBER | STRING | IDENTIFIER | "(" expression ")"
               | "super" "." IDENTIFIER ;

Utility Rules

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

Lexical Grammar

NUMBER         → DIGIT+ ( "." DIGIT+ )? ;
STRING         → "\"" <any char except "\"">* "\"" ;
IDENTIFIER     → ALPHA ( ALPHA | DIGIT )* ;
ALPHA          → "a" ... "z" | "A" ... "Z" | "_" ;
DIGIT          → "0" ... "9" ;

About

A C++ implementation of Lox language

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published