Чтобы сгенерировать большие тестовые данные для синтаксического анализатора выражений (на основе сортировочной станции Дейкстры), я придумал следующий скрипт Python:
#!/usr/bin/python
import ast
import sys
import random
import operator as op
def gen_digit(n):
i = 0
digit = ""
if random.randint(0, 1e06) % 17 == 0:
digit = digit + "-"
while i < n:
if i == 0:
digit = digit + str(random.randint(1, 9))
else:
digit = digit + str(random.randint(0, 9))
i = i + 1
return digit;
def rnd_op():
ops = [ "+", "-", "*", "/", "%" ]
return ops[random.randint(0, 4)]
operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
ast.Div: op.truediv, ast.Mod: op.mod, ast.USub: op.neg}
def eval_(node):
if isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
return operators[type(node.op)](eval_(node.left), eval_(node.right))
elif isinstance(node, ast.UnaryOp):
return operators[type(node.op)](eval_(node.operand))
else:
raise TypeError(node)
def eval_expr(expr):
return eval_(ast.parse(expr, mode='eval').body)
def right_op(op, expr):
if op == "/" or op == "%":
try:
v = eval_expr(expr)
except ZeroDivisionError:
v = 0
if v == 0:
return op + " (" + expr + " + " + gen_digit(random.randint(1, 4)) + ")"
else:
return op + " " + expr
else:
return op + " " + expr
def gen_term():
term = ""
if random.randint(0, 1e06) % 17 == 0:
term += "-"
term += "(" + right_op(gen_digit(random.randint(1, 4)), \
right_op(rnd_op() + " " + gen_digit(random.randint(1, 4)), \
rnd_op() + " " + gen_digit(random.randint(1, 4)))) + ")"
return term
def build_expr():
return "(" + gen_term() + " " + \
right_op(rnd_op(), gen_term()) + " " + \
right_op(rnd_op(), gen_term()) + ")"
def rnd_expr(expr, m, d):
if d < m:
expr = "(" + build_expr() + " " + \
right_op(rnd_op(), rnd_expr(expr, m, d + 1)) + " " + \
right_op(rnd_op(), build_expr()) + ")"
return expr
argc = len(sys.argv)
if argc > 1:
dpth = int(sys.argv[1]);
sys.setrecursionlimit(dpth * 10)
print (rnd_expr(build_expr(), dpth, 0))
else:
print (rnd_expr(build_expr(), 1, 0))
Моя реализация маневровой станции (еще один проект C++
) верна и принимает четыре основных арифметических оператора плюс % (по модулю).
Я хочу, чтобы сгенерированное выражение было допустимым, но в настоящее время я иногда сталкиваюсь с делением/по модулю на ноль ошибок, несмотря на то, что пытался их избежать. Далее ast
переполняется при глубине рекурсии больше 98
.
Изменить: ошибки деления/по модулю на ноль возникают не в скрипте Python, а при синтаксическом анализе с помощью внешнего инструмента, такого как bc
на Linux.
У кого-нибудь есть идеи, почему функция right_op
или алгоритм вообще иногда дает сбой.
bc
(калькулятор командной строки для Linux). Я не буду вдаваться в этот вопрос. - person Heiko Schäfer   schedule 13.02.2016while(./rnd_expr.py 40 | expr2cf); do true; done
- person Heiko Schäfer   schedule 13.02.2016