Build a simple interpreter --Part 3


  Let's continue learning from the previous part.Graphically,the arithmetic expression can be represented with the following syntax diagram:

              

   A syntax diagram is a graphical represation of a programming language's syntax rules.Basically,a syntax diagram visually show you which statements are allowed in your programming language and which are not.Syntax diagrams sever two main purposes:

      1.They graphically responsent the specification(grammar) of a programming language.

      2.It can be used to help us write our parser - we can map a diagram to code by following simple rules.

  We have known that the process of recognizing a phrase in the stream of tokens is called parsing and the part of an interpreter or a compiler that performs that job is called a parser. Parsing is also called syntax analysis, and the parser is also aptly called a syntax analyzer.

  The blog adopted the creatation of a new method to parse an integer and named it term:

def term(self):
  """Return an INTEGER token value""" token = self.current_token self.eat(INTEGER) return token.value def expr(self): """Parser / Interpreter """ # set current token to the first token taken from the input self.current_token = self.get_next_token() result = self.term() while self.current_token.type in (PLUS, MINUS): token = self.current_token if token.type == PLUS: self.eat(PLUS) result = result + self.term() elif token.type == MINUS: self.eat(MINUS) result = result - self.term() return result

  The following code is modified on the basis of original blog code.It allows inputting multiple numbers and performing addition,subtraction,multiplication and division operation:

  1 INTEGER , PLUS , MINUS , MULTI , DIV , EOF = 'INTEGER','PLUS','MINUS','MULTI','DIV','EOF'
  2 
  3 class Token(object):
  4     def __init__(self,type,value):
  5         self.type = type
  6         self.value = value
  7 
  8     def __str__(self):
  9         return 'Token({type},{value})'.format(
 10             type = self.type,
 11             value = self.value
 12         )
 13 
 14     def __repr__(self):
 15         return self.__str__()
 16 
 17 class Interpreter(object):
 18     def __init__(self,text):
 19         self.text = text
 20         self.pos = 0
 21         self.current_token = None
 22         self.current_char = self.text[self.pos]
 23 
 24     def error(self):
 25         raise Exception('Invalid syntax')
 26 
 27     def advance(self):
 28         self.pos += 1
 29         if self.pos > len(self.text) - 1:
 30             self.current_char = None
 31         else:
 32             self.current_char = self.text[self.pos]
 33 
 34     def skip_whitespace(self):
 35         while self.current_char is not None and self.current_char.isspace():
 36             self.advance()
 37 
 38     def integer(self):
 39         result = ''
 40         while self.current_char is not None and self.current_char.isdigit():
 41             result += self.current_char
 42             self.advance()
 43         return int(result)
 44 
 45     def get_next_token(self):
 46         while self.current_char is not None:
 47             if self.current_char.isspace():
 48                 self.skip_whitespace()
 49                 continue
 50             elif self.current_char.isdigit():
 51                 return Token(INTEGER,self.integer())
 52             elif self.current_char == '+':
 53                 self.advance()
 54                 return Token(PLUS,'+')
 55             elif self.current_char == '-':
 56                 self.advance()
 57                 return Token(MINUS,'-')
 58             elif self.current_char == '*':
 59                 self.advance()
 60                 return Token(MULTI,'*')
 61             elif self.current_char == '/':
 62                 self.advance()
 63                 return Token(DIV,'/')
 64             self.error()
 65         return Token(EOF,'EOF')
 66 
 67     def eat(self,token_type):
 68         if self.current_token.type == token_type:
 69             self.current_token = self.get_next_token()
 70         else:
 71             self.error()
 72 
 73     def term(self):
 74         """
 75         :return: an INTEGER value
 76         """
 77         token = self.current_token
 78         self.eat(INTEGER)
 79         return token.value
 80 
 81     def expr(self):
 82         self.current_token = self.get_next_token()
 83         result = self.term()
 84         while self.current_token.type in(PLUS,MINUS,MULTI,DIV):
 85             token = self.current_token
 86             if token.type == PLUS:
 87                 self.eat(PLUS)
 88                 result += self.term()
 89             elif token.type == MINUS:
 90                 self.eat(MINUS)
 91                 result -= self.term()
 92             elif token.type == MULTI:
 93                 self.eat(MULTI)
 94                 result *= self.term()
 95             elif token.type == DIV:
 96                 self.eat(DIV)
 97                 result /= self.term()
 98             else:
 99                 self.error()
100         return result
101 
102 def main():
103     while True:
104         try:
105             text = input('calc>')
106         except EOFError:
107             break
108         if not text:
109             continue
110         interpreter = Interpreter(text)
111         result = interpreter.expr()
112         print(result)
113 
114 if __name__ == '__main__':
115     main()