プログラミングと工作と

PythonとかPascalとかAVRマイコンとか、コンパイラつくったり電子工作なんかを楽しんでいるおっさんの記録

tokenクラスの変更

さて、Ver1の変更ですが、一番簡単そうなtokenクラスから始めます。

tokenの変更は、昨日書いたように

  • キーワード mod, div, not, and, or の追加
  • 新トークン :=, *, <>, <=, >=, <, >, ), ( の追加
  • 上記の追加を token.next()メソッドへ反映させる

 
という点です。

キーワード、新トークン(新記号)は、 __init__ へ追加してやるだけです。

      self.spechar = [':', ',', ';', '.','=', '+', '-', '*', '<', '>', '(', ')']
      self.keyword = ['program','const','var','int','integer','bool','boolean',
                      'begin','end','true','false','mod','div','not','and','or']
                      

token.next()メソッドは、2つの記号の組み合わせ「:=, <>, <=, >=」をトークンとして認識させるように変更を加えます。

記号を認識させる部分

            elif self.char in self.spechar:  #「記号」の処理
                self.token = self.char
                self.next_char()

を、以下のように変更します。

            elif self.char in self.spechar:  #「記号」の処理
                self.token = self.char
                self.next_char()
                if self.token == ':':    #代入記号 ':=' の処理
                    if self.char == '=':
                        self.token = self.token + self.char
                        self.next_char()
                if self.token == '<':    #記号 '<>', '<=' の処理
                    if self.char in ['>', '=']:
                        self.token = self.token + self.char
                        self.next_char()
                if self.token == '>':    #記号 '>=' の処理
                    if self.char == '=':
                        self.token = self.token + self.char
                        self.next_char()
                        
2つの記号の組み合わせ部分を追加しただけです。
ちょっと長ったらしいですが、トークンクラスの全てを以下に載せます。

#!/usr/bin/python
# coding: utf-8

import sys

class tkn:             
    
    def __init__(self, infile, e):
      self.infile = infile
      self.e = e
      self.token = ''
      self.char = ''
      self.digit = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
      self.letter = ['A','B','C','D','E','F','G','H','I','J','K','L','M',
                     'N','O','P','Q','R','S','T','U','V','W','X','Y','Z','_',
                     'a','b','c','d','e','f','g','h','i','j','k','l','m',
                     'n','o','p','q','r','s','t','u','v','w','x','y','z']
      self.spechar = [':', ',', ';', '.','=', '+', '-', '*', '<', '>', '(', ')']
      self.keyword = ['program','const','var','int','integer','bool','boolean',
                      'begin','end','true','false','mod','div','not','and','or']


    def next_char(self):  
        self.char = self.infile.read(1)
        if not self.char:   #EOFなら(file.read()は、EOFを検出すると空を返す)
            self.char = 'end_of_file'
            return self.char
        else:
            #sys.stdout.write(self.char)
            return self.char


    def next(self):
        self.token = ''
        while self.token == '':
            if self.char == '{':                    #トークンが{の場合
                while self.char != '}':             # }まで読み飛ばす(コメント処理)
                    if self.char == 'end_of_file':  #コメントの途中でEOFになったら
                        self.e.error(1)               #エラーを出す
                    else:
                        self.next_char()
                self.next_char()
            elif self.char == '}':  #トークンの頭に}は来ないので
                self.e.error(2)       #エラーを出す
            elif self.char == '#':  # #が来たら改行まで読み飛ばす(一行コメント処理)
                while self.char != '\n':
                    if self.char == 'end_of_file':
                        self.e.error(1)
                    else:
                        self.next_char()
            elif self.char in self.spechar:  #「記号」の処理
                self.token = self.char
                self.next_char()
                if self.token == ':':    #代入記号 ':=' の処理
                    if self.char == '=':
                        self.token = self.token + self.char
                        self.next_char()
                if self.token == '<':    #記号 '<>', '<=' の処理
                    if self.char in ['>', '=']:
                        self.token = self.token + self.char
                        self.next_char()
                if self.token == '>':    #記号 '>=' の処理
                    if self.char == '=':
                        self.token = self.token + self.char
                        self.next_char()
            elif self.char in self.letter:   #文字列の処理
                self.token = self.char
                self.next_char()
                while self.char in (self.letter + self.digit):
                    self.token = self.token + self.char
                    self.next_char()
            elif self.char in self.digit:  #数列の処理
                self.token = self.char
                self.next_char()
                while self.char in self.digit:
                    self.token = self.token + self.char
                    self.next_char()
            elif self.char in [' ', '\n', '\t', '\r']:  #スペース,改行,タブ,リターンは
                self.next_char()                        #読み飛ばす
            elif self.char == 'end_of_file': #EOFの処理
                self.token = 'end_of_file'
            else:
                self.e.error(3)
        #print(self.token)
        return self.token


さて、正常に動作してくれるか確認します。
確認用の以下のソースプログラムファイル test.p を作成しました。

program test1;
const
    zero = 0;
    five = 5;
var
    a     : integer;
    d,e,f : bool;
begin
    a := five * (3 + 34);
    d := a >= five;
    e := zero <> 1;
    f := five <= 10;
end.

内容は無いのですが、トークンを認識できるかが目的なので気にしないで下さい。

これを読ませるために、以下のテストファイル test.py を作成します。

#!/usr/bin/python
# coding: utf-8

from err import *
from tkn import *

infile = open('test.p', 'r')
e = err()
token = tkn(infile, e)
token.next_char()
token.next()

while token.token != 'end_of_file':
    print(token.token)
    token.next()

infile.close()

トークンを読んで、ひとつづつ表示させているだけです。
これを実行させたのが以下になります。

~/PythonProject/PasAvr$ python test.py
program
test1
;
const
zero
=
0
;
five
=
5
;
var
a
:
integer
;
d
,
e
,
f
:
bool
;
begin
a
:=
five
*
(
3
+
34
)
;
d
:=
a
>=
five
;
e
:=
zero
<>
1
;
f
:=
five
<=
10
;
end
.
~/PythonProject/PasAvr$

はい、ちゃんとトークンを認識できています。
今回は無問題ですんなり動いてくれました。
めずらしいことですw