Last active
August 31, 2016 06:37
-
-
Save dtoma/c7fb551d771f262a0f8e0d38996b6975 to your computer and use it in GitHub Desktop.
FizzBuzz Lang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Interpreter for a language targeted at solving fizzbuzz | |
Source: [The fastest fizzbuzz in the west](https://www.promptworks.com/blog/the-fastest-fizzbuzz-in-the-west) | |
## Imports | |
""" | |
from rply import LexerGenerator, ParserGenerator | |
from rply.token import BaseBox | |
import sys | |
""" | |
## Lexer | |
Ignores whitespaces. | |
Comments begin with `#`. | |
""" | |
lg = LexerGenerator() | |
lg.add('ELLIPSIS', r'\.\.\.') | |
lg.add('NUMBER', r'\d+') | |
lg.add('EQUALS', r'=') | |
lg.add('WORD', r'[a-z]+') | |
lg.ignore(r'\s+') # whitespace | |
lg.ignore(r'#.*\n') # comments | |
lexer = lg.build() | |
""" | |
## Boxes | |
> In RPython, like other statically typed languages, a variable must have a specific type. | |
> We take advantage of polymorphism to keep values in a box so that everything is statically typed. | |
> You can write whatever boxes you need for your project. | |
[Source](https://github.com/alex/rply#why-do-we-have-the-boxes) | |
""" | |
class IntBox(BaseBox): | |
def __init__(self, value): | |
self.value = int(value.getstr()) | |
def int(self): | |
return self.value | |
class WordBox(BaseBox): | |
def __init__(self, value): | |
self.value = value.getstr() | |
def str(self): | |
return self.value | |
class RangeBox(BaseBox): | |
def __init__(self, low, high): | |
self.low = low | |
self.high = high | |
def range(self): | |
return range(self.low.int(), self.high.int() + 1) | |
""" | |
This one implements the fizzbuzz checks for a given integer. | |
""" | |
class AssignmentBox(BaseBox): | |
def __init__(self, word, number): | |
self.word = word | |
self.number = number | |
def eval_with(self, i): | |
if not i % int(self.number.int()): | |
return self.word.str() | |
return '' | |
class AssignmentsBox(BaseBox): | |
def __init__(self, assigments=None, assignment=None): | |
self.assigments = assigments | |
self.assignment = assignment | |
def list(self): | |
if self.assigments: | |
return self.assigments.list() + [self.assignment] | |
return [] | |
class MainBox(BaseBox): | |
def __init__(self, range_box, assignments): | |
self.range_box = range_box | |
self.assignments = assignments | |
""" This runs every fizzbuzz rule on every line and prints the result """ | |
def eval(self): | |
lines = [] | |
for i in self.range_box.range(): | |
line = str(i) + '\t' | |
for assignment in self.assignments.list(): | |
line += assignment.eval_with(i) | |
lines.append(line) | |
return '\n'.join(lines) + '\n' | |
""" | |
## Parser | |
""" | |
pg = ParserGenerator([ | |
'ELLIPSIS', | |
'EQUALS', | |
'NUMBER', | |
'WORD' | |
]) | |
""" | |
### Rules | |
- the space before the ':' is mandatory, but sometimes missing in the tutorial | |
- the order of the rules is important: general to specific (opposite to that of the article) | |
""" | |
@pg.production('main : range assignments') | |
def main(p): | |
return MainBox(p[0], p[1]) | |
@pg.production('assignments : assignments assignment') | |
def expr_assignments(p): | |
return AssignmentsBox(p[0], p[1]) | |
@pg.production('assignments :') | |
def expr_empty_assignments(p): | |
return AssignmentsBox() | |
@pg.production('assignment : word EQUALS number') | |
def assignment_op(p): | |
return AssignmentBox(p[0], p[2]) | |
@pg.production('range : number ELLIPSIS number') | |
def range_op(p): | |
return RangeBox(p[0], p[2]) | |
@pg.production('number : NUMBER') | |
def number(p): | |
return IntBox(p[0]) | |
@pg.production('word : WORD') | |
def word(p): | |
return WordBox(p[0]) | |
parser = pg.build() | |
def begin(argv): | |
if len(argv) < 2: | |
print('./fizz_buzz_lang.py filename') | |
return | |
with open(argv[1], 'r') as f: | |
result = parser.parse(lexer.lex(f.read())) | |
print(result.eval()) | |
""" | |
## main | |
Example code file: | |
``` | |
0...15 | |
fizz=3 | |
buzz=5 | |
``` | |
Output: | |
``` | |
0 fizzbuzz | |
1 | |
2 | |
3 fizz | |
4 | |
5 buzz | |
6 fizz | |
7 | |
8 | |
9 fizz | |
10 buzz | |
11 | |
12 fizz | |
13 | |
14 | |
15 fizzbuzz | |
``` | |
""" | |
if __name__ == '__main__': | |
begin(sys.argv) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment