Read time: 2.5 minutes (248 words)
OpenSCAD Declarations¶
Our first real job in building something that will help in analyzing a model design is to teach our parser to handle OpenSCAD variable declarations. These have a simple form:
name = value ;
The rule that provides this new grammar form is simple as well:
declaration
=
id:identifier
'='
~ value:expression
';'
;
There is a new notation in this rule. The ~ symbol tells TatSu to continue processing this rule from this point on, even if it fails. This just helps speed up parsing by eliminating additional tests to find alternatives that might work. Once we see that equal sign, we know this should be a declaration and we are telling TatSu that here.
Once again, the parser will be building a Python dictionary for this part of the AST. Our tests need to check these dictionary strings, as we did for expressions
1import pytest
2import tatsu
3
4
5@pytest.mark.parametrize('t, e', [
6 ('A = 2;', "{'id': 'A', 'value': {'int': '2'}}"),
7 ('A = B;', "{'id': 'A', 'value': 'B'}"),
8])
9def test_declarations(scadparser, t, e):
10 ast = scadparser.parse(t, start='declaration')
11 assert str(ast) == e
12
Obviously, we could have a more complex expression on the right hand side of the equal sign, but we have already tested that.
Parsing a Real OpenSCAD File¶
We have almost enough to begin processing real OpenSCAD code files. To to that we need a top-level rule that begins our parsing. Traditionally, this rule is named start and it is placed at the top of our EBNF file.
start
=
{ @:statement }+
;
statement
=
| fileinclude
| declaration
;
fileinclude
=
'include'
~
'<'
file: filename
'>'
;
filename
=
/[^>]+/
;
There are a few more rules here, which will be needed in our next section.
Here is a piece of code that will read a real OpenSCAD data file and parse the contents.
1import tatsu
2from pprint import pprint
3
4
5def test():
6 g = open('scadparser/ebnf/scad.ebnf').read()
7 parser = tatsu.compile(g)
8 code = open('scad/constraints.scad').read()
9 ast = parser.parse(code, start='start')
10 pprint(ast)
11
12if __name__ == '__main__':
13 test()
Here is our test OpenSCAD file:
1// constraints.scad
2
3max_wing_span = 18;
4wing_x_offset = 1.675;
And, this is the result of running that code:
$ python sandbox/step02.py
[{'id': 'max_wing_span', 'value': {'int': '18'}},
{'id': 'wing_x_offset', 'value': {'float': '1.675'}}]
The final output is a list of the declarations found in the constraints.scad data file.