def define_grammar():
    global grammar
    one_expr = Forward()
    one_or_more_expr = Forward()
    zero_or_more_expr = Forward()

    anum = Word( alphas+"_", alphanums+"_" )    # Starts with a letter or underscore, can continue with numbers too
    nametype_expr = Group(anum + ":" + anum).setParseAction(actionNTP)
    comment_expr = Group(Literal("#") + restOfLine).setParseAction(actionComment)
    list_expr = Group(Suppress("[") + zero_or_more_expr + Optional(Suppress(",") + one_or_more_expr) + Suppress("]")).setParseAction(actionList)

    # Putting it all together.
    # Any earlier Forward-defined expressions must be assigned to using "<<" rather than "="
    # REMEMBER: You MUST put () around the whole RHS of << assignment, or the wrong thing happens
    one_expr << (list_expr | nametype_expr | comment_expr)
    # "|" is pyparsing's MatchFirst operator, so put most general matches at the end so they only gets matched if nothing else does
    # "^" is pyparsing's Or operator, which finds the longest match
    one_or_more_expr << (OneOrMore(one_expr))
    zero_or_more_expr << (ZeroOrMore(one_expr))
    zero_or_more_expr.validate() # Check for recursive loops (NOTE: Highly compute-intensive)

    final_expr = zero_or_more_expr + StringEnd()    # StringEnd() to force consumption of entire string (no trailing stuff)

    grammar = final_expr
Beispiel #2
0
def define_grammar():
    global grammar
    def removechar(s, removechar):
        # Returns string <s> having removed all occurrences of character <removechar> 
        return ''.join(s.split(removechar))
    def removechars(s, removechars):
        for c in removechars:
            s = removechar(s, c)
        return s

    # pyparsing's setResultsName() seems to have a bug: it names both a Group AND its children with the tag, which confuses us later when we traverse the tree
    # So instead, we use setParseAction() to manually create a special tag attribute
    # This can then be read from any x=ParseResults with x.storyelement (which will be the empty string if it doesn't exist)
    def setattr_choice(this):
        this.__setattr__("storyelement","choice")
    def setattr_varlookup(this):
        this.__setattr__("storyelement","var")
    def setattr_varassign(this):
        this[0].insert(0,SETVARINDICATOR)
    def setattr_constassign(this):
        this[0].insert(0,SETCONSTINDICATOR)
                     
    # Our Grammar...
    # Returns a pyparsing object
    ParserElement.setDefaultWhitespaceChars("")
    one_expr = Forward()            # Exactly one expression (of any sort)
    one_or_more_expr = Forward()    # Compound expression (must have at least one expression)
    zero_or_more_expr = Forward()            # Compound expression (can have zero expressions)
    # Any expression which has to be forward-declared MUST then be assigned-to later using "<<" not "=" !!!
    # WARNING: You must put brackets after << otherwise the wrong thing happens. e.g. A << (B | C) without the brackets fails

    # Define allchars as all characters
    allchars = srange("[!-~]")              # All ASCII characters (same as "printables"?)
    allchars = "\t\n " + allchars           # And all whitespace chars

    # Bodytext is all characters which don't have special meaning
    bodychars = removechars(allchars, "[]{|}=<>$")   # Bodytext cannot contain special characters
    
    # Bodytext (meaning text we want to leave alone) is
    # anything consisting entirely of non-special characters
    bodytext = OneOrMore(Word(bodychars)).leaveWhitespace()    # and don't try to strip off any leading whitespace

    htmlchars = allchars
    htmlchars = removechars(htmlchars, ">")
    
    # HTML tags need to be left alone.
    htmltags = Literal("<") - ZeroOrMore(Word(htmlchars)).leaveWhitespace() - Literal(">")

    # A valid variable name starts with a letter
    varname_expr = Word(alphas,alphanums + '_')

    # A use of a variable looks like [varname]
    # varname can be computed, e.g. [{A|B}] is fine
    # but it can't be empty, e.g. []
    var_expr = Group(Literal("[") - one_or_more_expr - Literal("]")).setParseAction(setattr_varlookup)

    # A choice looks like {} or {A} or {A|B} etc.
    # (and of course A & B can be any expression at all, i.e. recursive)
    # and can include null entries e.g. {A|}
    choice_expr = (Group(Literal("{") - Group(Optional(one_or_more_expr,default=""))("firstargs") - ZeroOrMore(Literal("|").suppress() - Group(Optional(one_or_more_expr, default="")("subsargs"))) - Literal("}")).setParseAction(setattr_choice))
    # We use Optional(one_or_more_expr) rather than zero_or_more_expr because that way we can explicitly capture null expressions as choices
    # We use Group() around the subexpressions to ensure that if they are complex they don't get flattened (otherwise we'll end-up choosing from things that are just sub-lists)
    # There seems to be a but with SetResultsName() which causes both the Group and its children to both be named the same (meaning you can't tell when traversing the tree whether you are a choice group, or an element within a choice group).
    # So instead we just leave the {} brackets unsuppressed and use them as a label 

    # Setting a variable looks like [varname]={blah}
    setvar_expr = Group(var_expr + Literal("=") - choice_expr).setParseAction(setattr_varassign)
    # We use "+" (not "-") before Literal("=") because it IS legal for there to be a var_expr on it's own (i.e. when a var is used not set)

    # Using a constant looks like $const
    const_expr = Group(Literal("$") - varname_expr)

    # Setting a constant looks like $const={choice} or $const=[var]
    setconst_expr = Group(const_expr + Literal("=") - (choice_expr ^ var_expr)).setParseAction(setattr_constassign)
    # We use "+" (not "-") before Literal("=") because it IS legal when using rather than assigning a constant
    
    # Putting it all together.
    # Any earlier Forward-defined expressions must be assigned to using "<<" rather than "="
    # REMEMBER: You MUST put () around the whole RHS of << assignment, or the wrong thing happens
    one_expr << (setconst_expr | const_expr | setvar_expr | var_expr | choice_expr | htmltags | bodytext)
    # "|" is pyparsing's MatchFirst operator, so put bodytext at the end so it only gets matched if nothing else does
    # "^" is pyparsing's Or operator, which finds the longest match
    one_or_more_expr << (OneOrMore(one_expr))
    zero_or_more_expr << (ZeroOrMore(one_expr))
    zero_or_more_expr.validate() # Check for recursive loops (NOTE: Highly compute-intensive)

    final_expr = zero_or_more_expr + StringEnd()

    grammar = final_expr
