在语句中添加括号

时间:2012-09-29 15:26:40

标签: python regex string parsing

我在解析字符串并在Python中为其添加括号时遇到问题。我的一个问题是字符串可能以完全括号的方式输入(即(01U(1U0))),也可能根本不输入(即01U1U0)。我似乎无法分开的语法是:

A -> e* | (eUe)
e -> any combination of chars from the grammar

e*的优先级高于U

希望这是有道理的。任何人都有任何想法我如何解析并检查括号?

1 个答案:

答案 0 :(得分:0)

从LL语法编写解析器的最简单方法是每个非终端符号有一个函数。此函数应该使用输入字符串中的字符对应一个规则的减少。下面是与语法

对应的解析器
A -> e+ | '(' A 'U' A ')'
e -> '0' | '1'

这不完全是你想要的语法,但它与你给出的例子更相关。语法是LL(1),你只需要读一个字就知道如何继续。以下解析器为这两个非终端符号定义了两个函数A()和e():

class MySyntaxError(Exception):
  def __init__(self, text, index):
    self.text = text 
    self.index = index
  def __str__(self):
    return "Syntax error at index " + repr(self.index) + ": " + self.text


def parse(input):
  global index
  index = 0

  def eat_char(set):
    global index
    if index < len(input) and input[index] in set:
      index += 1
    else:
      raise MySyntaxError("expected char in " + repr(set), index)

  def e():
    eat_char(['0', '1'])

  def A():
    global index
    if index == len(input): # Successfully parsed
      return
    elif input[index] in ['0', '1']:
      while (input[index] in ['0', '1']):
        e()
    elif input[index] is '(':
      eat_char(['('])
      A()
      eat_char(['U'])
      A()
      eat_char([')'])
    else:
      raise MySyntaxError("expected char '0', '1' or '('", index)

  A()
  if index != len(input): # Parsing didn't reach the end of the string
    raise MySyntaxError("parsing ended before the end of the input", index)


def test(string):
  try:
    parse(string)
    print "The string " + string + " have been successfully parsed"
  except MySyntaxError as e:
    print "The string " + string + " can't be parsed:"
    print e

test("(01U(1U0))")
test("(01U(1U0)") # Missing parenthesis, syntax error
test("(01U(1U0)))") # Too many parenthesis, syntax error
test("01U(1U0)") # No parenthesis, syntax error

注意使用e *而不是e +会将空缩减为A,这会使解析器复杂化。

下推自动机隐藏在解析器中。 o从语法中构建自动机并不是那么简单。这里,PDA只有一个状态。我们可以用这种方式描述自动机:

from state [1]
  read a '0' or a '1' and loop into state [1]
  read a '(', push the parenthesis and loop into state [1]
  read a 'U', when there is an open parenthesis on the top of stack, push the 'U' and loop into state [1]
  read a ')', when there is a 'U' on the top of the stack, pop the 'U', pop the opend parenthesis following, and loop into state[1]

在Python中编写这个自动机有一种简单的方法。通常,您需要一个goto来编写自动机。每个州都绑定一个标签,从一个州进入另一个州是用goto完成的。不幸的是,Python中没有goto。但是,由于你只有一个状态,所以没有必要和一个循环:

def parse(input):
  index = 0
  stack = [];

  def top():
    if len(stack) > 0:
      return stack[len(stack)-1]
    else:
      return None

  while index < len(input):
    if input[index] in ['0', '1']:
      pass
    elif input[index] is '(':
      stack.append('(')
    elif input[index] is 'U' and top() == '(':
      stack.append('U')
    elif input[index] is ')' and top() == 'U':
      stack.pop()
      stack.pop()
    else:
      raise MySyntaxError("Unexpected char '" + input[index] + "'", index)
    index += 1

  if len(stack):
    raise MySyntaxError("unterminated input", index)

这是一个具有显式堆栈的PDA。前一个解析器使用程序堆栈而不是隐式堆栈,并记住递归调用数中的括号读取次数。当您想要检查字符串的有效性时,第二个解析器很有用。但是当你想要生成输入的表示时,它是不方便的,比如抽象语法树:它们通常是从语法构建的,因此更容易从第一个解析器生成。第一个也更容易阅读,因为您不必计算自动机来理解它。