PageRenderTime 17ms CodeModel.GetById 8ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/lib/python/indra/ipc/tokenstream.py

https://bitbucket.org/lindenlab/viewer-beta/
Python | 154 lines | 145 code | 0 blank | 9 comment | 1 complexity | 8ee49bc6f5c93c16556394f4092147b2 MD5 | raw file
  1"""\
  2@file tokenstream.py
  3@brief Message template parsing utility class
  4
  5$LicenseInfo:firstyear=2007&license=mit$
  6
  7Copyright (c) 2007-2009, Linden Research, Inc.
  8
  9Permission is hereby granted, free of charge, to any person obtaining a copy
 10of this software and associated documentation files (the "Software"), to deal
 11in the Software without restriction, including without limitation the rights
 12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 13copies of the Software, and to permit persons to whom the Software is
 14furnished to do so, subject to the following conditions:
 15
 16The above copyright notice and this permission notice shall be included in
 17all copies or substantial portions of the Software.
 18
 19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 25THE SOFTWARE.
 26$/LicenseInfo$
 27"""
 28
 29import re
 30
 31class _EOF(object):
 32    pass
 33
 34EOF = _EOF()
 35
 36class _LineMarker(int):
 37    pass
 38    
 39_commentRE = re.compile(r'//.*')
 40_symbolRE = re.compile(r'[a-zA-Z_][a-zA-Z_0-9]*')
 41_integerRE = re.compile(r'(0x[0-9A-Fa-f]+|0\d*|[1-9]\d*)')
 42_floatRE = re.compile(r'\d+(\.\d*)?')
 43
 44
 45class ParseError(Exception):
 46    def __init__(self, stream, reason):
 47        self.line = stream.line
 48        self.context = stream._context()
 49        self.reason = reason
 50
 51    def _contextString(self):    
 52        c = [ ]
 53        for t in self.context:
 54            if isinstance(t, _LineMarker):
 55                break
 56            c.append(t)
 57        return " ".join(c)
 58
 59    def __str__(self):
 60        return "line %d: %s @ ... %s" % (
 61            self.line, self.reason, self._contextString())
 62
 63    def __nonzero__(self):
 64        return False
 65
 66
 67def _optionText(options):
 68    n = len(options)
 69    if n == 1:
 70        return '"%s"' % options[0]
 71    return '"' + '", "'.join(options[0:(n-1)]) + '" or "' + options[-1] + '"'
 72
 73
 74class TokenStream(object):
 75    def __init__(self):
 76        self.line = 0
 77        self.tokens = [ ]
 78    
 79    def fromString(self, string):
 80        return self.fromLines(string.split('\n'))
 81        
 82    def fromFile(self, file):
 83        return self.fromLines(file)
 84
 85    def fromLines(self, lines):
 86        i = 0
 87        for line in lines:
 88            i += 1
 89            self.tokens.append(_LineMarker(i))
 90            self.tokens.extend(_commentRE.sub(" ", line).split())
 91        self._consumeLines()
 92        return self
 93    
 94    def consume(self):
 95        if not self.tokens:
 96            return EOF
 97        t = self.tokens.pop(0)
 98        self._consumeLines()
 99        return t
100    
101    def _consumeLines(self):
102        while self.tokens and isinstance(self.tokens[0], _LineMarker):
103            self.line = self.tokens.pop(0)
104    
105    def peek(self):
106        if not self.tokens:
107            return EOF
108        return self.tokens[0]
109            
110    def want(self, t):
111        if t == self.peek():
112            return self.consume()
113        return ParseError(self, 'expected "%s"' % t)
114
115    def wantOneOf(self, options):
116        assert len(options)
117        if self.peek() in options:
118            return self.consume()
119        return ParseError(self, 'expected one of %s' % _optionText(options))
120
121    def wantEOF(self):
122        return self.want(EOF)
123        
124    def wantRE(self, re, message=None):
125        t = self.peek()
126        if t != EOF:
127            m = re.match(t)
128            if m and m.end() == len(t):
129                return self.consume()
130        if not message:
131            message = "expected match for r'%s'" % re.pattern
132        return ParseError(self, message)
133    
134    def wantSymbol(self):
135        return self.wantRE(_symbolRE, "expected symbol")
136    
137    def wantInteger(self):
138        return self.wantRE(_integerRE, "expected integer")
139    
140    def wantFloat(self):
141        return self.wantRE(_floatRE, "expected float")
142    
143    def _context(self):
144        n = min(5, len(self.tokens))
145        return self.tokens[0:n]
146
147    def require(self, t):
148        if t:
149            return t
150        if isinstance(t, ParseError):
151            raise t
152        else:
153            raise ParseError(self, "unmet requirement")
154