A simple, dynamic language interpreter written in Rust.
// recursively computes factorial of n
fact = fn(n) {
if (n < 2) {
return 1;
}
return n * fact(n - 1);
};
// Take a number from stdin, and compute the factorial
print(fact(input_num("Factorial: ")));
Puffin
Supports a mix of imperative and functional language features:
Puffin
functions are closures which capture their environment when evaluated, if bound to a name, functions may also be used recursively. Puffin
functions are declared with the keyword fn
, followed by any number of argument names in parens, and finally, the associated function body between curlies, or a single expression.
// Curried addition using closures
curry_add = fn(a) {
return fn(b) => a + b;
};
curry_10 = curry_add(10);
print(curry_10(7));
// Output: 17
You may also pass functions like other types freely.
call_twice = fn(f) {
f();
f();
};
call_twice(fn() => println("Hello World"));
// Output:
// Hello World
// Hello World
Puffin
Structures can be created and bound to names, arrays, or even other structure fields. Structure fields are dynamic and can be added ad-hoc.
// create a structure
user = {
name: "Rafi",
age: 22,
contact: {
github: "github.com/rafibayer",
linkedin: "linkedin.com/in/rafael-bayer"
}
};
// add a new field to the nested contact structure
user.contact.email = "rafibayer7@gmail.com";
// print the value of the 'contact' field
println(user.contact);
// output:
// {github: github.com/rafibayer, linkedin: linkedin.com/in/rafael-bayer, email: rafibayer7@gmail.com}
Structures may also define "receiver" functions (think Golang) that implicitly take themselves as the first argument. This is done by making self
the first argument to a function defined as a structure field. Here is an example structure that can be used to store odd and even numbers seperately. We use a lambda to act as a constructor so we could make many of these easily.
new_odd_even = fn() => {
evens: [0],
odds: [0],
add_num: fn(self, num) {
if (num % 2 == 0) {
push(self.evens, num);
} else {
push(self.odds, num);
}
}
};
odd_even = new_odd_even();
for (n in [0:10]) {
odd_even.add_num(n);
}
println(odd_even);
// output: {add_num: <(self) fn(num)>, odds: [1, 3, 5, 7, 9], evens: [0, 2, 4, 6, 8]}
puffin
allows arrays to have mixed types (this includes other arrays). See the Builtins section for more ways to manipulate arrays. Array indices have the initial value of null.
Sized Initialization:
// create a new array of size 5
arr = [5];
// fill arr with 1-5
for (i = 0; i < len(arr); i += 1) {
arr[i] = i + 1;
}
arr[len(arr)-1] = "Another type!";
println(arr);
// output: [1, 2, 3, 4, Another type!]
Range Initialization:
arr = [0:10];
print(arr);
// output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// standard for loop
for (i = 0; i < 10; i += 1) {
println(i);
}
// for-in loop
for (i in [0:10]) {
println(i);
}
// while-loop
i = 0;
while (i < 10) {
println(i);
i += 1;
}
Puffin
currently supports the following builtin functions and constants, although more may be added soon. Builtins cannot be rebound (although they can be used as structure field names):
PI
: approximatelyπ
true
: 1false
: 0EPSILON
: Ruststd::f64::EPSILON
str(a)
: Returns string representation ofa
len(a)
: Returns length of array, string, or structurea
print(...)
: prints elements of args delimited by spacesprintln(...)
: prints elements of args delimited by spaces, followed by a newlineerror(...)
: printlns args to sterr and exits with non-zero exit codesin(a)
,cos(a)
,tan(a)
,sqrt(a)
,abs(a)
,round(a)
: standard math functionspow(a, b)
: returns a^binput_str(...)
,input_num(...)
: prints args as prompt, parses next line from stdin as string or number.push(a, b)
: pushesb
onto the arraya
pop(a)
: pops the last elementb
, offa
, returningb
remove(a, i)
: removes and returns the element at indexi
in arraya
insert(a, i, v)
: inserts elementv
into arraya
at indexi
rand()
: returns a uniformly distributed random number between 0 and 1
Puffin
supports the following types. All types are pass by value with the exception of Array
and Structure
, which are pass by refrence.
Null
Num
String
Array
Structure
Closure
: Functions evaluate to closuresBuiltin
: Used internally only, behaves like a regular function when called
Puffin
Also supports other standard features such as standard arithmetic, comparison, and logical operators. There is no boolean type, all numbers are evaluated as true
unless they are 0
.
To execute a source file, just pass it to the puffin
cli.
Example: $ puffin program.puf
puffin
also supports the following optional cli flags:
-parse
: Show the program parse tree before execution-ast
: Show the program AST before execution
To start the REPl, just run puffin
with no arguments.
Example: $ puffin
- Array Resizing (automatic? via builtin?)
- Hash-table (and literals?)
- Runtime/AST error line numbers
- Imports/multi-file programs? standard library?
-----------------------------------------------------------
,▄▄▄▄,
▄██████████▄
,███ ▀██ ▀██████▄
███████████▓██████
██████████▌ ▀▀▀▀▀▀
████████████▄
▄████████████████▄
▄▄████████████████████
,▄████████████████████████`
██████████████████████████▀
┌██████████████████████████`
█████████████████████████▀
▄████████████████████████▀
,▄██████████████████████▀
▄█████████████████████▀
▀▀▀▀` ███ ▐██▀
▀▀▌ ▀▌
-----------------------------------------------------------
Puffin is the successor to my previous attempt at creating a language: smp-lang.