All Projects → lucifer1004 → MonkeyLang.jl

lucifer1004 / MonkeyLang.jl

Licence: MIT license
"Writing an Interpreter in GO" and "Writing a Compiler in GO" in Julia.

Programming Languages

julia
2034 projects
Makefile
30231 projects

Projects that are alternatives of or similar to MonkeyLang.jl

Swiftpascalinterpreter
Simple Swift interpreter for the Pascal language inspired by the Let’s Build A Simple Interpreter article series.
Stars: ✭ 270 (+800%)
Mutual labels:  interpreter, lexer
malluscript
A simple,gentle,humble scripting language for mallus, based on malayalam memes.
Stars: ✭ 112 (+273.33%)
Mutual labels:  interpreter, lexer
Mico
Mico ("Monkey" in catalan). Monkey language implementation done with C++. https://interpreterbook.com/
Stars: ✭ 19 (-36.67%)
Mutual labels:  interpreter, lexer
Rs Monkey Lang
Monkey Programming Language written in Rust.
Stars: ✭ 80 (+166.67%)
Mutual labels:  interpreter, lexer
types-and-programming-languages
C++ Implementations of programming languages and type systems studied in "Types and Programming Languages" by Benjamin C. Pierce..
Stars: ✭ 32 (+6.67%)
Mutual labels:  interpreter, lexer
pascal-interpreter
A simple interpreter for a large subset of Pascal language written for educational purposes
Stars: ✭ 21 (-30%)
Mutual labels:  interpreter, lexer
Lioness
The Lioness Programming Language
Stars: ✭ 155 (+416.67%)
Mutual labels:  interpreter, lexer
aria
Expressive, noiseless, interpreted, toy programming language
Stars: ✭ 40 (+33.33%)
Mutual labels:  interpreter, lexer
monkey-interpreter
Monkey programming language interpreter designed in "Writing An Interpreter In Go".
Stars: ✭ 26 (-13.33%)
Mutual labels:  interpreter, monkey
Cub
The Cub Programming Language
Stars: ✭ 198 (+560%)
Mutual labels:  interpreter, lexer
Monkey Rust
An interpreter for the Monkey programming language written in Rust
Stars: ✭ 174 (+480%)
Mutual labels:  interpreter, lexer
Own-Programming-Language-Tutorial
Репозиторий курса "Как создать свой язык программирования"
Stars: ✭ 95 (+216.67%)
Mutual labels:  interpreter, lexer
monkey
The Monkey Programming Language & Interpreter written in PHP.
Stars: ✭ 21 (-30%)
Mutual labels:  interpreter, lexer
fayrant-lang
Simple, interpreted, dynamically-typed programming language
Stars: ✭ 30 (+0%)
Mutual labels:  interpreter, lexer
brs
An interpreter for the BrightScript language that runs on non-Roku platforms.
Stars: ✭ 96 (+220%)
Mutual labels:  interpreter
fast-formula-parser
Parse and evaluate MS Excel formula in javascript.
Stars: ✭ 341 (+1036.67%)
Mutual labels:  interpreter
j2
j2 is a minimalist concatenative programming language that makes up for its simplicity by its ability to natively bind with C libraries' ABI *and types*, *without glue*
Stars: ✭ 37 (+23.33%)
Mutual labels:  interpreter
tau
Tau is an open source interpreted programming language designed to be minimal, fast and efficient.
Stars: ✭ 26 (-13.33%)
Mutual labels:  interpreter
BabyBrowser
A Small Web Browser Built in Python
Stars: ✭ 21 (-30%)
Mutual labels:  interpreter
jingle
🔔 Jingle is a dynamically-typed, multi-paradigm programming language designed for humans and machines.
Stars: ✭ 34 (+13.33%)
Mutual labels:  interpreter

MonkeyLang

version Build Status Coverage

Monkey Programming Language written in Julia.

Table of Contents

Using Monkey in Julia

You can start the REPL within Julia:

using MonkeyLang

start_repl()

Or you can evaluate Monkey programs using string macros:

using MonkeyLang

a = 2

monkey_eval"let b = $a; puts(b)"

monkey_vm"let c = [$a, $a]; puts(c)"

monkey_julia"let d = {$a: $a}; puts(d)"

Compile MonkeyLang.jl to a standalone executable

Clone the repo, and run make build in the root directory.

Caution: The compilation may take up to ~5 minutes.

Start the REPL

You can start the REPL in a Julia script or in the Julia REPL:

import Pkg; Pkg.add("MonkeyLang")

using MonkeyLang

MonkeyLang.start_repl()
MonkeyLang.start_repl(; use_vm = true) # use VM

You can press Ctrl-C or Ctrl-D to exit the REPL.

If you have compiled MonkeyLang.jl locally, then you can directly start the REPL by:

./monkey repl
./monkey repl --vm # use VM

Documentation

I created the document with reference to Writing An Interpreter In Go and rs-monkey-lang.

⚠️ Please note that there may be some mistakes.

Summary

  • C-like syntax
  • variable bindings
  • first-class and higher-order functions • closures
  • arithmetic expressions
  • built-in functions

Syntax overview

An example of Fibonacci function.

let fibonacci = fn(x) {
  if (x == 0) {
    0;
  } else {
    if (x == 1) {
      1;
    } else {
      fibonacci(x - 1) + fibonacci(x - 2);
    }
  }
};

fibonacci(10);

If

It supports the general if. else exists, but else if does not exist.

if (true) {
  10;
} else {
  5;
}

While

It also supports while loops.

let x = 5;
while (x > 0) {
  puts(x);
  x = x - 1;
}

Operators

It supports the general operations.

1 + 2 + (3 * 4) - (10 / 5);
!true;
!false;
+10;
-5;
"Hello" + " " + "World";

Return

It returns the value immediately. No further processing will be executed.

if (true) {
  return;
}
let identity = fn(x) {
  return x;
};

identity("Monkey");

Variable bindings

Variable bindings, such as those supported by many programming languages, are implemented. Variables can be defined using the let keyword. Variables cannot be redefined in the same scope, but they can be reassigned.

Format:

let <identifier> = <expression>; # Define

<identifier> = <expression>; # Reassign

Example:

let x = 0;
let y = 10;
let foobar = add(5, 5);
let alias = foobar;
let identity = fn(x) { x };

x = x + 1;
y = x - y;

Scopes

In Monkey, there are types of scopes:

Global Scope

Variables defined at the top level are visible everywhere, and can be modified from anywhere.

let x = 2; # `x` is a global variable

let f = fn() { 
  let g = fn() { 
    x = x + 1; # Modifies the global variable `x`
    return x; 
  } 
  return g; 
}

let g = f();
puts(g()); # 3
puts(g()); # 4

let h = f();
puts(h()); # 5
puts(h()); # 6

Local Scope

Variables defined within while loops or functions are of this scope. They can be modified from the same scope, or inner while loops' scopes.

let x = 1;

while (x > 0) {
  x = x - 1;
  let y = 1; # `y` is a local variable
  while (y > 0) {
    y = y - 1; # Modifies the local variable `y`
  }
  puts(y); # 0
}

Closure Scope

A function captures all non-global variables visible to it as its free variables. These variables can be modified from within the function.

let f = fn() { 
  let x = 2; 
  let g = fn() { 
    x = x + 1; # `x` is captured as a free variable
    return x; 
  } 
  return g; 
}

let g = f();
puts(g()); # 3
puts(g()); # 4

let h = f();
puts(h()); # 3, since in function `f`, `x` remains unchanged.
puts(h()); # 4

CurrentClosure Scope

Specially, a named function being defined is of this scope. It cannot be modified from within its body.

let f = fn(x) {
  f = 3; # ERROR: cannot reassign the current function being defined: f
}

But redefinition is OK:

let f = fn(x) {
  let f = x + x;
  puts(f);
}

f(3); # 6

Literals

Five types of literals are implemented.

INTEGER

INTEGER represents an integer value. Floating point numbers can not be handled.

Format:

[-+]?[1-9][0-9]*;

Example:

10;
1234;

BOOLEAN

BOOLEAN represents a boolean value.

Format:

true | false;

Example:

true;
false;

let truthy = !false;
let falsy = !true;

NULL

NULL represents null. When used as a condition, NULL is evaluated as falsy.

Format:

null;

Example:

if (null) { 2 } else { 3 }; # 3

STRING

STRING represents a string. Only double quotes can be used.

STRINGs can be concatenated with "+".

Format:

"<value>";

Example:

"Monkey Programming Language"; # "Monkey Programming Language";
"Hello" + " " + "World"; # "Hello World"

ARRAY

ARRAY represents an ordered contiguous element. Each element can contain different data types.

Format:

[<expression>, <expression>, ...];

Example:

[1, 2, 3 + 3, fn(x) { x }, add(2, 2), true];
let arr = [1, true, fn(x) { x }];

arr[0];
arr[1];
arr[2](10);
arr[1 + 1](10);

HASH

HASH expresses data associating keys with values.

Format:

{ <expression>: <expression>, <expression>: <expression>, ... };

Example:

let hash = {
  "name": "Jimmy",
  "age": 72,
  true: "a boolean",
  99: "an integer"
};

hash["name"];
hash["a" + "ge"];
hash[true];
hash[99];
hash[100 - 1];

FUNCTION

FUNCTION supports functions like those supported by other programming languages.

