Pythonで字句解析&構文解析
#!/usr/bin/env python # -*- coding: utf-8 -*- import re # テキスト読み込みクラス class LexerReader: def __init__(self, f): self.f = f self.unget = False self.c = '' # 1文字読み込み def read(self): if self.unget: self.unget = False else: self.c = self.f.read(1) return self.c # 1文字戻す def unread(self): self.unget = True # 字句解析クラス class Lexer: TYPE_INT = 257 TYPE_STRING = 258 def __init__(self, lexReader): self.lexReader = lexReader # ファイル読み込みオブジェクト self.token = "" # トークン self.type = 0 # トークンの種類 # 次のトークンに進める def advance(self): self.skipWhiteSpace() c = self.lexReader.read() if c == '': # EOF return False elif c == '/': c = self.lexReader.read() if c == '/': # コメント読み飛ばし c = self.lexReader.read() while True: if c == '\n' or c == '': # 改行かEOF break c = self.lexReader.read() c = self.lexReader.read() self.lexReader.unread() return self.advance() elif c == '#': # コメント読み飛ばし c = self.lexReader.read() while True: if c == '\n' or c == '': # 改行かEOF break c = self.lexReader.read() self.lexReader.unread() return self.advance() elif c == '=': self.type = c elif c == '[': self.type = c elif c == ']': self.type = c elif c == '{': self.type = c elif c == '}': self.type = c elif c == ',': self.type = c elif re.match("[0-9]", c): self.type = self.TYPE_INT self.lexReader.unread() self.token = self.lexDigit() elif re.match("[_a-zA-Z]", c): self.type = self.TYPE_STRING self.lexReader.unread() self.token = self.lexString() else: raise "Error:%s"%c return True def skipWhiteSpace(self): while True: c = self.lexReader.read() if c != ' ' and c != '\t' and c != '\n': self.lexReader.unread() break # 数値の解析 def lexDigit(self): num = 0 while True: c = self.lexReader.read() if not(re.match("[0-9]", c)): break num = num*10 + int(c) self.lexReader.unread() return num # 文字列の解析 def lexString(self): s = "" while True: c = self.lexReader.read() if not(re.match("[_a-zA-Z0-9]", c)): break s += c self.lexReader.unread() return s # 構文解析クラス class Parser: def __init__(self, lexer): self.lexer = lexer self.table = {} def parse(self): key = "" type = self.look_ahead() while not(type is None): if type == self.lexer.TYPE_STRING: key = self.lexer.token elif type == '=': pass elif type == '{': type = self.look_ahead() # skip '{' self.table[key] = self.parseIntArray(type) else: raise "Error: parse() type=%d,token=%s"%(type, self.lexer.token) type = self.look_ahead() return self.table # 先読みする def look_ahead(self): if self.lexer.advance(): return self.lexer.type else: return None # 数値リストを作成する def parseIntArray(self, type): ret = [] while True: if type is Lexer.TYPE_INT: ret.append(self.lexer.token) elif type == ',': pass elif type == '}': break else: raise "Error: parseIntArray() type=%s,token=%s"%(type, self.lexer.token) type = self.look_ahead() return ret def main(): parser = Parser(Lexer(LexerReader(open("enemy.txt")))) print parser.parse() if __name__ == "__main__": main()
字句解析(Lexer)を挟むだけで、だいぶシンプルに書けますねー。
読み込むenemy.txtはこんな感じ。
// 戦闘データ定義 battle1={28, 300, 10, 28, 1, 2} // ボス前半 battle2={28, 750, 11, 20, 1, 2} // ボス後半 battle3={34, 10, 10, 50, 0, 1} // 雑魚 battle4 = { 1234, //hogehoge 2354, //piyopiyo }
こんな感じで読み込めました。Great!
{ 'battle4': [1234, 2354], 'battle1': [28, 300, 10, 28, 1, 2], 'battle3': [34, 10, 10, 50, 0, 1], 'battle2': [28, 750, 11, 20, 1, 2] }
おおー、字句解析便利ー、と思っていたら、
ライブラリにshlex(字句解析クラス)があることに気づき、
むぎゅーな感じでした。