Writting a brainf*ck parser with BLK
At last! at this time BLK can compile a reasonable C subset... well, ok, it needs pointers and structures in order to have something decent, but the former are picky to simulate in a interpreter, and the structures are in the way. The important thing is that the general structure is more or less stable, even inside the prototyping branch, a consecuence is that is open to any participation :), so let's see an example of how to write a little brainf*ck parser.
The first thing we'll do is to import the bytecode manager and prepare a constant to hold the memory size, in this case it will be a static
1 2 3 |
|
The parser will be encapsulated in a class with the following scheme
1 2 3 4 5 |
|
Now comes the class intializer function, it receives the bytecode manager as the only parameter
1 2 |
|
Now we have to prepare a space to hold the code, it gets grouped in frames (which nearly matches the concept of C blocks, they can represent functions, loops or condicionals), at least one is needed in the code to be executable by the interpreter, let's call it '_start'
1 2 |
|
The syntax is:
1 |
|
Now we can define variables and operations inside that, let's manually create a cursor variable to point the current memory position and assign it the value 0
1 2 |
|
Here we have two more functions, the first one, Bytecode_manager.add_variable
receives the variable name, it's type, the frame and optionally a comment about
the variable as parameters.
Bytecode_manager.add_op
adds an operation to the frame, the first parameter is
the type of operation, the second one, a reference to the variable where the
result will be stored, as third the function parámeters (it would be a list if
there's more than one), and at last the frame.
It's also posible to create a new type to represent our variables, for example an array to keep track of the memory. Types are represented as dictionaries, al least with the attribute 'byte_size' with the obvious use, there are, too, other useful values like 'structure', which tells the size of the dimensions of the type, for example [2, 3] for a 2x3 array
1 2 |
|
The same can be done with variable references, if they are passed as dictionaries, it's possible to tell in 'pos' which part should be taken into account
1 2 3 |
|
The rest of the parse function doens't need anything new, just ADD_OP and SUB_OP over the two references we just defined, all less the '[' which will be mentioned next
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
To add a flow contro operation we'll use Bytecode_manager.add_branch
,
pasing as first parameter the branching type
(WHILE_BRANCH
, IF_BRANCH
, ELSE_BRANCH
or DO_WHILE_BRANCH
),
the second is the condition, if it's different than 0, the conditional is
executed or the loop keeps running, the third is the frame, it
also optionally accepts two more, one for the operations which take place
before the condition is checked and the last one for operations which takes
place befere from the second iteration on, this operation returns a frame for
the operations which takes place inside it.
1 |
|
The rest of the code is generic enough to barely need any explanation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Lo que si que se hace necesario mencionar es Bytecode_manager.add_entry_point
que indica la función por la que entrar y Bytecode_manager.save
para guardar
el código
Which does need get mentioned is Bytecode_manager.add_entry_point
to point the
entry function and Bytecode_manager.save
to save the code
O que si que é necesario mencionar é Bytecode_manager.add_entry_point
que
indica a función pola que entrar, e Bytecode_manager.save
para garda-lo código
The full source code is here:
See you