robturtle
1/4/2017 - 3:09 AM

DFA created by robturtle - https://repl.it/EzaW/28

DFA created by robturtle - https://repl.it/EzaW/28

corrects = [('0', 0), ('0.', 0), ('.0', 0), ('1.', 1), ('.1', .1),
            ('123.', 123), ('123', 123), ('.123', .123), ('0.0', 0), ('1.0', 1),
            ('0.1', .1), ('1.1', 1.1), ('12.1', 12.1), ('1.12', 1.12),
            ('0e0', 0), ('0e123', 0), ('1e0', 1), ('1e123', 1), ('123e0', 1),
            ('123e13', 123e13), ('3.14e7', 3.14e7), ('3.14e0', 1),
            ('3.14e+7', 3.14e7), ('3.14e-7', 3.14e-7), ('+3.14e+7', 3.14e7),
            ('-3.14e-7', -3.14e-7), ('-3.14E+7', -3.14e7),
            ('-3.141592653e-3', -3.141592653e-3)]
            
wrongs = ['+', '-', '++', '--', '+-', '.', '..', 'e', 'E',
          'eE', 'EE', 'yang', 'f', '3e', 'e7', '3e-', '4e+',
          '+3e', '-4E', '1.e', '.1e', '1.2E', '1.2e3.4', '1.2e.4',
          '1.2e4.', '+1.2e1+', '1.2e1+', '1.2E1+2', '1.2f1', '-1.2f+12',
          '1.fe+1.']
          
def test(parser):            
  for s, v in corrects:
    try:
      value = parser(s)
      if abs(value - v) > 1e-5:
        print('Wrong result for {!r}, expect {:5g}, got {:5g} instead'.format(s, v, value))
      else:
        print('{!r}: pass'.format(s))
    except SyntaxError as err:
      print('Wrong result for {!r}, expect {:5g}, got SyntaxError:\n{}'.format(s, v, err))
      
  for s in wrongs:
    try:
      value = parser(s)
      print('Wrong result for {!r}, expect SyntaxError, got {:5g} instead'.format(s, value))
    except SyntaxError:
      print('{!r}: pass'.format(s))
import string
from DFA import parser 

@parser
class Power(object):
  '''Get the exponent part value
  syntax: <"e"|"E"> ["+"|"-"] <digits+>
  '''
  def __init__(self):
    self.sign = 1
    self.power = 0
    
  def addDigit(self, c):
    self.power = self.power * 10 + ord(c) - ord('0')
  
  def setSign(self, c):
    self.sign = 1 if c == '+' else -1
    
  def get(self):
    return self.sign * self.power
    
  rules = [
    { 'eE': (1, None) },
    {
      '+-': (3, setSign),
      string.digits: (2, addDigit)
    },
    { string.digits: (2, addDigit) },
    { string.digits: (2, addDigit) }
  ]
  
  terminals = (2,)

examples = ['e', '3', 'E', 'e+', 'E-', 
            'e-', 'E+', 'e+31109', 'e0',
            'E1', 'e-13412', 'e++', 'e--', 
            'E+-', 'E-+', 'e1e4', 'e4+3', 
            'e13.2']
            
def runExample():
  print('-'*40)
  print('{:^40s}'.format('Exponent Parser Demo:'))
  print('-'*40)
  for e in examples:
    try:
      power = Power.parse(e)
      print('Correct: {}'.format(power))
    except SyntaxError as err:
      print(err)
  print('-'*40)

#runExample()
import NFA
from collections import defaultdict
from functools import reduce
from copy import deepcopy

class State:
  
  id = 0
  
  def __init__(self):
    self.edges = defaultdict(lambda: set())
    self.id = State.id
    State.id += 1
    
  def __repr__(self):
    return str(self.id)
    
  def link(self, char, dest):
    self.edges[char].add(dest)
    
  def __getitem__(self, char):
    return self.edges[char]
    
  def epsilonClosure(self):
    met = set()
    current = {self}
    while len(current) > 0:
      met = met | current
      current = reduce(set.union, map(lambda s: set(s[None]), current), set()) - met
    return met
  
  def copy(self, mapping = {}):
    clone = State()
    mapping[self] = clone
    for char, destinations in self.edges.items():
      for dest in destinations:
        if dest not in mapping:
          mapping[dest] = dest.copy(mapping)
        clone.link(char, mapping[dest])
    return clone
    
  def children(self):
    return reduce(set.union, self.edges.values(), set())
    
  def depthFirst(self, visitor, met = None):
    if met is None:
      met = set()
      
    met.add(self)
    visitor(self)
    print('children = {}'.format(self.children()))
    print('met = {}'.format(met))
    unmet = self.children() - met
    print('unmet = {}'.format(unmet))
    for child in self.children() - met:
      if child not in met:
        child.depthFirst(visitor, met)
        
  def printEdges(self):
    self.depthFirst(lambda v: print('{!r} -> {}'.format(v, v.children())))

