def parse_atoms(self, tokens, command, min_size, max_size=None): """ Parses a sequence of N atoms (min_size <= N <= max_size) consuming the tokens """ if max_size is None: max_size = min_size res = [] current = None for _ in xrange(min_size): current = tokens.consume() if current == ")": raise PysmtSyntaxError("Expected at least %d arguments in " "%s command." %\ (min_size, command)) if current == "(": raise PysmtSyntaxError("Unexpected token '(' in %s " "command." % command) res.append(current) for _ in xrange(min_size, max_size + 1): current = tokens.consume() if current == ")": return res if current == "(": raise PysmtSyntaxError("Unexpected token '(' in %s " "command." % command) res.append(current) raise PysmtSyntaxError("Unexpected token '%s' in %s command. Expected " \ "at most %d arguments." % (current, command, max_size))
def _enter_annotation(self, stack, tokens, key): """Deals with annotations""" #pylint: disable=unused-argument term = self.get_expression(tokens) tk = tokens.consume() while tk != ")": if not tk.startswith(":"): raise PysmtSyntaxError("Annotations keyword should start with" " colon! Offending token: '%s'" % tk) keyword = tk[1:] tk = tokens.consume() value = None if tk == "(": counter = 1 buff = [tk] while counter != 0: tk = tokens.raw_read() if tk == "(": counter += 1 elif tk == ")": counter -= 1 buff.append(tk) value = "".join(buff) else: value = tk tk = tokens.consume() self.cache.annotations.add(term, keyword, value) assert len(stack[-1]) == 0 # re-add the ")" to the tokenizer because we consumed it, but # get_expression needs it tokens.add_extra_token(")") stack[-1].append(lambda: term)
def _enter_quantifier(self, stack, tokens, key): """Handles quantifiers by defining the bound variable in the cache before parsing the matrix """ mgr = self.env.formula_manager vrs = [] self.consume_opening(tokens, "expression") current = "(" self.consume_opening(tokens, "expression") while current != ")": if current != "(": raise PysmtSyntaxError("Expected '(' in let binding") vname = self.parse_atom(tokens, "expression") typename = self.parse_type(tokens, "expression") var = self._get_var(vname, typename) self.cache.bind(vname, var) vrs.append(var) self.consume_closing(tokens, "expression") current = tokens.consume() quant = None if key == 'forall': quant = mgr.ForAll else: quant = mgr.Exists stack[-1].append(self._exit_quantifier) stack[-1].append(quant) stack[-1].append(vrs)
def parse_atom(self, tokens, command): """Parses a single name from the tokens""" var = tokens.consume() if var == "(" or var == ")": raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (var, command)) return var
def parseDefineFun(parser, tokens): current = tokens.consume() if (current != "define-fun"): raise PysmtSyntaxError("'define-fun' expected", tokens.pos_info) var = parser.get_expression(tokens) if (not var.is_symbol()): raise PysmtSyntaxError("Symbol expected", tokens.pos_info) namedparams = parser.parse_named_params(tokens, "define-fun") if (namedparams): raise PysmtSyntaxError("'()' expected", tokens.pos_info) rtype = parser.parse_type(tokens, "define-fun") ebody = parser.get_expression(tokens) if (not ebody.is_constant()): raise PysmtSyntaxError("Constant expected", tokens.pos_info) current = tokens.consume() return (var, ebody)
def expect(self, token_class, token_repr): """ Check that the next token is the specified one or fail with a ParserError """ if type(self.token) != token_class: raise PysmtSyntaxError("Expected '%s'" % token_repr) self.advance()
def _cmd_declare_sort(self, current, tokens): """(declare-sort <symbol> <numeral>)""" (typename, arity) = self.parse_atoms(tokens, current, 2) try: type_ = self.env.type_manager.Type(typename, int(arity)) except ValueError: raise PysmtSyntaxError("Expected an integer as arity of type %s" % typename) self.cache.bind(typename, type_) return SmtLibCommand(current, [type_])
def parse(self, string): """Parses the content of the given string""" self.token = None self.tokenizer = self.lexer.tokenize(string) self.token = next(self.tokenizer) result = self.expression() try: bd = next(self.tokenizer) raise PysmtSyntaxError("Bogus data after expression: '%s' " "(Partial: %s)" % (bd, result)) except StopIteration: return result
def parse_type(self, tokens, command, additional_token=None): """Parses a single type name from the tokens""" if additional_token is not None: var = additional_token else: var = tokens.consume() if var == "(": op = tokens.consume() if op == "Array": idxtype = self.parse_type(tokens, command) elemtype = self.parse_type(tokens, command) self.consume_closing(tokens, command) return ("Array", idxtype, elemtype) if op != "_": raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (op, command)) ts = tokens.consume() if ts != "BitVec": raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (ts, command)) size = 0 dim = tokens.consume() try: size = int(dim) except ValueError: raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (dim, command)) self.consume_closing(tokens, command) return "BV%d" % size elif var == ")": raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (var, command)) return var
def readModel(parser, modelFile): with open(modelFile) as script: model = {} symbols = parser.env.formula_manager.symbols parser.cache.update(symbols) tokens = Tokenizer(script, interactive=parser.interactive) res = [] current = tokens.consume() if (current != "sat"): raise PysmtSyntaxError("'sat' expected", tokens.pos_info) parser.consume_opening(tokens, "<main>") current = tokens.consume() if (current != "model"): raise PysmtSyntaxError("'model' expected", tokens.pos_info) current = tokens.consume() while current != ")": if (current != "("): raise PysmtSyntaxError("'(' expected", tokens.pos_info) (var, val) = parseDefineFun(parser, tokens) model[var] = val current = tokens.consume() return model
def readModel(parser, modelFile, inputFile): with open(modelFile) as script: lino = 0 for line in script: lino += 1 read_status = line.strip() if read_status != 'success': break if (read_status == "unknown"): print("model_validator_status=UNKNOWN") print("model_validator_error=solver_returned_unknown") sys.exit(0) if (read_status == "unsat"): status = None with open(inputFile, 'r') as infile: for line in infile: if ":status" in line: if "unsat" in line: status = "unsat" print("model_validator_status=UNKNOWN") print( "model_validator_error=the_problem_status_is_unsatisfiable" ) elif "sat" in line: status = "sat" print("model_validator_status=INVALID") print( "model_validator_error=the_problem_status_is_satisfiable" ) elif "unknown" in line: print("model_validator_status=UNKNOWN") print( "model_validator_error=the_problem_status_is_unknown" ) status = "unknown" break # the benchmark scrambler removes the status line, in case of a # benchmark without status line we assume satisfiability if not status: print("model_validator_status=INVALID") print("model_validator_error=solver_returned_unsat") sys.exit(0) if (read_status != "sat"): raise PysmtSyntaxError("'sat' expected at line %d" % lino) # Return UNKNOWN if the output is only "sat" and does not contain a model model, interpretation = parser.parse_model(script) return model, interpretation
def _enter_smtlib_as(self, stack, tokens, key): """Utility function that handles 'as' that is a special function in SMTLIB""" #pylint: disable=unused-argument const = self.parse_atom(tokens, "expression") if const != "const": raise PysmtSyntaxError("expected 'const' in expression after 'as'") ty = self.parse_type(tokens, "expression") def res(expr): return self.env.formula_manager.Array(ty.index_type, expr) def handler(): return res stack[-1].append(handler)
def get_expression(self, tokens): """ Returns the pysmt representation of the given parsed expression """ mgr = self.env.formula_manager stack = [] while True: tk = tokens.consume() if tk == "(": while tk == "(": stack.append([]) tk = tokens.consume() if tk in self.interpreted: fun = self.interpreted[tk] fun(stack, tokens, tk) else: stack[-1].append(self.atom(tk, mgr)) elif tk == ")": try: lst = stack.pop() fun = lst.pop(0) except IndexError: raise PysmtSyntaxError("Unexpected ')'") try: res = fun(*lst) except TypeError as err: if not callable(fun): raise NotImplementedError("Unknown function '%s'" % fun) raise err if len(stack) > 0: stack[-1].append(res) else: return res else: try: stack[-1].append(self.atom(tk, mgr)) except IndexError: return self.atom(tk, mgr)
def atom(self, token, mgr): """ Given a token and a FormulaManager, returns the pysmt representation of the token """ res = self.cache.get(token) if res is None: if token.startswith("#"): # it is a BitVector value = None width = None if token[1] == "b": # binary width = len(token) - 2 value = int("0" + token[1:], 2) else: if token[1] != "x": raise PysmtSyntaxError("Invalid bit-vector constant " "'%s'" % token) width = (len(token) - 2) * 16 value = int("0" + token[1:], 16) res = mgr.BV(value, width) else: # it could be a number or a string try: frac = Fraction(token) if frac.denominator == 1: # We found an integer, depending on the logic this can be # an Int or a Real if self.logic is None or \ self.logic.theory.integer_arithmetic: if "." in token: res = mgr.Real(frac) else: res = mgr.Int(frac.numerator) else: res = mgr.Real(frac) else: res = mgr.Real(frac) except ValueError: # a string constant res = token self.cache.bind(token, res) return res
def get_assignment_list(self, script): """ Parse an assignment list produced by get-model and get-value commands in SmtLib """ symbols = self.env.formula_manager.symbols self.cache.update(symbols) tokens = Tokenizer(script, interactive=self.interactive) res = [] self.consume_opening(tokens, "<main>") current = tokens.consume() while current != ")": if current != "(": raise PysmtSyntaxError("'(' expected") vname = self.get_expression(tokens) expr = self.get_expression(tokens) self.consume_closing(tokens, current) res.append((vname, expr)) current = tokens.consume() self.cache.unbind_all(symbols) return res
def _enter_let(self, stack, tokens, key): """Handles a let expression by recurring on the expression and updating the cache """ #pylint: disable=unused-argument self.consume_opening(tokens, "expression") newvals = {} current = "(" self.consume_opening(tokens, "expression") while current != ")": if current != "(": raise PysmtSyntaxError("Expected '(' in let binding") vname = self.parse_atom(tokens, "expression") expr = self.get_expression(tokens) newvals[vname] = expr self.cache.bind(vname, expr) self.consume_closing(tokens, "expression") current = tokens.consume() stack[-1].append(self._exit_let) stack[-1].append(newvals.keys())
def led(self, parser, left): # BVExtract, Select or Store op = left e1 = parser.expression() if type(parser.token) == ExprElse: #BVExtract parser.advance() end = parser.expression() parser.expect(CloseBrak, "]") return parser.mgr.BVExtract(op, e1.constant_value(), end.constant_value()) elif type(parser.token) == CloseBrak: # Select parser.advance() return parser.mgr.Select(op, e1) elif type(parser.token) == ArrStore: #Store parser.advance() e2 = parser.expression() parser.expect(CloseBrak, "]") return parser.mgr.Store(op, e1, e2) else: raise PysmtSyntaxError("Unexpected token:" + str(parser.token))
def led(self, parser, left): raise PysmtSyntaxError("Syntax error at token '%s' (Read: '%s')." % \ (parser.token, left))
def nud(self, parser): raise PysmtSyntaxError("Syntax error at token '%s'." % parser.token)
def lexing_error(self, read): raise PysmtSyntaxError("Unexpected input: %s" % read)
def parse_type(self, tokens, command, type_params=None, additional_token=None): """Parses a single type name from the tokens""" if additional_token is not None: var = additional_token else: var = tokens.consume() res = None if type_params and var in type_params: return (var, ) # This is a type parameter, it is handled recursively elif var == "(": op = tokens.consume() if op == "Array": idxtype = self.parse_type(tokens, command) elemtype = self.parse_type(tokens, command) self.consume_closing(tokens, command) res = self.env.type_manager.ArrayType(idxtype, elemtype) elif op == "_": ts = tokens.consume() if ts != "BitVec": raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (ts, command)) size = 0 dim = tokens.consume() try: size = int(dim) except ValueError: raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (dim, command)) self.consume_closing(tokens, command) res = self.env.type_manager.BVType(size) else: # It must be a custom-defined type base_type = self.cache.get(op) if base_type is None or not isinstance(base_type, _TypeDecl): raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (op, command)) pparams = [] has_free_params = False for _ in range(base_type.arity): ty = self.parse_type(tokens, command, type_params=type_params) pparams.append(ty) if isinstance(ty, tuple): has_free_params = True if has_free_params: def definition(*args): params = [] for x in pparams: if isinstance(x, tuple): params.append(args[type_params.index(x[0])]) else: params.append(x) return self.env.type_manager.get_type_instance( base_type, *params) res = PartialType("tmp", definition) else: res = self.env.type_manager.get_type_instance( base_type, *pparams) self.consume_closing(tokens, command) elif var == "Bool": res = self.env.type_manager.BOOL() elif var == "Int": res = self.env.type_manager.INT() elif var == "Real": res = self.env.type_manager.REAL() else: cached = self.cache.get(var) if cached is not None: res = self.cache.get(var) else: raise PysmtSyntaxError("Unexpected token '%s' in %s command." % \ (var, command)) if isinstance(res, _TypeDecl): return res() else: return res
def _smtlib_underscore(self, stack, tokens, key): #pylint: disable=unused-argument """Utility function that handles _ special function in SMTLIB""" mgr = self.env.formula_manager op = self.parse_atom(tokens, "expression") fun = None if op == "extract": send = self.parse_atom(tokens, "expression") sstart = self.parse_atom(tokens, "expression") try: start = int(sstart) end = int(send) except ValueError: raise PysmtSyntaxError("Expected number in '_ extract' " "expression") fun = lambda x: mgr.BVExtract(x, start, end) elif op == "zero_extend": swidth = self.parse_atom(tokens, "expression") try: width = int(swidth) except ValueError: raise PysmtSyntaxError("Expected number in '_ zero_extend' " "expression") fun = lambda x: mgr.BVZExt(x, width) elif op == "repeat": scount = self.parse_atom(tokens, "expression") try: count = int(scount) except ValueError: raise PysmtSyntaxError("Expected number in '_ repeat' " "expression") fun = lambda x: mgr.BVRepeat(x, count) elif op == "rotate_left": sstep = self.parse_atom(tokens, "expression") try: step = int(sstep) except ValueError: raise PysmtSyntaxError("Expected number in '_ rotate_left' " "expression") fun = lambda x: mgr.BVRol(x, step) elif op == "rotate_right": sstep = self.parse_atom(tokens, "expression") try: step = int(sstep) except ValueError: raise PysmtSyntaxError("Expected number in '_ rotate_left' " "expression") fun = lambda x: mgr.BVRor(x, step) elif op == "sign_extend": swidth = self.parse_atom(tokens, "expression") try: width = int(swidth) except ValueError: raise PysmtSyntaxError("Expected number in '(_ sign_extend) " "expression'") fun = lambda x: mgr.BVSExt(x, width) elif op.startswith("bv"): try: v = int(op[2:]) width = int(self.parse_atom(tokens, "expression")) except ValueError: raise PysmtSyntaxError("Expected number in '_ bv' expression: " "'%s'" % op) fun = mgr.BV(v, width) else: raise PysmtSyntaxError("Unexpected '_' expression '%s'" % op) stack[-1].append(lambda: fun)
def _res(a, b): if b.is_constant(): return op(a, b.constant_value()) else: raise PysmtSyntaxError("Constant expected, got '%s'" % b)
def create_generator(reader): """Takes a file-like object and produces a stream of tokens following the LISP rules. This is the method doing the heavy-lifting of tokenization. """ spaces = set([" ", "\n", "\t"]) separators = set(["(", ")", "|", "\""]) specials = spaces | separators | set([";", ""]) c = next(reader) eof = False while not eof: if c in specials: # consume the spaces if c in spaces: c = next(reader) elif c in separators: if c == "|": s = [] c = next(reader) while c and c != "|": if c == "\\": # This is a single '\' c = next(reader) if c != "|" and c != "\\": # Only \| and \\ are supported escapings raise PysmtSyntaxError("Unknown escaping in " \ "quoted symbol: " "'\\%s'" % c) s.append(c) c = next(reader) if not c: raise PysmtSyntaxError("Expected '|'") yield "".join(s) c = next(reader) elif c == "\"": # String literals s = [] c = next(reader) while c: if c == "\"": c = next(reader) if c == "\"": s.append(c) c = next(reader) else: break else: s.append(c) c = next(reader) if not c: raise PysmtSyntaxError("Expected '|'") yield '"%s"' % ( "".join(s) ) # string literals maintain their quoting else: yield c c = next(reader) elif c == ";": while c and c != "\n": c = next(reader) c = next(reader) else: # EOF eof = True assert len(c) == 0 else: tk = [] while c not in specials: tk.append(c) c = next(reader) yield "".join(tk)
def test_pysmt_syntax_error(self): from pysmt.exceptions import PysmtSyntaxError try: raise PysmtSyntaxError("'define-fun' expected", (5,5)) except PysmtSyntaxError as ex: self.assertEqual(str(ex), "Line 5, Col 5: 'define-fun' expected")
def consume_closing(self, tokens, command): """ Consumes a single ')' """ p = tokens.consume() if p != ")": raise PysmtSyntaxError("Unexpected token '%s' in %s command. " \ "Expected ')'" % (p, command))
def readModel(parser, modelFile, inputFile): with open(modelFile) as script: model = {} symbols = parser.env.formula_manager.symbols parser.cache.update(symbols) tokens = Tokenizer(script, interactive=parser.interactive) res = [] current = tokens.consume() if (current == "unknown"): print ("model_validator_status=UNKNOWN") print ("model_validator_error=solver_returned_unknown") sys.exit(0) if (current == "unsat"): status = None with open(inputFile, 'r') as infile: for line in infile: if ":status" in line: if "unsat" in line: status = "unsat" print ("model_validator_status=UNKNOWN") print ("model_validator_error=the_problem_status_is_unsatisfiable") elif "sat" in line: status = "sat" print ("model_validator_status=INVALID") print ("model_validator_error=the_problem_status_is_satisfiable") elif "unknown" in line: print ("model_validator_status=UNKNOWN") print ("model_validator_error=the_problem_status_is_unknown") status = "unknown" break # the benchmark scrambler removes the status line, in case of a # benchmark without status line we assume satisfiability if not status: print ("model_validator_status=INVALID") print ("model_validator_error=solver_returned_unsat") sys.exit(0) if (current != "sat"): raise PysmtSyntaxError("'sat' expected", tokens.pos_info) # Return UNKNOWN if the output is only "sat" and does not contain a model try: current = tokens.consume_maybe() except StopIteration: print ("model_validator_status=UNKNOWN") print ("model_validator_error=no_output") sys.exit(0) if (current != "("): raise PysmtSyntaxError("'(' expected", tokens.pos_info) current = tokens.consume() # Backwards compatibility: skip optional model keyword if (current == "model"): current = tokens.consume() while current != ")": if (current != "("): raise PysmtSyntaxError("'(' expected", tokens.pos_info) (var, val) = parseDefineFun(parser, tokens) model[var] = val current = tokens.consume() return model