Beispiel #3
0
def Grammar(home):
    # Productions
    cfoperator  =   equalsOp ^ notequalsOp
  
    pathElement=    seperator + identifier
    RelSpec    =    OneOrMore(pathElement)
    AbsSpec    =    identifier + RelSpec

    ExprField  =    AbsSpec ^ RelSpec

    ExprText   =    Forward()   
    BoolExpr   =    ExprField + cfoperator + ExprText 
    QueryExpr  =    BoolExpr + queryOp + ExprText + "/" +ExprText

    ExprText   <<  (  ExprField ^ \
    		          QueryExpr ^ \
                      LiteralVal  )

    ## Functions for parsing.
    def doBool(s,loc,toks):
    	modlogger.debug( "getbol\n")
        sense=(toks[1]=="!=")
    	return sense ^ ( str(toks[0]) == toks[2])


    def doQuery(s,loc,toks):
    	modlogger.debug( "doing query\n")
        if toks[0]:
    		return toks[2]
    	else:
    		return toks[4]

    def ExprFieldAction(s,loc,toks):
        #In this case our walk will fail so
        # just return None as an invalid thang.
        if home is None: return None

        #Determine whether abs or rel and
        # find our origin.
        # We use the home  objct - or
        # derefernce the object if it is an absolute reference.
        # - we can't just get the category because there is (currently)
        #   no object which represents those , but we can get an object
        #   and the grammar is specified such that an object must be spcified
        #   not just a category`
        if toks[0] == ":":
            origin  = home
            path    = toks[1:]
        else:
            origin  = home.get_root().get_object(toks[0],toks[2])
            path    = toks[4:]
        
        #Walk along the attributes
        field = origin
        canonpath = origin.get_nodeid()
        for ele in path:
            if ele != ":": #Skip ':' as grammar noise.
                if field is None:
                    raise NullReference(canonpath)
                canonpath ="%s:%s"%(field.get_nodeid(),ele)
                try:
                    field = field[ele]
                except KeyError:
                    raise NoAttribute("%s has no attribute `%s'"%(field.get_nodeid(),ele))

                if hasattr(field,"getSelf"):
                    field = field.getSelf()
        return field
    
    ## Bind functions to parse actions
    ExprField.setParseAction(ExprFieldAction)
    BoolExpr.setParseAction(doBool)
    QueryExpr.setParseAction(doQuery)

    ExprText.enablePackrat()
    ExprText.validate()
    return ExprText + stringEnd