Misaインタプリタ

 こないだ会社でMisaというプログラミング言語を教えてもらいました。といってもBrainfuckの派生言語です。

ちょっと仕事に疲れていたので、息抜きにPythonインタプリタを書いてみました。といっても、あまりに単純なので構文解析と言えるような処理はありません。

書いてて感じたことですが、Brainfuckは言語が単純なだけに、基礎的な原理を理解させる教材として結構よいかも知れません。

  • -vオプションをつけて動かすと、おおよその実行状況が分かります。
  • ファイルの文字コードは、みんなutf8のつもりで動いてます。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, getopt, codecs

EOF = 'eof'
def cmd(ch):
    CMD = [ ['>', '→', '〜', 'ー'],
            ['<', '←', '★', '☆'],
            ['+', 'あ', 'ぁ', 'お', 'ぉ'],
            ['-', 'っ', 'ッ'],
            ['.', '!'],
            [',', '?'],
            ['[', '「', '『'],
            [']', '」', '』'],]
    for clist in CMD:
        for c in clist:
            if c == ch: return clist[0]
    return ' '

class Misa:
    def __init__(self, lines, verbose):
        self.p = 0
        self.mem = 3000*[0]
        self.stack = []
        self.lines = lines
        self.lno = 0
        self.cno = 0
        self.cmd = ''
        self.char = ''
        self.verbose = verbose

    def log(self, s):
        if self.verbose: print s,
    def loglog(self):
        self.log('char%3d:%2s --> %s : [%d]=%d\n' % (self.cno+1, self.char, self.cmd, self.p, self.mem[self.p]))
    def currentline(self):
        if self.lno >= len(self.lines): return ''
        return self.lines[self.lno]
    def is_control_cmd(self):
        return self.cmd == '[' or self.cmd == ']'

    def getc(self):
        if self.cno == 0: self.log("line%3d: %s\n" % (self.lno+1, self.currentline()))
        if self.lno >= len(self.lines):         return EOF
        if self.cno >= len(self.currentline()): return EOF
        return self.lines[self.lno][self.cno]

    def forward(self):
        self.cno = self.cno + 1
        if self.cno >= len(self.currentline()):
            self.lno = self.lno + 1
            self.cno = 0

    def run(self):
        while True:
            self.char = self.getc()
            if self.char == EOF: break
            self.apply(cmd(self.char))

    def apply(self, ch):
      self.cmd = ch
      if self.is_control_cmd(): self.loglog()
      if   ch == '>': self.p = self.p + 1
      elif ch == '<': self.p = self.p - 1
      elif ch == '+': self.mem[self.p] = self.mem[self.p] + 1
      elif ch == '-': self.mem[self.p] = self.mem[self.p] - 1
      elif ch == '.': self.outp()
      elif ch == ',': self.mem[self.p] = ord(sys.stdin.read(1))
      elif ch == '[': self.beginloop()
      elif ch == ']': (self.lno, self.cno) = self.stack[len(self.stack)-1]
      if not self.is_control_cmd(): self.loglog()
      self.forward()

    def jump2loopend(self):
        jump = 1
        c = ''
        while jump != 0 and c != EOF:
            c = cmd(self.getc())
            if   c == '[': jump = jump + 1
            elif c == ']': jump = jump - 1
            self.forward()
        del self.stack[len(self.stack)-1]

    def outp(self):
        self.log('output :')
        sys.stdout.write('%c' % self.mem[self.p])
        self.log('\n')

    def beginloop(self):
        if self.mem[self.p] != 0:
            self.stack.append((self.lno, self.cno-1))
        else:
            self.forward()
            self.jump2loopend()

if __name__ == "__main__":
    try:
        optlist, args = getopt.getopt(sys.argv[1:], 'v')
        if len(args) == 1:
            Misa(codecs.open(args[0], 'r', 'utf8').readlines(), len(optlist) > 0).run();
        else:
            print "Usage: misa.py [-v] filename"
    except Exception, e:
        print e