Programming language wake

since: 2010-07-18 update: 2010-07-18 count:

For Japanese

Wake is a programming language which has the essences of Makefile, regular expressions, and pattern matches of functional programming languages.

This language intended to be an esolang. As this language doesn't have native support for integer arithmetics, this language is not practical.

This package contains two interpreters (C++ and Ruby) and some sample code (in test/ directory).

This document aims to introduce the features of wake with some examples.

Hello, world!

all: "Hello, world!\n"

Like Makefiles, a wake program consists of a number of rules. Each rule has one target and some actions. In the above example, all is the target of this rule and "Hello, world!\n" is the action. If an action is a string literal, the string will be outputted.

The another type of action has no quotes. When the interpreter finds this type of action, the interpreter searches a corresponding target and runs its actions. The following code also outputs "Hello, world!\n".

all: "Hello, " world
world: "world!\n"

The world in the first rule invokes the world in the second rule.

Regular expressions

A target is actually a regular expression. For example:

all: foo bar baz
.*: "yay!\n"

As the target of the second rule (.*) matches all strings, the second rule will be applied three times (foo, bar, and baz). Hence this program outputs "yay!\nyay!\nyay!\n".

You can use special variables $&, $+, $1, $2, $3, ..., and $9 in actions. The meanings of them are as same as these variables in Perl or Ruby - $& is the matched string, $+ is the last back reference, and $N is the N-th back reference.

For example, the following code reverses and outputs "foobar" (so the actual output is "raboof").

all: foobar "\n"
(.*)(.): "$2" $1
:

The target in the second line matches all strings except for an empty string. The first action in the second line outputs the last character and the second action applies the rest of string until the string becomes empty. The third line matches an empty string and does nothing.

Pattern matching

If there are multiple rules which can be applied, the first rule will be applied. For example, you can write a program which converts "o" into "O"

all: foobar "\n"
o(.*): "O" $1
(.)(.*): "$1" $2
:

This program outputs fOObar.

The above program is trivial as it always outputs the fixed string. You can use $< to read all characters from the standard input. Let's make the program a bit more interesting by using this feature:

all: $< "\n"
o(.*): "O" $1
(.)(.*): "$1" $2
:

This program reads all characters from the standard input and outputs the characters, converting "o" to "O".

Note that you can use $< multiple times. The following example code outputs the input twice:

all: "$<" "$<"

eval

If there is an expression $(...) in actions, wake evaluates ... (i.e., finds a rule whose target matches ... and invokes its actions) and the $(...) will be replaced by the result.

As I couldn't come up with a short example for this feature, the example of this feature is lengthy:

fizzbuzz: fb@1,1

inc1@(\d);.*?\1(\d)\d*: "$2"

inc@: "1"
inc@(\d*)9: inc@$1 "0"
inc@(\d*)(\d): "$1" inc1@$2;0123456789

inc_mod3@(\d): inc1@$1;0120

output@\d*[50],0: "FizzBuzz"
output@\d*[50],\d: "Buzz"
output@\d+,0: "Fizz"
output@(\d+),\d: "$1"

# Done
fb@101,\d:

fb@(\d+),(\d): output@$1,$2 "\n" fb@$(inc@$1),$(inc_mod3@$2)

This is a FizzBuzz program in wake. Let's look into fb@ in the last line. The $(...) expression is used to construct two "parameters" for fb@. This rule is meant to be a "function" like

void fb(int count, int count_mod3) {
  output(count, count_mod3);
  puts("\n");
  fb(inc(count), inc_mod3(count_mod3));
}

#include

If a line starts from #include, the interpreter reads rules from the specified file.

This is an example program which uses the standard library std/num.wake:

test: test_add test_sub test_mul test_div test_mod test_divmod
test_add: add(42,9) "\n"  # 51
test_sub: sub(42,9) "\n"  # 33
test_mul: mul(42,9) "\n"  # 378
test_div: div(42,9) "\n"  # 4
test_mod: mod(42,9) "\n"  # 6
test_divmod: divmod(42,9) "\n"  # 4,6

#include "std/num.wake"

Note that you should not #include the library in the first line. If you put the #include in the first line, the execution will start from the first rule of std/num.wake, not the first rule of the your program (i.e., test in the above example).

As of July 18, 2010, std/num.wake is the only standard library and the above example program showed all functions in this library.

The library provides 6 "functions" - add, sub, mul, div, mod, and divmod. They are expected to be used inside $() to invoke another rule with a calculated value. Here is another example code which uses std/num.wake. This program counts from 0 to 9 and outputs them.

all: count0
count10:
count(\d): "$1\n" count$(add($1,1))
#include "std/num.wake"

Some details

comments and spacing

Empty lines and whitespace-only lines add no rule. You can put any numbers of whitespaces before and after actions and they have no meanings. Note that whitespaces before the first colon (the separator for a target and actions) have effects. These whitespaces are part of the target.

escape sequence

In a string literal and an action, backslashes can be used as a escape character. "\n" and "\r" mean a newline and a carriage return, respectively. Other sequences start from backslashes just mean the second character. Note that you can output $1 by "\$1", " by "\"", and \ by "\\". Also, you can invoke an action which contains whitespaces with a backslash. For example, the following code outputs "OK".

test: use\ backslash
use backslash: "OK\n"

To use a colon in a target, you can use \:. Here is an example code:

all: :
\:: "OK\n"

implementations

The two reference implementations in the package should have the same semantics except for the detail of regular expressions. If you find a program which yields different results between the two implementations, this may be a bug.

non trivial code examples

Though wake is a small language, we can write non trivial code thanks for the power of regular expressions. In fact, std/num.wake , which is the only standard library in wake, implements integer arithmetics without special supports from the language.

The brainfuck interpreter would be another example you may be interested in.

My golf server also has a bunch of wake programs thanks to golf players.

TODO

ChangeLog


home / index

shinichiro.hamaji _at_ gmail.com / shinichiro.h