Arkar

Beerlang

May 17, 2023Arkar

My project on writing interpreter with Typescript every weekends.

On this page

What is Beerlang?

Six months ago, I challenged myself to explore a new area of programming every weekend, starting on Fridays. I pushed myself to build things that I had never tried before.

Here we have Beerlang, originally named Gideon, which is an interpreter written entirely in TypeScript. I named it Beerlang , guess what because I built it with a bottle of IPA every weekend, specifically on Friday nights.

Building beerlang challenged me to learn about compiler design patterns. Designing an interpreter gets back me to required studying concepts like abstract syntax trees, symbol tables, and intermediate representations,lexical analysis, syntax analysis, semantic analysis, and code generation which I completely ignored at my University times. It was a fascinating journey exploring algorithms and techniques in compiler design.

Notice: Beerlang now have interactive playground which is in alpha and work in progress. However feel free to check out here .

Features

Beerlang is a dynamically-typed, interpreted programming language designed for simplicity and enjoyment. It include features that all programming language might need such as

  • Variables: Beerlang supports declaring and using variables to store and manipulate data.
  • Operators: Beerlang includes various operators, such as arithmetic, comparison, and logical operators, to perform operations on data.
  • Loop: Beerlang provides loop constructs, such as for and while loops, to iterate over a block of code multiple times.
  • Functions: Beerlang allows defining and calling functions to encapsulate reusable code.
  • Closures: Beerlang supports closures, enabling functions to maintain references to variables from the parent scope even after the parent function has finished executing.
  • Return Types: Beerlang supports specifying return types for functions, allowing for explicit definition of the type of value returned by a function.
  • Classes : Beerlang also includes support for object-oriented programming with classes. You can define classes, instantiate objects from those classes, and access their properties and methods.

AST for a binary tree in beerlang

================      AST       ================
                                                 
(class Node
  (fun init (value )
    (expression (set left nil))
    (expression (set value value))
    (expression (set right nil))))
(class Tree
  (fun init ()
    (expression (set root nil)))
  (fun add (val )
    (var newNode (call Node val))
    (if (== (get root this) nil)
      (block 
        (expression (set root newNode))
        (return newNode)))
    (var tmp (get root this))
    (while (!= tmp nil)
      (block 
        (if (< val (get value tmp))
          (block 
            (if (== (get left tmp) nil)
              (block 
                (expression (set left newNode))
                (return newNode)))
            (expression (tmp (get left tmp))))
          (block 
            (if (== (get right tmp) nil)
              (block 
                (expression (set right newNode))
                (return newNode)))
            (expression (tmp (get right tmp)))))))))
(fun traverse (node )
  (if (== node nil)
    (return))
  (print (get value node))
  (expression (call traverse (get left node)))
  (expression (call traverse (get right node))))
(var t (call Tree))
(expression (call (get add t) 3))
(expression (call (get add t) 1))
(expression (call (get add t) 0))
(expression (call (get add t) 2))
(expression (call (get add t) 6))
(expression (call (get add t) 9))
(expression (call traverse (get root t)))
                                                 
================      Compiled Output       ================
                                                 
3
1
0
2
6
9

Introduction

Beerlang is a dynamically-typed, interpreted programming language designed for simplicity and fun.

Installation

Beerlang is developed using bun.js. Make sure you have installed bun.js on your local machine.

To install Beerlang globally via npm, you can use the following command:

npm i -g @arkardev/beerlang

This will install Beerlang as a global package, allowing you to run it from any directory in your system.

After installing Beerlang globally, you can execute Beerlang scripts using the beer command followed by the path to your Beerlang script file, like this:

beer myscript.beer

Replace myscript.beer with the path to your Beerlang script file.

By installing Beerlang globally, you can access it as a command-line tool and run Beerlang scripts conveniently.

Planned Features

  • Emoji Support: Beerlang is designed to support emojis as part of its syntax, adding a touch of fun and expressiveness to the language. You will be able to use emojis as variable names, function names, and even as operators.
  • Funny Syntax: Beerlang embraces a playful and unconventional syntax that encourages creativity and experimentation. It includes elements such as using "🍺" instead of parentheses for function calls and "🍻" instead of curly braces for code blocks.

Stay tuned for these exciting additions to Beerlang!

Features

  • Easy to Learn: Beerlang has a simple syntax and minimalistic design, making it easy for beginners to grasp.
  • Dynamic Typing: Variables in Beerlang are dynamically typed, allowing for flexible programming.
  • Extensible: Beerlang supports the creation of user-defined functions and encourages code modularity.

Syntax

Data types

