1
#include "EisenParser.h"
3
#include "../../SyntopiaCore/Logging/Logging.h"
4
#include "../Model/CustomRule.h"
8
using namespace SyntopiaCore::Logging;
9
using namespace StructureSynth::Model;
11
namespace StructureSynth {
21
program = { set | rule } ;
22
rule = 'RULE' , rule_name, { rule_modifier } , '{', { set | action } , '}' ;
23
rule_modifier = { 'MAXDEPTH' integer | WEIGHT float }
24
action = transformation rule_ref ;
25
set = 'SET' , var_name , string ;
27
'rule_ref', 'var_name', and 'string' are text strings with a reasonable set of allowed characters.
33
// C++ style multiline comments are allowed.
34
// A pre-processor strips comments, and imports '#include's
36
#include "../basicstuff.es"
37
SET BackgroundColor = #F00 // All 'SET' commands outside the scope of a rule are executed at startup.
51
EisenParser::EisenParser(Tokenizer* tokenizer) : tokenizer(tokenizer) {
54
EisenParser::~EisenParser() {
58
void EisenParser::getSymbol() {
59
symbol = tokenizer->getSymbol();
62
bool EisenParser::accept(Symbol::SymbolType st) {
63
if (symbol.type == st) {
70
void EisenParser::ruleModifierList(CustomRule* customRule) {
71
while (symbol.type == Symbol::Operator) {
72
if (symbol.text == "weight") {
74
double param = symbol.getNumerical();
75
if (!accept(Symbol::Number)) {
76
throw (ParseError("Rule modifier 'weight' expected numerical argument. Found: " + symbol.text, symbol.pos));
78
customRule->setWeight(param);
80
} else if (symbol.text == "maxdepth") {
82
int param = (int)symbol.getNumerical();
83
if (!symbol.isInteger || !accept(Symbol::Number)) {
84
throw (ParseError("Rule modifier 'maxdepth' expected integer argument. Found: " + symbol.text, symbol.pos));
86
customRule->setMaxDepth(param);
88
if (symbol.type == Symbol::MoreThan) {
90
QString ruleName = symbol.text;
91
if (!accept(Symbol::UserString)) throw (ParseError("After maxdepth retirement operator a rule name is expected. Found: " + symbol.text, symbol.pos));
92
customRule->setRetirementRule(ruleName);
95
throw (ParseError("In rule modifier list: expected maxdepth or weight. Found: " + symbol.text, symbol.pos));
99
if (!symbol.type == Symbol::LeftBracket) {
100
throw (ParseError("After rule modifier list: expected a left bracket. Found: " + symbol.text, symbol.pos));
104
Rule* EisenParser::rule() {
105
// rule = 'RULE' , rule_name, '{', { set | action } , '}' ;
107
if (!accept(Symbol::Rule)) throw (ParseError("Unexpected: trying to parse Rule not starting with rule identifier. Found: " + symbol.text, symbol.pos));
109
QString ruleName = symbol.text;
110
if (!accept(Symbol::UserString)) throw (ParseError("After rule identifier a rule name is expected. Found: " + symbol.text, symbol.pos));
111
CustomRule* customRule = new CustomRule(ruleName);
113
if (symbol.type != Symbol::LeftBracket) {
114
// This must be a rule_modifier list.
115
ruleModifierList(customRule);
119
if (!accept(Symbol::LeftBracket)) throw (ParseError("After rule name a left bracket is expected. Found: " + symbol.text, symbol.pos));
121
// TODO: implement rest of types:
123
// SET something = something
125
// { yaw 20 size 0.1 } rulename
126
// 20 * { forward 10 } rulename
127
while (symbol.type == Symbol::LeftBracket || symbol.type == Symbol::UserString ||
128
symbol.type == Symbol::Number || symbol.type == Symbol::Set) {
130
if (symbol.type == Symbol::Set) {
131
Action a = setAction();
132
customRule->appendAction(a);
135
customRule->appendAction(a);
139
if (!accept(Symbol::RightBracket)) throw (ParseError("A rule definition must end with a right bracket. Found: "+symbol.text, symbol.pos));
144
double degreeToRad(double degrees) {
145
return degrees*3.14159265/180.0;
148
Transformation EisenParser::transformation() {
150
QString type = symbol.text;
151
if (!accept(Symbol::Operator)) throw (ParseError("Transformation: Expected transformation identifier (e.g. 'x' or 'rx'). Found: " + symbol.text, symbol.pos));
154
double param = symbol.getNumerical();
155
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'X' (X-axis translation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
156
return Transformation::createX(param);
157
} else if (type == "y") {
158
double param = symbol.getNumerical();
159
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'Y' (Y-axis translation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
160
return Transformation::createY(param);
161
} else if (type == "z") {
162
double param = symbol.getNumerical();
163
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'Z' (Z-axis translation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
164
return Transformation::createZ(param);
165
} else if (type == "rx") {
166
double param = symbol.getNumerical();
167
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'RX' (X-axis rotation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
168
return Transformation::createRX(degreeToRad(param));
169
} else if (type == "ry") {
170
double param = symbol.getNumerical();
171
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'RY' (Y-axis rotation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
172
return Transformation::createRY(degreeToRad(param));
173
} else if (type == "rz") {
174
double param = symbol.getNumerical();
175
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'RZ' (Z-axis rotation): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
176
return Transformation::createRZ(degreeToRad(param));
177
} else if (type == "hue") {
178
double param = symbol.getNumerical();
179
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'hue': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
180
return Transformation::createHSV(param, 1,1,1);
181
} else if (type == "sat") {
182
double param = symbol.getNumerical();
183
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'sat': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
184
return Transformation::createHSV(0, param,1,1);
185
} else if (type == "brightness") {
186
double param = symbol.getNumerical();
187
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'brightness': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
188
return Transformation::createHSV(0, 1,param,1);
189
} else if (type == "color") {
190
QString param = symbol.text;
191
if (!QColor(param).isValid()) throw (ParseError("Transformation 'color': Expected a valid color. Found: " + symbol.text, symbol.pos));
193
return Transformation::createColor(param);
194
} else if (type == "alpha") {
195
double param = symbol.getNumerical();
196
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'alpha': Expected numerical parameter. Found: " + symbol.text, symbol.pos));
197
return Transformation::createHSV(0, 1,1,param);
198
} else if (type == "matrix") {
200
for (unsigned int i = 0; i < 9; i++) {
201
double param = symbol.getNumerical();
202
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'matrix': Expected nine (9) parameters. Found: " + symbol.text, symbol.pos));
205
return Transformation::createMatrix(ds);
206
} else if (type == "s") {
207
double param = symbol.getNumerical();
208
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'S' (size): Expected numerical parameter. Found: " + symbol.text, symbol.pos));
210
if (symbol.type == Symbol::Number) {
211
double param2 = symbol.getNumerical();
213
double param3 = symbol.getNumerical();
214
if (!accept(Symbol::Number)) throw (ParseError("Transformation 'S' (size): Expected third numerical parameter. Found: " + symbol.text, symbol.pos));
215
return Transformation::createScale(param,param2,param3);
217
return Transformation::createScale(param,param,param);
218
} else if (type == "fx") {
219
return Transformation::createScale(-1,1,1);
220
} else if (type == "fy") {
221
return Transformation::createScale(1,-1,1);
222
} else if (type == "fz") {
223
return Transformation::createScale(1,1,-1);
225
throw (ParseError("Unknown transformation type: " + type, symbol.pos));
229
Transformation EisenParser::transformationList() {
230
// A transformationlist is something like:
235
if (!accept(Symbol::LeftBracket)) throw (ParseError("Transformation List: Expected a left bracket. Found: " + symbol.text, symbol.pos));
237
while (symbol.type == Symbol::Operator) {
238
t.append(transformation());
241
if (!accept(Symbol::RightBracket)) throw (ParseError("Transformation List: Expected a right bracket or an operator. Found: " + symbol.text, symbol.pos));
246
Action EisenParser::action() {
247
// There are 3 types of action statements:
248
// { rx 20 ry 30 rz 20 } rulename
250
// 20 * { x 10 } 10 * { y 10 } rulename
252
if (symbol.type == Symbol::LeftBracket) {
253
Transformation t = transformationList();
254
QString ruleName = symbol.text.trimmed();
255
if (!accept(Symbol::UserString)) throw (ParseError("Expected a rule name after the transformation list. Found: " + symbol.text, symbol.pos));
256
return Action(t, ruleName);
257
} else if (symbol.type == Symbol::UserString) {
258
QString ruleName = symbol.text.trimmed();
259
accept(Symbol::UserString);
260
return Action(ruleName);
261
} else if (symbol.type == Symbol::Number) {
265
while (symbol.type == Symbol::Number) {
266
// number of loops...
267
if (!symbol.isInteger) throw (ParseError("Expected an integer count in the transformation loop. Found: " + symbol.text, symbol.pos));
268
int count = symbol.intValue;
272
if (!accept(Symbol::Multiply)) throw (ParseError("Expected a '*' after the transformation count. Found: " + symbol.text, symbol.pos));
274
// transformation list
275
Transformation t = transformationList();
276
action.addTransformationLoop(TransformationLoop(count, t));
280
QString ruleName = symbol.text.trimmed();
281
if (!accept(Symbol::UserString)) throw (ParseError("Expected a rule name or a new loop after the transformation list. Found: " + symbol.text, symbol.pos));
282
action.setRule(ruleName);
287
throw (ParseError("A rule action must start with either a number, a rule name or a left bracket. Found: "+symbol.text, symbol.pos));
291
Action EisenParser::setAction() {
294
QString key = symbol.text;
295
if (symbol.type == Symbol::Operator && key == "maxdepth") {
297
} else if (!accept(Symbol::UserString)) throw (ParseError("Expected a valid setting name. Found: " + symbol.text, symbol.pos));
298
QString value = symbol.text;
299
getSymbol(); // We will accept everything here!
301
return Action(key,value);
304
RuleSet* EisenParser::ruleset() {
305
RuleSet* rs = new RuleSet();
308
while (symbol.type == Symbol::Rule || symbol.type == Symbol::Set
309
|| symbol.type == Symbol::LeftBracket || symbol.type == Symbol::UserString || symbol.type == Symbol::Number) {
310
if (symbol.type == Symbol::Rule) {
313
} else if (symbol.type == Symbol::Set) {
314
Action a = setAction();
315
rs->getTopLevelRule()->appendAction(a);
318
rs->getTopLevelRule()->appendAction(a);
322
if (!accept(Symbol::End)) throw (ParseError("Unexpected symbol found. At this scope only RULE and SET statements are allowed. Found: " + symbol.text, symbol.pos));
327
RuleSet* EisenParser::parseRuleset() {