def _create_parser() -> ParserElement: # operators in the format later used by infixNotation operator_list = [ (None, 2, opAssoc.LEFT, BooleanAndOperation._create_from_implicit_tokens), (CaselessKeyword('not') | "~" | "!", 1, opAssoc.RIGHT, BooleanNotOperation._create_from_tokens), (CaselessKeyword('and') | "&", 2, opAssoc.LEFT, BooleanAndOperation._create_from_tokens), (CaselessKeyword('xor') | "^", 2, opAssoc.LEFT, BooleanXorOperation._create_from_tokens), (CaselessKeyword('or') | "|", 2, opAssoc.LEFT, BooleanOrOperation._create_from_tokens), ] # terms (atoms) that will be combined with the boolean operators term_list = [ (CaselessKeyword('tag'), TagFilterTerm._create_from_tokens), (CaselessKeyword('ingr'), IngredientFilterTerm._create_from_tokens), (CaselessKeyword('unit'), UnitFilterTerm._create_from_tokens), (None, AnyFilterTerm._create_from_tokens), ] # extract keywords that can operator_expressions = [om[0] for om in operator_list if om[0] is not None] term_expressions = [tm[0] for tm in term_list if tm[0] is not None] reserved_expressions = operator_expressions + term_expressions # quoted string indicates exact macthc quoted_filter_string = (QuotedString('"', escChar='\\') | QuotedString("'", escChar='\\')).setResultsName('string') # quoted_filter_string.setDebug(True) quoted_filter_string.setName("quoted_filter_string") quoted_filter_string.setParseAction(ExactFilterString._create_from_tokens) # not quoted string is inexact match, can't contain whitespace or be an operator unquoted_filter_string = ~MatchFirst(reserved_expressions) + Regex(r'[^\s\(\)]+', flags=re.U).setResultsName('string') # unquoted_filter_string.setDebug(True) unquoted_filter_string.setName("unquoted_filter_string") unquoted_filter_string.setParseAction(FuzzyFilterString._create_from_tokens) # regular expressions aren't parsed in the grammar but delegated to python re.compile in the parser action regex_filter_string = QuotedString('/', escChar='\\') regex_filter_string.setName("regex_filter_string") regex_filter_string.setParseAction(RegexFilterString._create_from_tokens) # unquoted_filter_string must be last, so that initial quotes are handled correctly filter_string = regex_filter_string | quoted_filter_string | unquoted_filter_string filter_string.setParseAction(lambda toks: toks[0]) filter_terms = [] for prefix_expression, term_action in term_list: if prefix_expression is not None: filter_term = Combine(prefix_expression + ':' + filter_string.setResultsName("filter_string")) filter_term.setName("filter_term_"+str(prefix_expression.match)) else: filter_term = filter_string.setResultsName("filter_string") filter_term.setName("filter_term_None") # filter_term.setDebug(True) filter_term.addParseAction(term_action) filter_terms.append(filter_term) filter_term = MatchFirst(filter_terms) filter_expr = infixNotation(filter_term, operator_list) return filter_expr
labeloffset.setName('labeloffset') # Bytes can be represented in binary, hex, char, or a number (0-255 or -128-127) # and may include embedded arithmetic # OPCODE 0b00001100 # OPCODE 0x0b # OPCODE 'a' # OPCODE 254-0x0a # OPCODE 'a'&0b00001111 binbyte = Combine(Literal('0b') + Char('01') * 8) binbyte.setName('binbyte') binbyte.setParseAction(lambda t: [int(t[0], 2)]) hexbyte = Combine(Literal('0x') + Char(srange("[0-9a-fA-F]")) * 2) hexbyte.setName('hexbyte') hexbyte.setParseAction(lambda t: [int(t[0], 16)]) chrbyte = QuotedString(quoteChar="'", unquoteResults=True) chrbyte.setName('char') chrbyte.setParseAction(lambda t: [ord(t[0])]) number = Word(nums + '-') number.setName('number') number.setParseAction(lambda t: [int(t[0])]) allbytes = binbyte | hexbyte | chrbyte | number mathtoken = Combine(oneOf('+ - & |') + allbytes) bytemathexpression = Combine(allbytes + OneOrMore(mathtoken)) bytemathexpression.setParseAction(lambda t: [eval(t[0])]) byte = bytemathexpression | allbytes byte.setName('byte') # Words can be represented in binary, hex, label, or number (0-65535 or -32768-32767) # OPCODE 0b0000111100001111 # OPCODE 0x2911 # OPCODE .label # OPCODE .label+4