SHARE
TWEET

Untitled

a guest Feb 27th, 2020 80 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import collections
  2. import re
  3.  
  4. def compile(source):
  5.     return Parser(source)
  6.    
  7. Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])
  8. Expression = collections.namedtuple('Expression', ['type', 'sub', 'max', 'min'])
  9.  
  10. class Parser:
  11.    
  12.     length = 1
  13.     code = "True"
  14.     cols = set()
  15.     def __init__(self, source):
  16.         tokenizer = Parser.tokenize(source)
  17.         condition = Parser.get_exp(tokenizer)
  18.         self.code, self.cols = Parser.get_string(condition)
  19.         try:
  20.             self.length = condition.max - condition.min + 1
  21.         except TypeError:
  22.             #either min or max or both are un-set, thus the minimum trace length is 1
  23.             self.length = 1
  24.    
  25.     def get_string(expression):
  26.         #print(expression)
  27.         list = []
  28.         cols = set()
  29.         #print(isinstance(expression, Token))
  30.         if isinstance(expression, str):
  31.             return expression, set()
  32.         if isinstance(expression, Token):
  33.             if expression.type == 'ATOM':
  34.                 return expression.value.capitalize(), set()
  35.             elif expression.type == 'NUMBER':
  36.                 return str(expression.value), set()
  37.             elif expression.type == 'FUNCTION':
  38.                 print(expression)
  39.                 return str(expression.value)
  40.         else:
  41.             for x in expression.sub:
  42.                 sub = Parser.get_string(x)
  43.                 list.append(sub[0])
  44.                 cols.update(sub[1])
  45.             if expression.type == 'COMPERATOR':
  46.                 print(list[0])
  47.                 print(list[1])
  48.                 col = ' '.join(list)
  49.                 print()
  50.                 return "ยง(" + col + ')', {col}
  51.         inner = ','.join(list)
  52.         if expression.type == 'None':
  53.             return inner, set()
  54.         code = expression.type.format(inner) if '{}' in expression.type else expression.type + '(' + inner + ')'
  55.         return code, cols
  56.    
  57.     comperators = {
  58.     '>':    'GREATER',
  59.     '<':    'LESSER',
  60.     '>=':   'GREATER_EQUAL',
  61.     '<=':   'LESSER_EQUAL',
  62.     '==':   'EQUAL',
  63.     '!=':   'NOT_EQUAL',
  64.     '<>':   'NOT_EQUAL',
  65.     }
  66.     def get_exp(tokenizer):
  67.         c_sub = []
  68.         c_type = 'None'
  69.         #The endpoint of the expression
  70.         c_max = 1
  71.         #The startingpoint of the expression
  72.         c_min = 1
  73.        
  74.         for t in tokenizer:
  75.             #Go one level deeper
  76.             if t.type == 'OPEN':
  77.                 sub = Parser.get_exp(tokenizer)
  78.                 c_max = max(sub.max, c_max) if isinstance(sub, int) else ''
  79.                 c_min = min(sub.min, c_min) if isinstance(sub, int) else ''
  80.                 c_sub.append(sub)
  81.             #return from a sub-expression
  82.             elif t.type == 'CLOSE':
  83.                 return Expression(c_type, c_sub, c_max, c_min)
  84.             elif t.type == 'COMPERATOR':
  85.                 c_type = 'COMPERATOR'
  86.                 c_sub.append(Parser.comperators[t.value])
  87.             elif t.type in ['AND', 'mean']:
  88.                 c_type = t.type
  89.             elif t.type == 'SLICER':
  90.                 #deconstruct the slicer
  91.                 values =  [int(x) if isint(x) else '' for x in t.value[1:-1].split(':')]
  92.                 if len(values) == 1:
  93.                     #slicer is of form [int]
  94.                     c_min = c_max = values[0]
  95.                 else:
  96.                     #slicer is of form [int:int]
  97.                     c_min, c_max = values
  98.                    
  99.                 try:
  100.                     always_value = ',' + str(c_max - c_min + 1)
  101.                 except TypeError:
  102.                     always_value = ''
  103.                    
  104.                 #Restructure the slicer into a NEXT(ALWAYS()) Expression
  105.                 if c_min == 0 or c_min == '': c_type = 'ALWAYS({}' + always_value + ')'
  106.                 else: c_type = 'NEXT(ALWAYS({}' + always_value + '),' + str(c_min) + ')'
  107.             else:
  108.                 c_sub.append(t)
  109.                
  110.         return Expression(c_type, c_sub, c_max, c_min)
  111.            
  112.     def tokenize(code):
  113.         keywords = {'AND', 'OR', 'UNTIL'}
  114.         token_specification = [
  115.             ('NUMBER',      r'\d+(\.\d*)?'),
  116.             ('ACCESSOR',    r'@\w+:\w+'),
  117.             ('OPEN',        r'\('),
  118.             ('CLOSE',       r'\)'),
  119.             ('SLICER',      r'\[\d*:?\d*\]'),
  120.             ('ATOM',        r'true|false'),
  121.             ('KEYWORD',     '|'.join(keywords)),
  122.             ('FUNCTION',    r'[A-Za-z_]+'),
  123.             ('VARIABLE',    r'\$[A-Za-z_]+\w*'),
  124.             ('COMPERATOR',  r'>=|<=|==|!=|<>|>|<'),
  125.             ('NEWLINE',     r'\n'),
  126.             ('SKIP',        r'\s'),
  127.             ('MISMATCH',    r'.'),
  128.         ]
  129.         tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
  130.         line_num = 1
  131.         line_start = 0
  132.         for mo in re.finditer(tok_regex, code):
  133.             kind = mo.lastgroup
  134.             value = mo.group()
  135.             column = mo.start() - line_start
  136.             if kind == 'NUMBER':
  137.                 value = float(value) if '.' in value else int(value)
  138.             elif kind == 'KEYWORD':
  139.                 kind = value
  140.             elif kind == 'NEWLINE':
  141.                 line_start = mo.end()
  142.                 line_num += 1
  143.                 continue
  144.             elif kind == 'SKIP':
  145.                 continue
  146.             elif kind == 'MISMATCH':
  147.                 raise RuntimeError(f'{value!r} unexpected on line {line_num}')
  148.             yield Token(kind, value, line_num, column)
  149.    
  150.    
  151.     def __str__(self):
  152.         return str(self.cols) + '\n[' + str(self.length) + ']\n' + self.code
  153.  
  154. def isint(s):
  155.     try:
  156.         int(s)
  157.         return True
  158.     except ValueError:
  159.         return False
  160.  
  161. if __name__ == '__main__':
  162.     #print(compile('(true)[:1]'))
  163.     print(compile('($steps >= 50) AND (mean($my_var 100) > 20)'))
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top