Pythonで字句解析&構文解析

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(字句解析クラス)があることに気づき、
むぎゅーな感じでした。