Format:

fn (<parameter one>, <parameter two>, ...) { <block statement> };

Example:

let add = fn(x, y) {
  return x + y;
};

add(10, 20);
let add = fn(x, y) {
  x + y;
};

add(10, 20);

If return does not exist, it returns the result of the last evaluated expression.

let addThree = fn(x) { x + 3 };
let callTwoTimes = fn(x, f) { f(f(x)) };

callTwoTimes(3, addThree);

Passing around functions, higher-order functions and closures will also work.

The evaluation order of function parameters is left to right.

So a memoized Fibonacci function should be implemented like:

let d = {}

let fibonacci = fn(x) {
    if (x == 0) {
        0
    } else {
        if (x == 1) {
            1;
        } else {
            if (type(d[x]) == "NULL") {
                # You cannot use `d = push(d, x, fibonacci(x - 1) + fibonacci(x - 2))`
                # since `d` is evaluated first, which means it will not be updated
                # when `fibonacci(x - 1)` and `fibonacci(x - 2)` are called.
                let g = fibonacci(x - 1) + fibonacci(x - 2);
                d = push(d, x, g);
            }

            d[x];
        }
    }
};

fibonacci(35);

Built-in Functions

You can use the following built-in functions 🚀

type(<arg1>): STRING

Return the type of arg1 as a STRING.

type(1); # INTEGER
type("123"); # STRING
type(false); # BOOLEAN

puts(<arg1>, <arg2>, ...): NULL

It outputs the specified value to stdout. In the case of Playground, it is output to console.

puts("Hello");
puts("World!");

len(<arg>): INTEGER

  • For STRING, it returns the number of characters.
  • For ARRAY, it returns the number of elements.
  • For HASH, it returns the number of key-value pairs.
len("Monkey"); # 6
len([0, 1, 2]); # 3
len({1: 2, 2: 3}); # 2

first(<arg: STRING>): STRING | NULL

Returns the character at the beginning of a STRING. If the STRING is empty, return NULL instead.

first("123"); # "1"
first(""); # null

first(<arg: Array>): any

Returns the element at the beginning of an ARRAY. If the ARRAY is empty, return NULL instead.

first([0, 1, 2]); # 0
first([]); # null

last(<arg: String>): STRING | NULL

Returns the element at the last of a STRING. If the STRING is empty, return NULL instead.

last("123"); # "3"
last(""); # null

last(<arg: Array>): any

Returns the element at the last of an ARRAY. If the ARRAY is empty, return NULL instead.

last([0, 1, 2]); # 2
last([]) # null

rest(<arg: STRING>): STRING | NULL

Returns a new STRING with the first element removed. If the STRING is empty, return Null instead.

rest("123"); # "23"
rest(""); # null

rest(<arg: ARRAY>): ARRAY | NULL

Returns a new ARRAY with the first element removed. If the ARRAY is empty, return NULL instead.

rest([0, 1, 2]); # [1, 2]
rest([]); # null

push(<arg1: ARRAY>, <arg2>): ARRAY

Returns a new ARRAY with the element specified at the end added.

push([0, 1], 2); # [0, 1, 2]

push(<arg1: HASH>, <arg2>, <arg3>): HASH

Returns a new HASH with arg2: arg3 added. If arg2 already exists, the value will be updated with arg3.

push({0: 1}, 1, 2); # {1:2, 0:1}
push({0: 1}, 0, 3); # {0:3}

Advanced examples

A custom map function

let map = fn(arr, f) {
  let iter = fn(arr, accumulated) { 
    if (len(arr) == 0) {  
      accumulated 
    } else { 
      iter(rest(arr), push(accumulated, f(first(arr)))); 
    } 
  };

  iter(arr, []);
};

let a = [1, 2, 3, 4];
let double = fn(x) { x * 2};
map(a, double); # [2, 4, 6, 8]

A custom reduce function

let reduce = fn(arr, initial, f) {
  let iter = fn(arr, result) {
    if (len(arr) == 0) {
      result
    } else { 
      iter(rest(arr), f(result, first(arr)))
    }
  }

  iter(arr, initial)
}

let sum = fn(arr) { 
  reduce(arr, 0, fn(initial, el) { initial + el })
}

sum([1, 2, 3, 4, 5]); # 15

Macro System

Now that the Lost Chapter has been implemented, MonkeyLang.jl provides a powerful macro system.

Here is an example:

let unless = macro(condition, consequence, alternative) {
    quote(if (!(unquote(condition))) {
        unquote(consequence);
    } else {
        unquote(alternative);
    });
};

unless(10 > 5, puts("not greater"), puts("greater")); # greater

In the REPL, you need to enter all the contents in a single line without \n characters.


Enjoy Monkey 🐵 !


Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].