Beerlang supports the following data types:

  • Number: Numbers in Beerlang are represented by numeric literals. For example, var a = 12; and var b = 26; assign the values 12 and 26 to variables a and b, respectively.
  • String: Strings are sequences of characters enclosed in double quotes ("). For example, var breakfast = "break"; assigns the string "break" to the variable breakfast. You can concatenate strings using the + operator, as shown in the example print "Today's breakfast is " + breakfast + ".";.
  • Boolean: Beerlang has two boolean values: true and false. For example, var t = true; assigns the value true to the variable t, while var f = false; assigns false to the variable f.
  • Nil: In Beerlang, nil represents the absence of a value. For example, var notExists = nil; assigns nil to the variable notExists. When you print notExists, it will display nil.
var a = 12;
var b = 26;
print a + b;

var t = true;
var f = false;
var notExists = nil;

print notExists;

var breakfast = "break";
print "Today's breakfast is " + breakfast + ".";

Operators

Beerlang supports the following arithmetic operators:

  • Addition (+): The addition operator is used to add two numbers together. For example, var sum = 2 + 3; assigns the value 5 to the variable sum.
  • Subtraction (-): The subtraction operator is used to subtract one number from another. For example, var difference = 7 - 4; assigns the value 3 to the variable difference.
  • Multiplication (*): The multiplication operator is used to multiply two numbers. For example, var product = 5 * 6; assigns the value 30 to the variable product.
  • Division (/): The division operator is used to divide one number by another. For example, var quotient = 10 / 2; assigns the value 5 to the variable quotient.

Here's an example that demonstrates the usage of these operators:

var a = 2 + 3; // Addition
var b = 7 - 4; // Subtraction
var c = 5 * 6; // Multiplication
var d = 10 / 2; // Division

print a; // Output: 5
print b; // Output: 3
print c; // Output: 30
print d; // Output: 5

These operators can be used with variables, numeric literals, and expressions to perform arithmetic operations in Beerlang.

Control flows

Beerlang supports the following control flow statements:

if statement

The if statement allows you to conditionally execute a block of code based on a Boolean expression.

Here's an example that demonstrates the usage of the if statement:

var a = -10;
var condition = a > 3;
if (condition) {
  print " I am true...";
} else {
  print " I am false...";
}

while loop

The while loop allows you to repeatedly execute a block of code as long as a condition is true.

Here's an example that demonstrates the usage of the while loop:

var i = 1;
while (i < 8) {
  print i;
  i = i + 1;
}

for loop

The for loop allows you to iterate over a range of values or elements in Beerlang.

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

These control flow statements allow you to make decisions, repeat actions, and iterate over values in Beerlang, adding flexibility and control to your programs.

Functions

In Beerlang, functions are first-class citizens, which means they can be assigned to variables, passed as arguments to other functions, and returned as values from other functions. You can define functions using the fun keyword, followed by the function name and a pair of parentheses for the parameter list. The function body is enclosed in curly braces {}.

Here's an example of a function that adds two numbers:

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

var result = add(3, 4);
print(result); // Output: 7

fun fib(n) {
  if (n <= 1) return n;
  return fib(n - 2) + fib(n - 1);
}
for (var i = 0; i < 20; i = i + 1) {
  print fib(i);
}

Closures

Beerlang supports closures, which are functions that have access to variables from their outer (enclosing) scope. This allows functions to "remember" the values of variables even after they have finished executing. Closures can be useful for creating functions with private variables or for implementing callback functions.

Here's an example of a closure in Beerlang:

fun counter() {
  var count = 0;

  fun increment() {
    count = count + 1;
    print("Count: " + count);
  }

  return increment;
}

var counterFunc = counter();
counterFunc(); // Output: Count: 1
counterFunc(); // Output: Count: 2

In the example above, the counter function returns the increment function, which has access to the count variable in its outer scope. Each time the increment function is called, it increments the count variable and prints the updated value.

Closures in Beerlang provide a powerful mechanism for creating functions with encapsulated state and behavior.

Classes

Beerlang supports object-oriented programming through the use of classes. A class is a blueprint for creating objects that share similar properties and behaviors. In Beerlang, you can define classes using the class keyword, followed by the class name and a pair of curly braces {} to enclose the class body.

In Beerlang, the init method is used as a constructor for a class. It is called when a new instance of the class is created. The init method initializes the properties of the class and performs any necessary setup.

Here's an example of a class definition in Beerlang:

class Breakfast {
  init() {
    this.name = "cake";
    this.number = 2;
    print "Cakes ready!";
  }

  eat() {
    if (this.number > 0) {
      print "Eating " + this.name + "...";
      this.number = this.number - 1;
    } else {
      print "Nothing to eat.";
    }
  }
}
var breakfast = Breakfast();
var quickEat = breakfast.eat;
quickEat();
breakfast.eat();
quickEat();

Beerlang's class-based approach allows you to organize your code into reusable and modular components, making it easier to manage and maintain your programs.

Inheritance

In Beerlang, you can create classes that inherit properties and behaviors from other classes. This is known as inheritance and allows you to create a hierarchy of classes with shared characteristics.

To define a class that inherits from another class, you can use the < keyword followed by the name of the parent class. The child class can then access and override the properties and methods of the parent class.

Here's an example that demonstrates inheritance in Beerlang:

class Doughnut {
  cook() {
    print "Fry until golden brown.";
  }
}

class BostonCream < Doughnut {
  cook() {
    super.cook();
    print "Pipe full of custard and coat with chocolate.";
  }
}

BostonCream().cook();

Inheritance allows you to reuse code and create a more organized and modular class structure in Beerlang.

Installation

To use Beerlang, ensure that you have Bun.js and npm installed on your system. Then, follow these steps:

  1. Clone the Beerlang repository: git clone https://github.com/Riley1101/Beerlang>
  2. Navigate to the repository: cd beerlang
  3. Install dependencies: bun install
  4. Build the project: bun run build
  5. Run Beerlang: bun start

Running Beerlang Script

To execute a Beerlang script, use the following command:

beer myscript.beer

Replace myscript.beer with the path to your Beerlang script file.

Examples

Checkout examples in /examples

Contributing

Contributions to Beerlang are welcome! If you find any bugs, have feature requests, or want to contribute enhancements, please open an issue or submit a pull request on the

Roadmap and next step

The next step in the development of Beerlang is to add more fun to it. This will involve introducing some funny syntax to make the language even more playful and unconventional. Stay tuned for these exciting additions to Beerlang!

I am rewriting Beerlang in C to make it faster and more powerful. This rewrite will allow Beerlang to run faster and be more optimized. By using C, I aim to make Beerlang more versatile and efficient. Stay tuned for updates on this exciting development!

Resources