2012年6月1日星期五

那啥简单的Sexp解析

* 简单的Sexp解析

嘛,没啥东西可贴,就找找以前有没有什么好玩的东西出来吧 >_<
这个就是简单地把那种lisp括号表达式在Python中解析成字符串组成的元组。
能解析的格式被简化的很厉害,不仅是为了实现简单,也是确实让代码可以更简单一些。
用的LL的递归下降对字符解析,考虑到Python3有流IO,用那个会更好一些。
#! /usr/bin/env python26
# coding: utf-8

class SSexpr:
    """
    A Simple Simplified Sexpr Reader.
    @Author ee.zsy
    @Date Mar.2012
    type ssexpr = Symbol of string | Block of ssexpr list;
    function parse : string - > ssexpr;
    >>> parse(r'(1 2 "3\" 4"( 1 2 3))')
    ['1', '2', '3" 4', ['1', '2', '3']]
    Notice that Python evaluates expressions from left to right.
    """
    w = " \n\t"
    e = " \n\t()[]"
    b = (lambda i:lambda s,x:i(x).pop())(set(')]').difference)
    m = 'Brackets or Parentheses do not match.'
    a = dict(zip('\"\'nt\\','\"\'\n\t\\'))
    f = 'Escape character is not recognized.'
    def __init__(s,code):
        s.s, s.p = code, 0
    def read(s):
        try:return s.exp()
        except RuntimeError as e:raise SyntaxError(e)
    @classmethod
    def parse(c,code):
        return c(code).read()
    def peek(s):
        return s.s[s.p] if s.p<len(s.s) else None
    def eat(s):
        s.p += 1;return s.s[s.p-1]
    def exp(s,cm=0):
        c = s.peek()
        if c is None:return None
        elif c in s.w:return s.eat() and s.exp()
        elif c == '(':return s.eat() and s.blk(')')
        elif c == '[':return s.eat() and s.blk(']')
        elif c == '"':return s.eat() and s.str()
        else:return s.sym()
    def sym(s):
        c = s.peek()
        if c is None or c in s.e:return ""
        else:return s.eat() and c+s.sym()
    def str(s):
        c = s.peek()
        if c is None:raise EOFError
        elif c == '"':return s.eat() and ''
        elif c =='\\':return s.eat() and s.esc(s.eat())+s.str()
        else:return s.eat() and c+s.str()
    def esc(s,c):
        if c in s.a:return s.a[c]
        else:raise SyntaxError(s.f)
    def blk(s,end):
        c = s.peek()
        if c is None:raise EOFError
        elif c == s.b(end):raise SyntaxError(s.m)
        elif c in s.w:return s.eat() and s.blk(end)
        elif c == end:return s.eat() and tuple()
        else:return tuple([s.exp()])+s.blk(end)
        
if 1:
    assert SSexpr("""123 12""").read() == '123'
    assert SSexpr("""(1 2 3 4(1 2 3))""").read() == ('1', '2', '3', '4', ('1', '2', '3'))
    assert SSexpr("""(1 2 "3 4"( 1 2 3))""").read() == ('1', '2', '3 4', ('1', '2', '3'))
    assert SSexpr.parse(r"""(1 2 "3\" 4"( 1 2 3))""") == ('1', '2', '3" 4', ('1', '2', '3'))
    assert SSexpr.parse(r"""[]""") == tuple()

if __name__ == '__main__':
    print SSexpr("""[hello world]""").read()
    while 1:
        print SSexpr(raw_input()).read()



再往前的一个功能略微多一点点的版本,有单独的正则表达式实现的词法解析部分。

#@Date Nov.27,2011
#@Author ee.zsy
import re
pattern = r'''
 (?P<lpr>\()
|(?P<rpr>\))
|(?P<qte>\')
|(?P<qqt>`)
|(?P<uqs>,@)
|(?P<uqt>,)
|(?P<blt>\#[tT])
|(?P<blf>\#[fF])
|(?P<chr>\#\\.[^ \)\t\n\r]*)
|(?P<shr>\#)
|(?P<num>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)
|(?P<str>\"(\\.|[^"])*\")
|(?P<wht>([ \t\n\r]+)|(;.*?[\n\r]))
|(?P<sym>[^ ;\)\(\t\n\r]+)
'''
token = re.compile(pattern,re.S|re.X)
def peekToken(text,start=0):
    regex = token.match(text,start)
    if regex:
        tag = [(k,v) for k,v in regex.groupdict().items() if v]
        return tag[0],regex.end()
    return None,None


然后余下的语法解析还是一样的了,只是情况相比要多一些。
不过代码代码不像这里这样简单的看的那么清爽,这里就不贴了。
虽然这里的东西都故意做了简化,但是改动之后倒是可以做表达式计算或者类AWK的解析什么的。
手写LL(1)的时候,有些左连接的操作符,要把循环的部分单独写一条规则出来。
比如像“num "+" num {"+" num}”这样的,要保证可以只根据下一个未处理的Token作出分支。
这里的这种情况有个专门的名词叫做什么什么来着的,囧死了人家都不知道了。

话说内部接口封装不够不可靠都很讨厌。


* Stack machine

这里先假设操作符都是二元的,那么最简单的一个前缀转后缀可以这么实现。
(define e '(+ (* 3 (- 2 3)) (+ 2 3)))
(define (t e) (if (pair? e) (let ((x (map t e))) (apply append (append (cdr x) (list (car x))))) (list e)))
(display (t e))

那么接下来,那么接下来就以后再写吧。