92 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			92 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from tango.value import TangoIdent, TangoExpr
 | |
| 
 | |
| PAREN_OPEN = '('
 | |
| PAREN_CLOSE = ')'
 | |
| DOUBLE_QUOTE = '"'
 | |
| WHITESPACE = ' \t\n\r'
 | |
| 
 | |
| 
 | |
| class StringReader:
 | |
|     def __init__(self, s):
 | |
|         self._src = s
 | |
|         self._len = len(s)
 | |
|         self._cur = -1
 | |
| 
 | |
|     def next(self):
 | |
|         if self._len == self._cur + 1:
 | |
|             return None
 | |
|         else:
 | |
|             self._cur += 1
 | |
|             return self._src[self._cur]
 | |
| 
 | |
| 
 | |
| class TangoParser:
 | |
|     def __init__(self, src):
 | |
|         self._cur = None
 | |
|         self._src = src
 | |
|         self._next()
 | |
| 
 | |
|     def parse(self):
 | |
|         while self._cur is not None:
 | |
|             self._skip_whitespace()
 | |
|             if self._cur is PAREN_OPEN:
 | |
|                 return self._parse_expr()
 | |
|             elif self._cur is DOUBLE_QUOTE:
 | |
|                 return self._parse_str()
 | |
|             elif self._cur.isalpha():
 | |
|                 return self._parse_ident()
 | |
|             elif self._cur.isnumeric():
 | |
|                 return self._parse_int()
 | |
|             else:
 | |
|                 self._raise_invalid_char()
 | |
| 
 | |
|     def _parse_expr(self):
 | |
|         expr = TangoExpr()
 | |
|         self._next()
 | |
|         while self._cur is not None:
 | |
|             self._skip_whitespace()
 | |
|             if self._cur is PAREN_CLOSE:
 | |
|                 self._next()
 | |
|                 return expr
 | |
|             else:
 | |
|                 expr.append(self.parse())
 | |
| 
 | |
|         self._raise_unexpected_end()
 | |
| 
 | |
|     def _parse_ident(self):
 | |
|         ident = self._read_until(WHITESPACE + PAREN_CLOSE)
 | |
|         return TangoIdent(ident)
 | |
| 
 | |
|     def _parse_str(self):
 | |
|         self._next()
 | |
|         s = self._read_until(DOUBLE_QUOTE)
 | |
|         self._next()
 | |
|         return s
 | |
| 
 | |
|     def _parse_int(self):
 | |
|         s = self._read_until(WHITESPACE + PAREN_CLOSE)
 | |
|         return int(s)
 | |
| 
 | |
|     def _read_until(self, delims):
 | |
|         s = ''
 | |
| 
 | |
|         while self._cur is not None and self._cur not in delims:
 | |
|             s += self._cur
 | |
|             self._next()
 | |
| 
 | |
|         return s
 | |
| 
 | |
|     def _next(self):
 | |
|         self._cur = self._src.next()
 | |
|         return self._cur
 | |
| 
 | |
|     def _raise_unexpected_end(self):
 | |
|         raise ValueError('Unexpected end of stream')
 | |
| 
 | |
|     def _raise_invalid_char(self):
 | |
|         raise ValueError('Invalid chr: ' + self._cur)
 | |
| 
 | |
|     def _skip_whitespace(self):
 | |
|         if self._cur in WHITESPACE:
 | |
|             while self._next() in WHITESPACE:
 | |
|                 continue |