Как написать правило грамматики textx для определения стандартных типов данных без их изменения?
Я хочу написать правило грамматики textx, которое может состоять из другого определенного правила или любого типа стандартного типа данных (Int, Float, String и т. Д.).
Это для простого текстового DSL, который должен иметь возможность писать (и переводить в конце) условия, которые могут состоять из других правил грамматики (например, предопределенных функций) или из любых стандартных предопределенных типов данных (String/Int/Float). /Bool/ID).
Итак, я действительно хочу иметь возможность написать что-то вроде, например,
condition insert input data 5 equal 10 BEGIN
...
END
Это означает нормальный ЕСЛИ. insert input data 5
это правило, которое затем переводится в обычный вызов функции insertOutputData(5)
, Грамматика, которую я использую там:
Model: commands*=Command;
Command: Function | Branch;
Function: Func_InsertInputData | Func_InsertOutputData;
Func_InsertInputData: 'insert input data' index=INT;
Func_InsertOutputData: 'insert output data' index=INT;
Branch: 'condition' condition=Condition 'BEGIN'
commands*=Command;
'END'
Condition: Cond_Equal | Cond_And | Cond_False;
Cond_Equal: op1=Operand 'equal' op2=Operand;
Cond_And: op1=Operand 'and' op2=Operand;
Cond_False: op1=Operand 'is false';
Operand: Function | OR_ANY_OTHER_KIND_OF_DATA;
В интерпретаторе я пытаюсь прочитать код, выполнив это:
def translateCommands(cmds):
commands = []
for cmd in cmds:
commands.append(translateCommand(cmd))
return commands
def translateCommand(cmd):
print(cmd)
print(cmd.__class__)
if cmd.__class__.__name__ == 'int' or cmd.__class__.__name__ == 'float':
return str(cmd)
elif cmd.__class__.__name__ == 'str':
return '\'' + cmd + '\''
elif(cmd.__class__.__name__ == 'Branch'):
s = ''
if(cmd.condition.__class__.__name__ == 'Cond_Equal'):
s = 'if ' + translateCommand(cmd.condition.op1) + '==' + translateCommand(cmd.condition.op2) + ':'
if(cmd.condition.__class__.__name__ == 'Cond_And'):
s = 'if ' + translateCommand(cmd.condition.op1) + 'and' + translateCommand(cmd.condition.op2) + ':'
# ...
commandsInBlock = translateCommands(cmd.commands)
for command in commandsInBlock:
s += '\n '+command
return s
В OR ANY OTHER KIND OF DATA
Я попытался это с перечислением фактических типов данных, но это не работает. Если я обработаю модель с кодом DSL, показанным выше с Function | FLOAT | INT | BOOL | ID | STRING
как правило операнда, целые числа (10 после равенства в примере) конвертируются в числа с плавающей точкой
if insertInputData(5)==10.0:
Если я обработаю модель с помощью правила операнда, как Function | INT | FLOAT | BOOL | ID | STRING
Я получаю ошибку
textx.exceptions.TextXSyntaxError: None:13:43: error: Expected 'BEGIN' at position (13, 43) => 't equal 10*.0 BEGIN '.
Результат, который я хотел бы видеть,
if insertInputData(5)==10:
или же
if insertInputData(5)==10.0:
с
condition insert input data 5 equal 10.0 BEGIN
...
END
но textx, кажется, всегда пытается преобразовать значение, которое он получает в этой позиции, в предложенный тип в правиле операнда, что в данном случае плохо. Как мне изменить свое правило, чтобы оно правильно определяло каждый тип данных, ничего не меняя?
РЕДАКТИРОВАТЬ 1
Игорь Деянович только что описал проблему, и я следовал его подходу.
грамматика (соответствующая часть):
Command: Function | Branch | MyNumber;
#...
Oparand: Function | MyNumber | BOOL | ID | STRING;
MyNumber: STRICTFLOAT | INT;
STRICTFLOAT: /[+-]?(((\d+\.(\d*)?|\.\d+)([eE][+-]?\d+)?)|((\d+)([eE][+-]?\d+)))(?<=[\w\.])(?![\w\.])/;
код:
mm = metamodel_from_str(grammar)
mm.register_obj_processors({'STRICTFLOAT': lambda x: float(x)})
dsl_code = '''
10
10.5
'''
model = mm.model_from_str(dsl_code)
commands = iterateThroughCommands(model.commands)
Это приводит к
10
<class 'int'>
'10.5'
<class 'str'>
Итак, чего-то не хватает, чтобы заставить работать объектный процессор...
1 ответ
Проблема в том, что каждое действительное целое число может быть интерпретировано как FLOAT
так что если вы заказываете свои правила как FLOAT | INT |...
Вы получаете float
введите как FLOAT
правило будет соответствовать, но если вы заказываете правила как INT | FLOAT|...
для числа с плавающей точкой парсер будет использовать часть числа до .
и тогда анализ не будет продолжаться.
Это решено в версии разработки textX (см. CHANGELOG.md), введя STRICTFLOAT
правило, которое никогда не будет соответствовать целому числу и встроенному NUMBER
правило изменено на первую попытку совпадения STRICTFLOAT
а потом INT
,
Следующий релиз будет 2.0.0
и это произойдет в ближайшие несколько недель, я надеюсь. В то же время вы можете либо установить напрямую с github, либо изменить свою грамматику, чтобы она выглядела примерно так:
MyNumber: STRICTFLOAT | INT;
STRICTFLOAT: /[+-]?(((\d+\.(\d*)?|\.\d+)([eE][+-]?\d+)?)|((\d+)([eE][+-]?\d+)))(?<=[\w\.])(?![\w\.])/; // or the float format you prefer
И зарегистрируйте объектный процессор для вашего STRICTFLOAT
тип, который будет конвертировать в Python float
, После обновления до textX 2.0.0 вы должны просто заменить ссылки на MyNumber
с NUMBER
в грамматике.
Более подробную информацию можно найти в сообщенной проблеме
РЕДАКТИРОВАТЬ 1:
Предложенное решение в настоящий момент не работает из-за ошибки, сообщенной здесь.
РЕДАКТИРОВАТЬ 2:
Ошибка исправлена в версии для разработчиков. До выхода 2.0.0 вы должны
pip install https://github.com/textX/textX/archive/master.zip
и тогда вам вообще не понадобится обходной путь, если вы не хотите изменять типы по умолчанию.