Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
OMetaJS: FizzBuzz Compiler
// our FizzBuzz language
var code =
"for every number from 1 to 100\
if the number is a multiple of 3 and it is a multiple of 5 then\
print \"FizzBuzz\"\
else if it is a multiple of 3 then\
print \"Fizz\"\
else if it is a multiple of 5 then\
print \"Buzz\"\
else\
print the number\
";
ometa FizzBuzz {
// number is overwritten to parse digit characters and return them as a string
number = spaces ('+' | '-' | empty):prefix digit+:ds -> (
parseInt(
(prefix.length > 0) ?
prefix + ds.join('') :
ds.join('')
)
),
// quotedString matches strings inside quotes
quotedString = spaces '"' (~'"' anything)*:string '"' -> (
string.length == 0 ?
"" :
string.join("")
),
// variables can be prefixed with `the`, we need to track it as `_it` in the state table so it can be referenced again
variableName =
("the" | empty) spaces
firstAndRest('letter', 'letterOrDigit'):x
!(self.set("_it", x.join("")))
-> (x.join("")),
// expressions are either an andExpression, multipleExpression, numberExpression or a quotedString
// all expressions are translated into functions
expression = andExpression
| multipleExpression
| numberExpression
| quotedString:qs -> (function () { return qs; }),
// and expressions are left recursive allowing nested and expressions, and they evaluate into a function returning a boolean
andExpression = andExpression:l "and" booleanExpression:r -> (
function () {
return !!l() && !!r();
}
)
| booleanExpression,
// a boolean expression is just a boolean function
booleanExpression = expression:e -> (function () {
var object = e();
if (typeof object == "boolean") {
return object;
} else if (typeof o == "number") {
return object != 0;
} else {
return (String(object).length > 0) && object !== null && object !== undefined;
}
}),
// number expressions are functions that return an integers
// this is where `_it` can be resolved from the previously assigned `the`
numberExpression = number:n -> (function () {
return parseInt(n);
})
| "it" -> (function () {
return parseInt(
self.get(
self.get("_it")
)
);
})
| variableName:vn -> (function () {
return parseInt(self.get(vn));
}),
// `is a multiple of` is a primitive infix operator
multipleExpression = numberExpression:left "is a multiple of" numberExpression:right -> (
function () {
return (left() % right()) == 0;
}
),
// statements represent top expressions
// we have `print`, `if then else` and `for every`
statement = "print" expression:e -> (function () { console.log(e()); })
| "if" andExpression:condition "then" statement:first ("else" statement | empty):second -> (
function () {
if (condition()) {
first();
} else if (String(second).length > 0 && second != null) {
second();
}
}
)
| "for every" variableName:vn "from" number:low "to" number:high statement:s -> (
function () {
for (var i = low; i <= high; i++) {
self.set(vn, i);
s();
}
}
),
// a block is zero or more statements
block = statement*:ss -> (
function () {
ss.forEach(function (statement) {
statement();
});
}
),
// end of file is any number of spaces then an end
EOF = spaces end,
// our program is just one block!
program = block:b EOF -> (b)
}
FizzBuzz.initialize = function() {
// our global state table
this.vars = {};
this.set = function(k, v){
this.vars[k] = v;
return this;
};
this.get = function(k) {
return this.vars[k];
};
};
// compiles our language into JavaScript with the top level program rule
var result = FizzBuzz.matchAll(
code,
'program'
);
// execute the code!
result();
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment