Source code for contracts.syntax

from numbers import Number
import math


# All the imports from pyparsing go here
from pyparsing import (delimitedList, Forward, Literal,
                       stringEnd, nums, Word, CaselessLiteral, Combine,
                       Optional, Suppress, OneOrMore, ZeroOrMore, opAssoc,
                       operatorPrecedence, oneOf, ParseException,
                       ParserElement,
                       alphas, alphanums, ParseFatalException,
                       ParseSyntaxException, FollowedBy, NotAny, Or,
                       MatchFirst, Keyword, Group, White, lineno, col)


# from .pyparsing_utils import myOperatorPrecedence


# Enable memoization (much faster!)
if True:
    ParserElement.enablePackrat()
else:
    # Pyparsing 2.0
    from pyparsing import infixNotation
    myOperatorPrecendence = infixNotation

from .interface import Where


[docs]class ParsingTmp(): # TODO: FIXME: decide on an order, if we do the opposite it doesn't work. contract_types = [] keywords = []
[docs]def add_contract(x): ParsingTmp.contract_types.append(x)
[docs]def add_keyword(x): """ Declares that x is a keyword --- this is useful to have more clear messages. "keywords" are not parsed by Extension. (see extensions.py) and allows to have "deep" error indications. See http://pyparsing.wikispaces.com/message/view/home/620225 and the discussion of the "-" operator in the docs. """ ParsingTmp.keywords.append(x)
W = Where O = Optional S = Suppress basenumber = Word(nums) point = Literal('.') e = CaselessLiteral('E') plusorminus = Literal('+') | Literal('-') integer = Combine(O(plusorminus) + basenumber) integer.setParseAction(lambda tokens: SimpleRValue(int(tokens[0]))) floatnumber = Combine( O(plusorminus) + integer + (point + O(basenumber)) ^ (e + integer)) floatnumber.setParseAction(lambda tokens: SimpleRValue(float(tokens[0]))) pi = Keyword('pi').setParseAction( lambda tokens: SimpleRValue(math.pi, 'pi')) # @UnusedVariable try: import numpy except ImportError: numpy = None
[docs]def isnumber(x): # These are scalar quantities that we can compare (=,>,>=, etc.) if isinstance(x, Number): return True if numpy is not None and isinstance(x, numpy.number): return True return False
rvalue = Forward() rvalue.setName('rvalue') contract_expression = Forward() contract_expression.setName('contract') simple_contract = Forward() simple_contract.setName('simple_contract') # Import all expressions -- they will call add_contract() from .library import (EqualTo, Unary, Binary, composite_contract, identifier_contract, misc_variables_contract, scoped_variables_ref, int_variables_contract, int_variables_ref, misc_variables_ref, SimpleRValue) number = pi | floatnumber | integer operand = number | int_variables_ref | misc_variables_ref | scoped_variables_ref operand.setName('r-value') op = operatorPrecedence # op = myOperatorPrecedence rvalue << op(operand, [ ('-', 1, opAssoc.RIGHT, Unary.parse_action), ('*', 2, opAssoc.LEFT, Binary.parse_action), ('-', 2, opAssoc.LEFT, Binary.parse_action), ('+', 2, opAssoc.LEFT, Binary.parse_action), ('^', 2, opAssoc.LEFT, Binary.parse_action), ]) # I want # - BindVariable to have precedence to EqualTo(VariableRef) # but I also want: # - Arithmetic to have precedence w.r.t BindVariable # last is variables add_contract(misc_variables_contract) add_contract(int_variables_contract) add_contract(rvalue.copy().setParseAction(EqualTo.parse_action)) hardwired = MatchFirst(ParsingTmp.contract_types) hardwired.setName('Predefined contract expression') simple_contract << (hardwired | identifier_contract) simple_contract.setName('simple contract expression') any_contract = composite_contract | simple_contract any_contract.setName('Any simple or composite contract') contract_expression << (any_contract) # Parentheses before << !!