l = [None] * 5
for i in range(len(l)):
  l[i] = State()
  
l[0].link(1, l[1])
l[0].link(1, l[2])
l[1].link(3, l[0])
l[1].link(5, l[2])
l[2].link(7, l[3])
l[2].link(6, l[4])
l[4].link(2, l[0])
l[4].link(1, l[3])

l[0].depthFirst(lambda v: print(v))
l[0].printEdges()
print('-'*40)
l[0].copy().depthFirst(lambda v: print(v))
import string
import NFA
from NFA import NFA

'''
floatLiteral = [sign] pointfloat [exp]

pointfloat = [intpart] fraction | intpart ["."]
intpart = [0-9]+
fraction = "." intpart

exp = /[eE]/ [sign] intpart
'''
sign = NFA().concat('-+').oneOrZero()

intpart = NFA().concat(string.digits).oneOrMore()
fraction = NFA().concat('.').concat(intpart)
pointfloat = intpart.oneOrZero().concat(None, fraction)
pointfloat.either(intpart.concat(NFA().concat('.').oneOrZero()))

exp = NFA().concat('eE').concat(None, sign).concat(None, intpart).oneOrZero()

floatLiteral = sign.concat(pointfloat).concat(exp)

def floatParser(chars):
  if floatLiteral.match(chars):
    return 0
  else:
    raise SyntaxError('error')
from DFA import parser

'''
Your task is to write a parser that convert a string into a floating point number.
'''
@parser
class Float:
  rules = {
    0: {}
  }
  terminals = ()
  def get(self):
    return 0
  
def makeStateAutomata(rules, terminals, resultGetter):
  '''
  Rules should be indexed and has element at index 0 as initial state.
  For each rule, there should be zero or more tranfer entries, where an
  entry is a key-value pair, the key will be tested against the input 
  char by keyword `in`. And the value is a 2-tuple, where the first one
  is the next state, and the second one is the payload function that will
  be executed when transfer took place.
  
  Terminals should be a iterable containing all the terminating states.
  
  ResultGetter should be a function used for returning value.
  
  Example:
  
    class IntPattern:
      rules = {
        0 : {
          '123456789': (1, addDigit),
        },
        1: {
          '0123456789': (1, addDigit),
        }
      }
      
      terminals = (1,)
      
      def __init__(self):
        self.value = 0
        
      def addDigit(self, char):
        self.value = self.value * 10 + ord(char) - ord('0')
      
      def get(self):
        return self.value
        
    intParser = makeStateAutomata(IntPattern.rules, IntPattern.terminals, IntPattern.get)
    value = intParser(IntPattern(), '309')
                      ^- use an instance to store the parsing result 
  '''
  def startAutomata(obj, chars):
    state = 0
    i = 0
    for i, c in enumerate(chars):
      for rule in rules[state]:
        if c in rule:
          pair = rules[state][rule]
          state = pair[0]
          if pair[1] is not None:
            pair[1](obj, c)
          break
      else:
        state = 0
        break
    else:
      i += 1
    if state not in terminals:
        raise SyntaxError(chars + '\n' + ' '*i + '^')
    return resultGetter(obj)
  return startAutomata

def parser(clz):
  '''A convenient decorator to add a parse functionality to a type
  
  Example:
    @parser
    class IntPattern: # as described above
    
    value = IntPattern.parse('309')
  '''
  if not (hasattr(clz, 'rules') and hasattr(clz, 'terminals') and hasattr(clz, 'get')):
    raise TypeError(
        "type {!r} should has attributes 'rules', 'terminals' and 'get'".format(clz))
  clz.parser = makeStateAutomata(clz.rules, clz.terminals, clz.get)
  clz.parse = lambda chars: clz.parser(clz(), chars)
  return clz