From f43c5238c22c0f1b1d1366b981631774d3e2da46 Mon Sep 17 00:00:00 2001 From: Hamled Date: Sat, 16 Jan 2010 20:21:28 -0800 Subject: [PATCH] Fix twitter plugin replied-to feature. --- plugins_available/babel.py | 93 + plugins_available/bf.py | 87 + plugins_available/bible.py | 27 + plugins_available/choose.py | 19 + plugins_available/dice.py | 59 + plugins_available/dotnetpad.py | 96 + plugins_available/down.py | 27 + plugins_available/explain.py | 15 + plugins_available/google.py | 59 + plugins_available/goonsay.py | 8 + plugins_available/hash.py | 20 + plugins_available/help.py | 17 + plugins_available/log.py | 108 + plugins_available/misc.py | 30 + plugins_available/mtg.py | 170 + plugins_available/profile.py | 13 + plugins_available/pycparser/__init__.py | 0 plugins_available/pycparser/cdecl.py | 98 + plugins_available/pycparser/lextab.py | 9 + .../pycparser/pycparser/__init__.py | 75 + .../pycparser/pycparser/_ast_gen.py | 249 ++ .../pycparser/pycparser/_build_tables.py | 31 + .../pycparser/pycparser/_c_ast.yaml | 164 + .../pycparser/pycparser/c_ast.py | 1163 ++++++ .../pycparser/pycparser/c_lexer.py | 443 +++ .../pycparser/pycparser/c_parser.py | 1259 +++++++ .../pycparser/pycparser/ply/__init__.py | 4 + .../pycparser/pycparser/ply/cpp.py | 898 +++++ .../pycparser/pycparser/ply/ctokens.py | 133 + .../pycparser/pycparser/ply/lex.py | 1021 ++++++ .../pycparser/pycparser/ply/yacc.py | 3165 +++++++++++++++++ .../pycparser/pycparser/plyparser.py | 67 + plugins_available/pycparser/yacctab.py | 250 ++ plugins_available/pyexec.py | 25 + plugins_available/remember.py | 89 + plugins_available/seen.py | 77 + plugins_available/sieve.py | 29 + plugins_available/suggest.py | 36 + plugins_available/tell.py | 131 + plugins_available/tinyurl.py | 18 + plugins_available/twitter.py | 137 + plugins_available/urbandictionary.py | 29 + plugins_available/util/__init__.py | 0 plugins_available/util/hook.py | 94 + plugins_available/util/timesince.py | 96 + plugins_available/util/yaml/LICENSE | 19 + plugins_available/util/yaml/PKG-INFO | 36 + plugins_available/util/yaml/README | 35 + plugins_available/util/yaml/__init__.py | 288 ++ plugins_available/util/yaml/composer.py | 139 + plugins_available/util/yaml/constructor.py | 684 ++++ plugins_available/util/yaml/cyaml.py | 85 + plugins_available/util/yaml/dumper.py | 62 + plugins_available/util/yaml/emitter.py | 1135 ++++++ plugins_available/util/yaml/error.py | 75 + plugins_available/util/yaml/events.py | 86 + plugins_available/util/yaml/loader.py | 40 + plugins_available/util/yaml/nodes.py | 49 + plugins_available/util/yaml/parser.py | 584 +++ plugins_available/util/yaml/reader.py | 225 ++ plugins_available/util/yaml/representer.py | 489 +++ plugins_available/util/yaml/resolver.py | 224 ++ plugins_available/util/yaml/scanner.py | 1457 ++++++++ plugins_available/util/yaml/serializer.py | 111 + plugins_available/util/yaml/tokens.py | 104 + plugins_available/weather.py | 72 + plugins_available/wikipedia.py | 57 + plugins_available/youtube.py | 41 + 68 files changed, 16735 insertions(+) create mode 100644 plugins_available/babel.py create mode 100644 plugins_available/bf.py create mode 100644 plugins_available/bible.py create mode 100644 plugins_available/choose.py create mode 100644 plugins_available/dice.py create mode 100644 plugins_available/dotnetpad.py create mode 100644 plugins_available/down.py create mode 100755 plugins_available/explain.py create mode 100644 plugins_available/google.py create mode 100644 plugins_available/goonsay.py create mode 100644 plugins_available/hash.py create mode 100644 plugins_available/help.py create mode 100644 plugins_available/log.py create mode 100644 plugins_available/misc.py create mode 100644 plugins_available/mtg.py create mode 100644 plugins_available/profile.py create mode 100644 plugins_available/pycparser/__init__.py create mode 100644 plugins_available/pycparser/cdecl.py create mode 100644 plugins_available/pycparser/lextab.py create mode 100644 plugins_available/pycparser/pycparser/__init__.py create mode 100644 plugins_available/pycparser/pycparser/_ast_gen.py create mode 100644 plugins_available/pycparser/pycparser/_build_tables.py create mode 100644 plugins_available/pycparser/pycparser/_c_ast.yaml create mode 100644 plugins_available/pycparser/pycparser/c_ast.py create mode 100644 plugins_available/pycparser/pycparser/c_lexer.py create mode 100644 plugins_available/pycparser/pycparser/c_parser.py create mode 100644 plugins_available/pycparser/pycparser/ply/__init__.py create mode 100644 plugins_available/pycparser/pycparser/ply/cpp.py create mode 100644 plugins_available/pycparser/pycparser/ply/ctokens.py create mode 100644 plugins_available/pycparser/pycparser/ply/lex.py create mode 100644 plugins_available/pycparser/pycparser/ply/yacc.py create mode 100644 plugins_available/pycparser/pycparser/plyparser.py create mode 100644 plugins_available/pycparser/yacctab.py create mode 100644 plugins_available/pyexec.py create mode 100644 plugins_available/remember.py create mode 100644 plugins_available/seen.py create mode 100644 plugins_available/sieve.py create mode 100644 plugins_available/suggest.py create mode 100644 plugins_available/tell.py create mode 100644 plugins_available/tinyurl.py create mode 100644 plugins_available/twitter.py create mode 100644 plugins_available/urbandictionary.py create mode 100644 plugins_available/util/__init__.py create mode 100644 plugins_available/util/hook.py create mode 100644 plugins_available/util/timesince.py create mode 100644 plugins_available/util/yaml/LICENSE create mode 100644 plugins_available/util/yaml/PKG-INFO create mode 100644 plugins_available/util/yaml/README create mode 100644 plugins_available/util/yaml/__init__.py create mode 100644 plugins_available/util/yaml/composer.py create mode 100644 plugins_available/util/yaml/constructor.py create mode 100644 plugins_available/util/yaml/cyaml.py create mode 100644 plugins_available/util/yaml/dumper.py create mode 100644 plugins_available/util/yaml/emitter.py create mode 100644 plugins_available/util/yaml/error.py create mode 100644 plugins_available/util/yaml/events.py create mode 100644 plugins_available/util/yaml/loader.py create mode 100644 plugins_available/util/yaml/nodes.py create mode 100644 plugins_available/util/yaml/parser.py create mode 100644 plugins_available/util/yaml/reader.py create mode 100644 plugins_available/util/yaml/representer.py create mode 100644 plugins_available/util/yaml/resolver.py create mode 100644 plugins_available/util/yaml/scanner.py create mode 100644 plugins_available/util/yaml/serializer.py create mode 100644 plugins_available/util/yaml/tokens.py create mode 100644 plugins_available/weather.py create mode 100644 plugins_available/wikipedia.py create mode 100644 plugins_available/youtube.py diff --git a/plugins_available/babel.py b/plugins_available/babel.py new file mode 100644 index 0000000..f12c3a6 --- /dev/null +++ b/plugins_available/babel.py @@ -0,0 +1,93 @@ +import urllib +import htmlentitydefs +import re +import json + +from util import hook + +########### from http://effbot.org/zone/re-sub.htm#unescape-html ############# + +def unescape(text): + + def fixup(m): + text = m.group(0) + if text[:2] == "&#": + # character reference + try: + if text[:3] == "&#x": + return unichr(int(text[3:-1], 16)) + else: + return unichr(int(text[2:-1])) + except ValueError: + pass + else: + # named entity + try: + text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) + except KeyError: + pass + return text # leave as is + + return re.sub("&#?\w+;", fixup, text) + +############################################################################## + +languages = 'ja fr de ko ru zh'.split() +language_pairs = zip(languages[:-1], languages[1:]) + + +def goog_trans(text, slang, tlang): + req_url = 'http://ajax.googleapis.com/ajax/services/language/translate' \ + '?v=1.0&q=%s&langpair=%s' + url = req_url % (urllib.quote(text, safe=''), slang + '%7C' + tlang) + page = urllib.urlopen(url).read() + parsed = json.loads(page) + if not 200 <= parsed['responseStatus'] < 300: + raise IOError('error with the translation server: %d: %s' % ( + parsed['responseStatus'], '')) + return unescape(parsed['responseData']['translatedText']) + + +def babel_gen(inp): + for language in languages: + inp = inp.encode('utf8') + trans = goog_trans(inp, 'en', language).encode('utf8') + inp = goog_trans(trans, language, 'en') + yield language, trans, inp + + +@hook.command +def babel(inp): + ".babel -- translates through multiple languages" + + if not inp: + return babel.__doc__ + + try: + return list(babel_gen(inp))[-1][2] + except IOError, e: + return e + + +@hook.command +def babelext(inp): + ".babelext -- like .babel, but with more detailed output" + + if not inp: + return babelext.__doc__ + + try: + babels = list(babel_gen(inp)) + except IOError, e: + return e + + out = u'' + for lang, trans, text in babels: + out += '%s:"%s", ' % (lang, text.decode('utf8')) + + out += 'en:"' + babels[-1][2].decode('utf8') + '"' + + if len(out) > 300: + out = out[:150] + ' ... ' + out[-150:] + + return out diff --git a/plugins_available/bf.py b/plugins_available/bf.py new file mode 100644 index 0000000..610fc19 --- /dev/null +++ b/plugins_available/bf.py @@ -0,0 +1,87 @@ +'''brainfuck interpreter adapted from (public domain) code at +http://brainfuck.sourceforge.net/brain.py''' + +import re +import random + +from util import hook + + +BUFFER_SIZE = 5000 +MAX_STEPS = 1000000 + + +@hook.command +def bf(inp): + ".bf -- executes brainfuck program """ + + if not inp: + return bf.__doc__ + + program = re.sub('[^][<>+-.,]', '', inp) + + # create a dict of brackets pairs, for speed later on + brackets={} + open_brackets=[] + for pos in range(len(program)): + if program[pos] == '[': + open_brackets.append(pos) + elif program[pos] == ']': + if len(open_brackets) > 0: + brackets[pos] = open_brackets[-1] + brackets[open_brackets[-1]] = pos + open_brackets.pop() + else: + return 'unbalanced brackets' + if len(open_brackets) != 0: + return 'unbalanced brackets' + + # now we can start interpreting + ip = 0 # instruction pointer + mp = 0 # memory pointer + steps = 0 + memory = [0] * BUFFER_SIZE #initial memory area + rightmost = 0 + output = "" #we'll save the output here + + # the main program loop: + while ip < len(program): + c = program[ip] + if c == '+': + memory[mp] = memory[mp] + 1 % 256 + elif c == '-': + memory[mp] = memory[mp] - 1 % 256 + elif c == '>': + mp += 1 + if mp > rightmost: + rightmost = mp + if mp >= len(memory): + # no restriction on memory growth! + memory.extend([0]*BUFFER_SIZE) + elif c == '<': + mp = mp - 1 % len(memory) + elif c == '.': + output += chr(memory[mp]) + if len(output) > 500: + break + elif c == ',': + memory[mp] = random.randint(1, 255) + elif c == '[': + if memory[mp] == 0: + ip = brackets[ip] + elif c == ']': + if memory[mp] != 0: + ip = brackets[ip] + + ip += 1 + steps += 1 + if steps > MAX_STEPS: + output += "Maximum number of steps exceeded" + break + + output = '/'.join(output.splitlines()) + + if output == '': + return 'no output' + + return unicode(output, 'iso-8859-1')[:430] diff --git a/plugins_available/bible.py b/plugins_available/bible.py new file mode 100644 index 0000000..bc41a30 --- /dev/null +++ b/plugins_available/bible.py @@ -0,0 +1,27 @@ +import urllib + +from util import hook + + +@hook.command('god') +@hook.command +def bible(inp): + ".bible -- gets from the Bible (ESV)" + + if not inp: + return bible.__doc__ + + base_url = 'http://www.esvapi.org/v2/rest/passageQuery?key=IP&' \ + 'output-format=plain-text&include-heading-horizontal-lines&' \ + 'include-headings=false&include-passage-horizontal-lines=false&' \ + 'include-passage-references=false&include-short-copyright=false&' \ + 'include-footnotes=false&line-length=0&passage=' + + text = urllib.urlopen(base_url + urllib.quote(inp)).read() + + text = ' '.join(text.split()) + + if len(text) > 400: + text = text[:text.rfind(' ', 0, 400)] + '...' + + return text diff --git a/plugins_available/choose.py b/plugins_available/choose.py new file mode 100644 index 0000000..3c6788e --- /dev/null +++ b/plugins_available/choose.py @@ -0,0 +1,19 @@ +import re +import random + +from util import hook + +@hook.command +def choose(inp): + ".choose , , ... -- makes a decision" + + if not inp: + return choose.__doc__ + + c = re.findall(r'([^,]+)', inp) + if len(c) == 1: + c = re.findall(r'(\S+)', inp) + if len(c) == 1: + return 'the decision is up to you' + + return random.choice(c).strip() diff --git a/plugins_available/dice.py b/plugins_available/dice.py new file mode 100644 index 0000000..13bb040 --- /dev/null +++ b/plugins_available/dice.py @@ -0,0 +1,59 @@ +""" +dice.py: written by Scaevolus 2008, updated 2009 +simulates dicerolls +""" +import re +import random + +from util import hook + + +whitespace_re = re.compile(r'\s+') +valid_diceroll_re = re.compile(r'^[+-]?(\d+|\d*d\d+)([+-](\d+|\d*d\d+))*$') +sign_re = re.compile(r'[+-]?(?:\d*d)?\d+') +split_re = re.compile(r'([\d+-]*)d?(\d*)') + + +def nrolls(count, n): + "roll an n-sided die count times" + if n < 2: #it's a coin + if count < 5000: + return sum(random.randint(0, 1) for x in xrange(count)) + else: #fake it + return int(random.normalvariate(.5*count, (.75*count)**.5)) + else: + if count < 5000: + return sum(random.randint(1, n) for x in xrange(count)) + else: #fake it + return int(random.normalvariate(.5*(1+n)*count, + (((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5)) + + +@hook.command +def dice(inp): + ".dice -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \ + "D20s, subtract 1D5, add 4" + if not inp.strip(): + return dice.__doc__ + + spec = whitespace_re.sub('', inp) + if not valid_diceroll_re.match(spec): + return "Invalid diceroll" + sum = 0 + groups = sign_re.findall(spec) + for roll in groups: + count, side = split_re.match(roll).groups() + if side == "": + sum += int(count) + else: + count = int(count) if count not in" +-" else 1 + side = int(side) + try: + if count > 0: + sum += nrolls(count, side) + else: + sum -= nrolls(abs(count), side) + except OverflowError: + return "Thanks for overflowing a float, jerk >:[" + + return str(sum) diff --git a/plugins_available/dotnetpad.py b/plugins_available/dotnetpad.py new file mode 100644 index 0000000..eb302b2 --- /dev/null +++ b/plugins_available/dotnetpad.py @@ -0,0 +1,96 @@ +"dotnetpad.py: by sklnd, because gobiner wouldn't shut up" + +import urllib +import httplib +import socket +import json + +from util import hook + + +def dotnetpad(lang, code): + "Posts a provided snippet of code in a provided langugage to dotnetpad.net" + + code = code.encode('utf8') + params = urllib.urlencode({'language': lang, 'code': code}) + + headers = {"Content-type": "application/x-www-form-urlencoded", + "Accept": "text/plain"} + + try: + conn = httplib.HTTPConnection("dotnetpad.net:80") + conn.request("POST", "/Skybot", params, headers) + response = conn.getresponse() + except httplib.HTTPException: + conn.close() + return 'error: dotnetpad is broken somehow' + except socket.error: + return 'error: unable to connect to dotnetpad' + + try: + result = json.loads(response.read()) + except ValueError: + conn.close() + return 'error: dotnetpad is broken somehow' + + conn.close() + + if result['Errors']: + return 'First error: %s' % (result['Errors'][0]['ErrorText']) + elif result['Output']: + return result['Output'].lstrip() + else: + return 'No output' + + +@hook.command +def fs(inp): + ".fs -- post a F# code snippet to dotnetpad.net and print the results" + + if not inp: + return fs.__doc__ + + return dotnetpad('fsharp', inp) + + +@hook.command +def cs(snippet): + ".cs -- post a C# code snippet to dotnetpad.net and print the results" + + if not snippet: + return cs.__doc__ + + file_template = ('using System; ' + 'using System.Linq; ' + 'using System.Collections.Generic; ' + 'using System.Text; ' + '%(class)s') + + class_template = ('public class Default ' + '{ ' + ' %(main)s ' + '}') + + main_template = ('public static void Main(String[] args) ' + '{ ' + ' %(snippet)s ' + '}') + + # There are probably better ways to do the following, but I'm feeling lazy + # if no main is found in the snippet, then we use the template with Main in it + if 'public static void Main' not in snippet: + code = main_template % { 'snippet': snippet } + code = class_template % { 'main': code } + code = file_template % { 'class' : code } + + # if Main is found, check for class and see if we need to use the classed template + elif 'class' not in snippet: + code = class_template % { 'main': snippet } + code = file_template % { 'class' : code } + + return 'Error using dotnetpad' + # if we found class, then use the barebones template + else: + code = file_template % { 'class' : snippet } + + return dotnetpad('csharp', code) diff --git a/plugins_available/down.py b/plugins_available/down.py new file mode 100644 index 0000000..a6247d7 --- /dev/null +++ b/plugins_available/down.py @@ -0,0 +1,27 @@ +import urllib2 +import urlparse + +from util import hook + +@hook.command +def down(inp): + '''.down -- checks to see if the site is down''' + inp = inp.strip() + + if not inp: + return down.__doc__ + + if 'http://' not in inp: + inp = 'http://' + inp + + inp = 'http://' + urlparse.urlparse(inp).netloc + + # http://mail.python.org/pipermail/python-list/2006-December/589854.html + try: + request = urllib2.Request(inp) + request.get_method = lambda: "HEAD" + http_file = urllib2.urlopen(request) + head = http_file.read() + return inp + ' seems to be up' + except urllib2.URLError: + return inp + ' seems to be down' diff --git a/plugins_available/explain.py b/plugins_available/explain.py new file mode 100755 index 0000000..ba24501 --- /dev/null +++ b/plugins_available/explain.py @@ -0,0 +1,15 @@ +from util import hook +from pycparser.cdecl import explain_c_declaration + +@hook.command('explain') +def explain(inp): + ".explain -- gives an explanation of C expression" + if not inp: + return explain.__doc__ + + inp = inp.encode('utf8', 'ignore') + + try: + return explain_c_declaration(inp.rstrip()) + except Exception, e: + return 'error: %s' % e diff --git a/plugins_available/google.py b/plugins_available/google.py new file mode 100644 index 0000000..e90c874 --- /dev/null +++ b/plugins_available/google.py @@ -0,0 +1,59 @@ +import urllib +import random +from lxml import html +import json + +from util import hook + + +def api_get(kind, query): + req_url = 'http://ajax.googleapis.com/ajax/services/search/%s?' \ + 'v=1.0&safe=off&q=%s' + query = query.encode('utf8') + url = req_url % (kind, urllib.quote(query, safe='')) + page = urllib.urlopen(url).read() + return json.loads(page) + + +@hook.command +def gis(inp): + '''.gis -- returns first google image result (safesearch off)''' + if not inp: + return gis.__doc__ + + parsed = api_get('images', inp) + if not 200 <= parsed['responseStatus'] < 300: + raise IOError('error searching for images: %d: %s' % ( + parsed['responseStatus'], '')) + if not parsed['responseData']['results']: + return 'no images found' + return random.choice(parsed['responseData']['results'][:10] + )['unescapedUrl'] # squares is dumb + + +@hook.command +@hook.command('g') +def google(inp): + '''.g/.google -- returns first google search result''' + if not inp: + return google.__doc__ + + parsed = api_get('web', inp) + if not 200 <= parsed['responseStatus'] < 300: + raise IOError('error searching for pages: %d: %s' % ( + parsed['responseStatus'], '')) + if not parsed['responseData']['results']: + return 'no results found' + + result = parsed['responseData']['results'][0] + + title, content = map(lambda x: html.fromstring(x).text_content(), + (result['titleNoFormatting'], result['content'])) + + out = '%s -- \x02%s\x02: "%s"' % (result['unescapedUrl'], title, content) + out = ' '.join(out.split()) + + if len(out) > 300: + out = out[:out.rfind(' ')] + '..."' + + return out diff --git a/plugins_available/goonsay.py b/plugins_available/goonsay.py new file mode 100644 index 0000000..7cd3623 --- /dev/null +++ b/plugins_available/goonsay.py @@ -0,0 +1,8 @@ +from util import hook + +#Scaevolus: factormystic if you commit a re-enabled goonsay I'm going to revoke your commit access +#@hook.command +def goonsay(bot, input): + input.say(' __________ /') + input.say('(--[. ]-[ .] /') + input.say('(_______o__)') diff --git a/plugins_available/hash.py b/plugins_available/hash.py new file mode 100644 index 0000000..99a2b84 --- /dev/null +++ b/plugins_available/hash.py @@ -0,0 +1,20 @@ +import hashlib + +from util import hook + + +@hook.command +def md5(inp): + return hashlib.md5(inp).hexdigest() + + +@hook.command +def sha1(inp): + return hashlib.sha1(inp).hexdigest() + + +@hook.command +def hash(inp): + ".hash -- returns hashes of " + return ', '.join(x + ": " + getattr(hashlib, x)(inp).hexdigest() + for x in 'md5 sha1 sha256'.split()) diff --git a/plugins_available/help.py b/plugins_available/help.py new file mode 100644 index 0000000..af73ce1 --- /dev/null +++ b/plugins_available/help.py @@ -0,0 +1,17 @@ +from util import hook + +@hook.command +def help(bot, input): + ".help [command] -- gives a list of commands/help for a command" + + funcs = {} + for csig, func, args in bot.plugs['command']: + if args['hook'] != r'(.*)': + if func.__doc__ is not None: + funcs[csig[1]] = func + + if not input.inp.strip(): + input.pm('available commands: ' + ' '.join(sorted(funcs))) + else: + if input.inp in funcs: + input.pm(funcs[input.inp].__doc__) diff --git a/plugins_available/log.py b/plugins_available/log.py new file mode 100644 index 0000000..ce27906 --- /dev/null +++ b/plugins_available/log.py @@ -0,0 +1,108 @@ +""" +log.py: written by Scaevolus 2009 +""" + +import os +import thread +import codecs +import time +import re + +from util import hook + + +lock = thread.allocate_lock() +log_fds = {} # '%(net)s %(chan)s' : (filename, fd) + +timestamp_format = '%H:%M:%S' + +formats = {'PRIVMSG': '<%(nick)s> %(msg)s', + 'PART': '-!- %(nick)s [%(user)s@%(host)s] has left %(chan)s', + 'JOIN': '-!- %(nick)s [%(user)s@%(host)s] has joined %(param0)s', + 'MODE': '-!- mode/%(chan)s [%(param_tail)s] by %(nick)s', + 'KICK': '-!- %(param1)s was kicked from %(chan)s by %(nick)s [%(msg)s]', + 'TOPIC': '-!- %(nick)s changed the topic of %(chan)s to: %(msg)s', + 'QUIT': '-!- %(nick)s has quit [%(msg)s]', + 'PING': '', + 'NOTICE': '' +} + +ctcp_formats = {'ACTION': '* %(nick)s %(ctcpmsg)s'} + +irc_color_re = re.compile(r'(\x03(\d+,\d+|\d)|[\x0f\x02\x16\x1f])') + + +def get_log_filename(dir, server, chan): + return os.path.join(dir, 'log', gmtime('%Y'), server, + gmtime('%%s.%m-%d.log') % chan).lower() + + +def gmtime(format): + return time.strftime(format, time.gmtime()) + + +def beautify(input): + format = formats.get(input.command, '%(raw)s') + args = vars(input) + leng = len(args['paraml']) + for n, p in enumerate(args['paraml']): + args['param' + str(n)] = p + args['param_' + str(abs(n - leng))] = p + + args['param_tail'] = ' '.join(args['paraml'][1:]) + args['msg'] = irc_color_re.sub('', args['msg']) + + if input.command == 'PRIVMSG' and input.msg.count('\x01') >= 2: + #ctcp + ctcp = input.msg.split('\x01', 2)[1].split(' ', 1) + if len(ctcp) == 1: + ctcp += [''] + args['ctcpcmd'], args['ctcpmsg'] = ctcp + format = ctcp_formats.get(args['ctcpcmd'], + '%(nick)s [%(user)s@%(host)s] requested unknown CTCP ' + '%(ctcpcmd)s from %(chan)s: %(ctcpmsg)s') + + return format % args + + +def get_log_fd(dir, server, chan): + fn = get_log_filename(dir, server, chan) + cache_key = '%s %s' % (server, chan) + filename, fd = log_fds.get(cache_key, ('', 0)) + + if fn != filename: # we need to open a file for writing + if fd != 0: # is a valid fd + fd.flush() + fd.close() + dir = os.path.split(fn)[0] + if not os.path.exists(dir): + os.makedirs(dir) + fd = codecs.open(fn, 'a', 'utf-8') + log_fds[cache_key] = (fn, fd) + + return fd + + +@hook.tee +def log(bot, input): + with lock: + timestamp = gmtime(timestamp_format) + + fd = get_log_fd(bot.persist_dir, input.server, 'raw') + fd.write(timestamp + ' ' + input.raw + '\n') + + if input.command == 'QUIT': # these are temporary fixes until proper + input.chan = 'quit' # presence tracking is implemented + if input.command == 'NICK': + input.chan = 'nick' + + beau = beautify(input) + + if beau == '': # don't log this + return + + if input.chan: + fd = get_log_fd(bot.persist_dir, input.server, input.chan) + fd.write(timestamp + ' ' + beau + '\n') + + print timestamp, input.chan, beau.encode('utf8', 'ignore') diff --git a/plugins_available/misc.py b/plugins_available/misc.py new file mode 100644 index 0000000..181db84 --- /dev/null +++ b/plugins_available/misc.py @@ -0,0 +1,30 @@ +from util import hook +import socket + +socket.setdefaulttimeout(5) # global setting + + +#autorejoin channels +@hook.event('KICK') +def rejoin(bot, input): + if input.paraml[1] == input.conn.nick: + if input.paraml[0] in input.conn.channels: + input.conn.join(input.paraml[0]) + +#join channels when invited +@hook.event('INVITE') +def invite(bot, input): + if input.command == 'INVITE': + input.conn.join(input.inp) + +#join channels when server says hello & identify bot +@hook.event('004') +def onjoin(bot, input): + for channel in input.conn.channels: + input.conn.join(channel) + + nickserv_password = input.conn.conf.get('nickserv_password', '') + nickserv_name = input.conn.conf.get('nickserv_name', 'nickserv') + nickserv_command = input.conn.conf.get('nickserv_command', 'IDENTIFY %s') + if nickserv_password: + input.conn.msg(nickserv_name, nickserv_command % nickserv_password) diff --git a/plugins_available/mtg.py b/plugins_available/mtg.py new file mode 100644 index 0000000..e3054f3 --- /dev/null +++ b/plugins_available/mtg.py @@ -0,0 +1,170 @@ +from lxml import html +import re +import urllib2 +import sys + +from util import hook + + +@hook.command +def mtg(inp): + url = 'http://magiccards.info/query.php?cardname=' + url += urllib2.quote(inp, safe='') + h = html.parse(url) + name = h.find('/body/table/tr/td/table/tr/td/h1') + if name is None: + return "no cards found" + card = name.getparent() + text = card.find('p') + + type = text.text + text = text.find('b').text_content() + text = re.sub(r'\(.*?\)', '', text) # strip parenthetical explanations + text = re.sub(r'\.(\S)', r'. \1', text) # fix spacing + + printings = card.find('table/tr/td/img').getparent().text_content() + printings = re.findall(r'\s*(.+?(?: \([^)]+\))*) \((.*?)\)', + ' '.join(printings.split())) + printing_out = ', '.join('%s (%s)' % (set_abbrevs.get(x[0], x[0]), + rarity_abbrevs.get(x[1], x[1])) + for x in printings) + + name.make_links_absolute() + link = name.find('a').attrib['href'] + name = name.text_content().strip() + type = type.strip() + text = ' '.join(text.split()) + + return ' | '.join((name, type, text, printing_out, link)) + + +set_abbrevs = { + '15th Anniversary': '15ANN', + 'APAC Junior Series': 'AJS', + 'Alara Reborn': 'ARB', + 'Alliances': 'AI', + 'Anthologies': 'AT', + 'Antiquities': 'AQ', + 'Apocalypse': 'AP', + 'Arabian Nights': 'AN', + 'Arena League': 'ARENA', + 'Asia Pacific Land Program': 'APAC', + 'Battle Royale': 'BR', + 'Beatdown': 'BD', + 'Betrayers of Kamigawa': 'BOK', + 'Celebration Cards': 'UQC', + 'Champions of Kamigawa': 'CHK', + 'Champs': 'CP', + 'Chronicles': 'CH', + 'Classic Sixth Edition': '6E', + 'Coldsnap': 'CS', + 'Coldsnap Theme Decks': 'CSTD', + 'Conflux': 'CFX', + 'Core Set - Eighth Edition': '8E', + 'Core Set - Ninth Edition': '9E', + 'Darksteel': 'DS', + 'Deckmasters': 'DM', + 'Dissension': 'DI', + 'Dragon Con': 'DRC', + 'Duel Decks: Divine vs. Demonic': 'DVD', + 'Duel Decks: Elves vs. Goblins': 'EVG', + 'Duel Decks: Garruk vs. Liliana': 'GVL', + 'Duel Decks: Jace vs. Chandra': 'JVC', + 'Eighth Edition Box Set': '8EB', + 'European Land Program': 'EURO', + 'Eventide': 'EVE', + 'Exodus': 'EX', + 'Fallen Empires': 'FE', + 'Fifth Dawn': '5DN', + 'Fifth Edition': '5E', + 'Fourth Edition': '4E', + 'Friday Night Magic': 'FNMP', + 'From the Vault: Dragons': 'FVD', + 'From the Vault: Exiled': 'FVE', + 'Future Sight': 'FUT', + 'Gateway': 'GRC', + 'Grand Prix': 'GPX', + 'Guildpact': 'GP', + 'Guru': 'GURU', + 'Happy Holidays': 'HHO', + 'Homelands': 'HL', + 'Ice Age': 'IA', + 'Introductory Two-Player Set': 'ITP', + 'Invasion': 'IN', + 'Judge Gift Program': 'JR', + 'Judgment': 'JU', + 'Junior Series': 'JSR', + 'Legend Membership': 'DCILM', + 'Legends': 'LG', + 'Legions': 'LE', + 'Limited Edition (Alpha)': 'AL', + 'Limited Edition (Beta)': 'BE', + 'Lorwyn': 'LW', + 'MTGO Masters Edition': 'MED', + 'MTGO Masters Edition II': 'ME2', + 'MTGO Masters Edition III': 'ME3', + 'Magic 2010': 'M10', + 'Magic Game Day Cards': 'MGDC', + 'Magic Player Rewards': 'MPRP', + 'Magic Scholarship Series': 'MSS', + 'Magic: The Gathering Launch Parties': 'MLP', + 'Media Inserts': 'MBP', + 'Mercadian Masques': 'MM', + 'Mirage': 'MR', + 'Mirrodin': 'MI', + 'Morningtide': 'MT', + 'Multiverse Gift Box Cards': 'MGBC', + 'Nemesis': 'NE', + 'Ninth Edition Box Set': '9EB', + 'Odyssey': 'OD', + 'Onslaught': 'ON', + 'Planar Chaos': 'PC', + 'Planechase': 'PCH', + 'Planeshift': 'PS', + 'Portal': 'PO', + 'Portal Demogame': 'POT', + 'Portal Second Age': 'PO2', + 'Portal Three Kingdoms': 'P3K', + 'Premium Deck Series: Slivers': 'PDS', + 'Prerelease Events': 'PTC', + 'Pro Tour': 'PRO', + 'Prophecy': 'PR', + 'Ravnica: City of Guilds': 'RAV', + 'Release Events': 'REP', + 'Revised Edition': 'RV', + 'Saviors of Kamigawa': 'SOK', + 'Scourge': 'SC', + 'Seventh Edition': '7E', + 'Shadowmoor': 'SHM', + 'Shards of Alara': 'ALA', + 'Starter': 'ST', + 'Starter 2000 Box Set': 'ST2K', + 'Stronghold': 'SH', + 'Summer of Magic': 'SOM', + 'Super Series': 'SUS', + 'Tempest': 'TP', + 'Tenth Edition': '10E', + 'The Dark': 'DK', + 'Time Spiral': 'TS', + 'Time Spiral Timeshifted': 'TSTS', + 'Torment': 'TR', + 'Two-Headed Giant Tournament': 'THGT', + 'Unglued': 'UG', + 'Unhinged': 'UH', + 'Unhinged Alternate Foils': 'UHAA', + 'Unlimited Edition': 'UN', + "Urza's Destiny": 'UD', + "Urza's Legacy": 'UL', + "Urza's Saga": 'US', + 'Visions': 'VI', + 'Weatherlight': 'WL', + 'Worlds': 'WRL', + 'WotC Online Store': 'WOTC', + 'Zendikar': 'ZEN'} + +rarity_abbrevs = { + 'Common': 'C', + 'Uncommon': 'UC', + 'Rare': 'R', + 'Special': 'S', + 'Mythic Rare': 'MR'} diff --git a/plugins_available/profile.py b/plugins_available/profile.py new file mode 100644 index 0000000..a6faee2 --- /dev/null +++ b/plugins_available/profile.py @@ -0,0 +1,13 @@ +# for crusty old rotor + +from util import hook + + +@hook.command +def profile(inp): + ".profile -- links to 's profile on SA" + if not inp: + return profile.__doc__ + + return 'http://forums.somethingawful.com/member.php?action=getinfo' + \ + '&username=' + '+'.join(inp.split()) diff --git a/plugins_available/pycparser/__init__.py b/plugins_available/pycparser/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins_available/pycparser/cdecl.py b/plugins_available/pycparser/cdecl.py new file mode 100644 index 0000000..c0dc5b3 --- /dev/null +++ b/plugins_available/pycparser/cdecl.py @@ -0,0 +1,98 @@ +#----------------------------------------------------------------- +# pycparser: cdecl.py +# +# Example of the CDECL tool using pycparser. CDECL "explains" +# C type declarations in plain English. +# +# The AST generated by pycparser from the given declaration is +# traversed recursively to build the explanation. +# Note that the declaration must be a valid external declaration +# in C. All the types used in it must be defined with typedef, +# or parsing will fail. The definition can be arbitrary, it isn't +# really used - by pycparser must know which tokens are types. +# +# For example: +# +# 'typedef int Node; const Node* (*ar)[10];' +# => +# ar is a pointer to array[10] of pointer to const Node +# +# Copyright (C) 2008, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- +import sys + +from pycparser import c_parser, c_ast + + +def explain_c_declaration(c_decl): + """ Parses the declaration in c_decl and returns a text + explanation as a string. + + The last external node of the string is used, to allow + earlier typedefs for used types. + """ + parser = c_parser.CParser() + + node = parser.parse(c_decl, filename='') + + if ( not isinstance(node, c_ast.FileAST) or + not isinstance(node.ext[-1], c_ast.Decl)): + return "Last external node is invalid type" + + return _explain_decl_node(node.ext[-1]) + + +def _explain_decl_node(decl_node): + """ Receives a c_ast.Decl note and returns its explanation in + English. + """ + #~ print decl_node.show() + storage = ' '.join(decl_node.storage) + ' ' if decl_node.storage else '' + + return (decl_node.name + + " is a " + + storage + + _explain_type(decl_node.type)) + + +def _explain_type(decl): + """ Recursively explains a type decl node + """ + typ = type(decl) + + if typ == c_ast.TypeDecl: + quals = ' '.join(decl.quals) + ' ' if decl.quals else '' + return quals + _explain_type(decl.type) + elif typ == c_ast.Typename or typ == c_ast.Decl: + return _explain_type(decl.type) + elif typ == c_ast.IdentifierType: + return ' '.join(decl.names) + elif typ == c_ast.PtrDecl: + quals = ' '.join(decl.quals) + ' ' if decl.quals else '' + return quals + 'pointer to ' + _explain_type(decl.type) + elif typ == c_ast.ArrayDecl: + arr = 'array' + if decl.dim: arr += '[%s]' % decl.dim.value + + return arr + " of " + _explain_type(decl.type) + + elif typ == c_ast.FuncDecl: + if decl.args: + params = [_explain_type(param) for param in decl.args.params] + args = ', '.join(params) + else: + args = '' + + return ('function(%s) returning ' % (args) + + _explain_type(decl.type)) + + +if __name__ == "__main__": + if len(sys.argv) > 1: + c_decl = sys.argv[1] + else: + c_decl = "char *(*(**foo[][8])())[];" + + print "Explaining the declaration:", c_decl + print "\n", explain_c_declaration(c_decl) diff --git a/plugins_available/pycparser/lextab.py b/plugins_available/pycparser/lextab.py new file mode 100644 index 0000000..f33ce0c --- /dev/null +++ b/plugins_available/pycparser/lextab.py @@ -0,0 +1,9 @@ +# pycparser.lextab.py. This file automatically created by PLY (version 3.3). Don't edit! +_tabversion = '3.3' +_lextokens = {'VOID': 1, 'LBRACKET': 1, 'WCHAR_CONST': 1, 'FLOAT_CONST': 1, 'MINUS': 1, 'RPAREN': 1, 'LONG': 1, 'PLUS': 1, 'ELLIPSIS': 1, 'GT': 1, 'GOTO': 1, 'ENUM': 1, 'PERIOD': 1, 'GE': 1, 'INT_CONST_DEC': 1, 'ARROW': 1, 'DOUBLE': 1, 'MINUSEQUAL': 1, 'INT_CONST_OCT': 1, 'TIMESEQUAL': 1, 'OR': 1, 'SHORT': 1, 'RETURN': 1, 'RSHIFTEQUAL': 1, 'STATIC': 1, 'SIZEOF': 1, 'UNSIGNED': 1, 'UNION': 1, 'COLON': 1, 'WSTRING_LITERAL': 1, 'DIVIDE': 1, 'FOR': 1, 'PLUSPLUS': 1, 'EQUALS': 1, 'ELSE': 1, 'EQ': 1, 'AND': 1, 'TYPEID': 1, 'LBRACE': 1, 'PPHASH': 1, 'INT': 1, 'SIGNED': 1, 'CONTINUE': 1, 'NOT': 1, 'OREQUAL': 1, 'MOD': 1, 'RSHIFT': 1, 'DEFAULT': 1, 'CHAR': 1, 'WHILE': 1, 'DIVEQUAL': 1, 'EXTERN': 1, 'CASE': 1, 'LAND': 1, 'REGISTER': 1, 'MODEQUAL': 1, 'NE': 1, 'SWITCH': 1, 'INT_CONST_HEX': 1, 'PLUSEQUAL': 1, 'STRUCT': 1, 'CONDOP': 1, 'BREAK': 1, 'VOLATILE': 1, 'ANDEQUAL': 1, 'DO': 1, 'LNOT': 1, 'CONST': 1, 'LOR': 1, 'CHAR_CONST': 1, 'LSHIFT': 1, 'RBRACE': 1, 'LE': 1, 'SEMI': 1, 'LT': 1, 'COMMA': 1, 'TYPEDEF': 1, 'XOR': 1, 'AUTO': 1, 'TIMES': 1, 'LPAREN': 1, 'MINUSMINUS': 1, 'ID': 1, 'IF': 1, 'STRING_LITERAL': 1, 'FLOAT': 1, 'XOREQUAL': 1, 'LSHIFTEQUAL': 1, 'RBRACKET': 1} +_lexreflags = 0 +_lexliterals = '' +_lexstateinfo = {'ppline': 'exclusive', 'INITIAL': 'inclusive'} +_lexstatere = {'ppline': [('(?P"([^"\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))*")|(?P(0(([uU][lL])|([lL][uU])|[uU]|[lL])?)|([1-9][0-9]*(([uU][lL])|([lL][uU])|[uU]|[lL])?))|(?P\\n)|(?Pline)', [None, ('t_ppline_FILENAME', 'FILENAME'), None, None, None, None, None, None, ('t_ppline_LINE_NUMBER', 'LINE_NUMBER'), None, None, None, None, None, None, None, None, ('t_ppline_NEWLINE', 'NEWLINE'), ('t_ppline_PPLINE', 'PPLINE')])], 'INITIAL': [('(?P[ \\t]*\\#)|(?P\\n+)|(?P((((([0-9]*\\.[0-9]+)|([0-9]+\\.))([eE][-+]?[0-9]+)?)|([0-9]+([eE][-+]?[0-9]+)))[FfLl]?))|(?P0[xX][0-9a-fA-F]+(([uU][lL])|([lL][uU])|[uU]|[lL])?)|(?P0[0-7]*[89])|(?P0[0-7]*(([uU][lL])|([lL][uU])|[uU]|[lL])?)|(?P(0(([uU][lL])|([lL][uU])|[uU]|[lL])?)|([1-9][0-9]*(([uU][lL])|([lL][uU])|[uU]|[lL])?))|(?P\'([^\'\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))\')|(?PL\'([^\'\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))\')|(?P(\'([^\'\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))*\\n)|(\'([^\'\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))*$))|(?P(\'([^\'\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))[^\'\n]+\')|(\'\')|(\'([\\\\][^a-zA-Z\\\\?\'"x0-7])[^\'\\n]*\'))|(?PL"([^"\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))*")|(?P"([^"\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))*([\\\\][^a-zA-Z\\\\?\'"x0-7])([^"\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))*")|(?P[a-zA-Z_][0-9a-zA-Z_]*)|(?P"([^"\\\\\\n]|(\\\\(([a-zA-Z\\\\?\'"])|([0-7]{1,3})|(x[0-9a-fA-F]+))))*")', [None, ('t_PPHASH', 'PPHASH'), ('t_NEWLINE', 'NEWLINE'), ('t_FLOAT_CONST', 'FLOAT_CONST'), None, None, None, None, None, None, None, None, None, ('t_INT_CONST_HEX', 'INT_CONST_HEX'), None, None, None, ('t_BAD_CONST_OCT', 'BAD_CONST_OCT'), ('t_INT_CONST_OCT', 'INT_CONST_OCT'), None, None, None, ('t_INT_CONST_DEC', 'INT_CONST_DEC'), None, None, None, None, None, None, None, None, ('t_CHAR_CONST', 'CHAR_CONST'), None, None, None, None, None, None, ('t_WCHAR_CONST', 'WCHAR_CONST'), None, None, None, None, None, None, ('t_UNMATCHED_QUOTE', 'UNMATCHED_QUOTE'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_BAD_CHAR_CONST', 'BAD_CHAR_CONST'), None, None, None, None, None, None, None, None, None, None, ('t_WSTRING_LITERAL', 'WSTRING_LITERAL'), None, None, None, None, None, None, ('t_BAD_STRING_LITERAL', 'BAD_STRING_LITERAL'), None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_ID', 'ID'), (None, 'STRING_LITERAL')]), ('(?P\\.\\.\\.)|(?P\\+\\+)|(?P\\|\\|)|(?P\\|=)|(?P<<=)|(?P>>=)|(?P\\*=)|(?P\\+=)|(?P^=)|(?P\\+)|(?P%=)|(?P\\{)|(?P/=)|(?P\\])|(?P\\?)', [None, (None, 'ELLIPSIS'), (None, 'PLUSPLUS'), (None, 'LOR'), (None, 'OREQUAL'), (None, 'LSHIFTEQUAL'), (None, 'RSHIFTEQUAL'), (None, 'TIMESEQUAL'), (None, 'PLUSEQUAL'), (None, 'XOREQUAL'), (None, 'PLUS'), (None, 'MODEQUAL'), (None, 'LBRACE'), (None, 'DIVEQUAL'), (None, 'RBRACKET'), (None, 'CONDOP')]), ('(?P\\^)|(?P<<)|(?P<=)|(?P\\()|(?P->)|(?P==)|(?P\\})|(?P!=)|(?P--)|(?P\\|)|(?P\\*)|(?P\\[)|(?P>=)|(?P\\))|(?P&&)|(?P>>)|(?P&=)|(?P-=)|(?P\\.)|(?P=)|(?P<)|(?P,)|(?P/)|(?P&)|(?P%)|(?P;)|(?P-)|(?P>)|(?P:)|(?P~)|(?P!)', [None, (None, 'XOR'), (None, 'LSHIFT'), (None, 'LE'), (None, 'LPAREN'), (None, 'ARROW'), (None, 'EQ'), (None, 'RBRACE'), (None, 'NE'), (None, 'MINUSMINUS'), (None, 'OR'), (None, 'TIMES'), (None, 'LBRACKET'), (None, 'GE'), (None, 'RPAREN'), (None, 'LAND'), (None, 'RSHIFT'), (None, 'ANDEQUAL'), (None, 'MINUSEQUAL'), (None, 'PERIOD'), (None, 'EQUALS'), (None, 'LT'), (None, 'COMMA'), (None, 'DIVIDE'), (None, 'AND'), (None, 'MOD'), (None, 'SEMI'), (None, 'MINUS'), (None, 'GT'), (None, 'COLON'), (None, 'NOT'), (None, 'LNOT')])]} +_lexstateignore = {'ppline': ' \t', 'INITIAL': ' \t'} +_lexstateerrorf = {'ppline': 't_ppline_error', 'INITIAL': 't_error'} diff --git a/plugins_available/pycparser/pycparser/__init__.py b/plugins_available/pycparser/pycparser/__init__.py new file mode 100644 index 0000000..6219bb2 --- /dev/null +++ b/plugins_available/pycparser/pycparser/__init__.py @@ -0,0 +1,75 @@ +#----------------------------------------------------------------- +# pycparser: __init__.py +# +# This package file exports some convenience functions for +# interacting with pycparser +# +# Copyright (C) 2008-2009, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + +__all__ = ['c_lexer', 'c_parser', 'c_ast'] +__version__ = '1.05' + +from subprocess import Popen, PIPE +from types import ListType + +from c_parser import CParser + + +def parse_file( filename, use_cpp=False, + cpp_path='cpp', cpp_args=''): + """ Parse a C file using pycparser. + + filename: + Name of the file you want to parse. + + use_cpp: + Set to True if you want to execute the C pre-processor + on the file prior to parsing it. + + cpp_path: + If use_cpp is True, this is the path to 'cpp' on your + system. If no path is provided, it attempts to just + execute 'cpp', so it must be in your PATH. + + cpp_args: + If use_cpp is True, set this to the command line + arguments strings to cpp. Be careful with quotes - + it's best to pass a raw string (r'') here. + For example: + r'-I../utils/fake_libc_include' + If several arguments are required, pass a list of + strings. + + When successful, an AST is returned. ParseError can be + thrown if the file doesn't parse successfully. + + Errors from cpp will be printed out. + """ + if use_cpp: + path_list = [cpp_path] + if isinstance(cpp_args, ListType): + path_list += cpp_args + elif cpp_args != '': + path_list += [cpp_args] + path_list += [filename] + + # Note the use of universal_newlines to treat all newlines + # as \n for Python's purpose + # + pipe = Popen( path_list, + stdout=PIPE, + universal_newlines=True) + text = pipe.communicate()[0] + else: + text = open(filename).read() + + parser = CParser() + return parser.parse(text, filename) + + +if __name__ == "__main__": + pass + + diff --git a/plugins_available/pycparser/pycparser/_ast_gen.py b/plugins_available/pycparser/pycparser/_ast_gen.py new file mode 100644 index 0000000..47ba148 --- /dev/null +++ b/plugins_available/pycparser/pycparser/_ast_gen.py @@ -0,0 +1,249 @@ +#----------------------------------------------------------------- +# _ast_gen.py +# +# Generates the AST Node classes from a specification given in +# a .yaml file +# +# The design of this module was inspired by astgen.py from the +# Python 2.5 code-base. +# +# Copyright (C) 2008-2009, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + +import pprint +from string import Template + +import yaml + + +class ASTCodeGenerator(object): + def __init__(self, cfg_filename='_c_ast.yaml'): + """ Initialize the code generator from a configuration + file. + """ + self.cfg_filename = cfg_filename + cfg = yaml.load(open(cfg_filename).read()) + self.node_cfg = [NodeCfg(name, cfg[name]) for name in cfg] + + #~ pprint.pprint(self.node_cfg) + #~ print '' + + def generate(self, file=None): + """ Generates the code into file, an open file buffer. + """ + src = Template(_PROLOGUE_COMMENT).substitute( + cfg_filename=self.cfg_filename) + + src += _PROLOGUE_CODE + for node_cfg in self.node_cfg: + src += node_cfg.generate_source() + '\n\n' + + file.write(src) + + +class NodeCfg(object): + def __init__(self, name, contents): + self.name = name + self.all_entries = [] + self.attr = [] + self.child = [] + self.seq_child = [] + + for entry in contents: + clean_entry = entry.rstrip('*') + self.all_entries.append(clean_entry) + + if entry.endswith('**'): + self.seq_child.append(clean_entry) + elif entry.endswith('*'): + self.child.append(clean_entry) + else: + self.attr.append(entry) + + def generate_source(self): + src = self._gen_init() + src += '\n' + self._gen_children() + src += '\n' + self._gen_show() + return src + + def _gen_init(self): + src = "class %s(Node):\n" % self.name + + if self.all_entries: + args = ', '.join(self.all_entries) + arglist = '(self, %s, coord=None)' % args + else: + arglist = '(self, coord=None)' + + src += " def __init__%s:\n" % arglist + + for name in self.all_entries + ['coord']: + src += " self.%s = %s\n" % (name, name) + + return src + + def _gen_children(self): + src = ' def children(self):\n' + + if self.all_entries: + src += ' nodelist = []\n' + + template = ('' + + ' if self.%s is not None:' + + ' nodelist.%s(self.%s)\n') + + for child in self.child: + src += template % ( + child, 'append', child) + + for seq_child in self.seq_child: + src += template % ( + seq_child, 'extend', seq_child) + + src += ' return tuple(nodelist)\n' + else: + src += ' return ()\n' + + return src + + def _gen_show(self): + src = ' def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False):\n' + src += " lead = ' ' * offset\n" + + src += " buf.write(lead + '%s: ')\n\n" % self.name + + if self.attr: + src += " if attrnames:\n" + src += " attrstr = ', '.join('%s=%s' % nv for nv in [" + src += ', '.join('("%s", repr(%s))' % (nv, 'self.%s' % nv) for nv in self.attr) + src += '])\n' + src += " else:\n" + src += " attrstr = ', '.join('%s' % v for v in [" + src += ', '.join('self.%s' % v for v in self.attr) + src += '])\n' + src += " buf.write(attrstr)\n\n" + + src += " if showcoord:\n" + src += " buf.write(' (at %s)' % self.coord)\n" + src += " buf.write('\\n')\n\n" + + src += " for c in self.children():\n" + src += " c.show(buf, offset + 2, attrnames, showcoord)\n" + + return src + + +_PROLOGUE_COMMENT = \ +r'''#----------------------------------------------------------------- +# ** ATTENTION ** +# This code was automatically generated from the file: +# $cfg_filename +# +# Do not modify it directly. Modify the configuration file and +# run the generator again. +# ** ** *** ** ** +# +# pycparser: c_ast.py +# +# AST Node classes. +# +# Copyright (C) 2008, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + +''' + +_PROLOGUE_CODE = r''' +import sys + + +class Node(object): + """ Abstract base class for AST nodes. + """ + def children(self): + """ A sequence of all children that are Nodes + """ + pass + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + """ Pretty print the Node and all its attributes and + children (recursively) to a buffer. + + file: + Open IO buffer into which the Node is printed. + + offset: + Initial offset (amount of leading spaces) + + attrnames: + True if you want to see the attribute names in + name=value pairs. False to only see the values. + + showcoord: + Do you want the coordinates of each Node to be + displayed. + """ + pass + + +class NodeVisitor(object): + """ A base NodeVisitor class for visiting c_ast nodes. + Subclass it and define your own visit_XXX methods, where + XXX is the class name you want to visit with these + methods. + + For example: + + class ConstantVisitor(NodeVisitor): + def __init__(self): + self.values = [] + + def visit_Constant(self, node): + self.values.append(node.value) + + Creates a list of values of all the constant nodes + encountered below the given node. To use it: + + cv = ConstantVisitor() + cv.visit(node) + + Notes: + + * generic_visit() will be called for AST nodes for which + no visit_XXX method was defined. + * The children of nodes for which a visit_XXX was + defined will not be visited - if you need this, call + generic_visit() on the node. + You can use: + NodeVisitor.generic_visit(self, node) + * Modeled after Python's own AST visiting facilities + (the ast module of Python 3.0) + """ + def visit(self, node): + """ Visit a node. + """ + method = 'visit_' + node.__class__.__name__ + visitor = getattr(self, method, self.generic_visit) + return visitor(node) + + def generic_visit(self, node): + """ Called if no explicit visitor function exists for a + node. Implements preorder visiting of the node. + """ + for c in node.children(): + self.visit(c) + + +''' + + + +if __name__ == "__main__": + import sys + + ast_gen = ASTCodeGenerator('_c_ast.yaml') + ast_gen.generate(open('c_ast.py', 'w')) + + + diff --git a/plugins_available/pycparser/pycparser/_build_tables.py b/plugins_available/pycparser/pycparser/_build_tables.py new file mode 100644 index 0000000..0f02387 --- /dev/null +++ b/plugins_available/pycparser/pycparser/_build_tables.py @@ -0,0 +1,31 @@ +#----------------------------------------------------------------- +# pycparser: _build_tables.py +# +# A dummy for generating the lexing/parsing tables and and +# compiling them into .pyc for faster execution in optimized mode. +# Also generates AST code from the _c_ast.yaml configuration file. +# +# Copyright (C) 2008, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + +# Generate c_ast.py +# +from _ast_gen import ASTCodeGenerator +ast_gen = ASTCodeGenerator('_c_ast.yaml') +ast_gen.generate(open('c_ast.py', 'w')) + +import c_parser + +# Generates the tables +# +c_parser.CParser( + lex_optimize=True, + yacc_debug=False, + yacc_optimize=True) + +# Load to compile into .pyc +# +import lextab +import yacctab +import c_ast diff --git a/plugins_available/pycparser/pycparser/_c_ast.yaml b/plugins_available/pycparser/pycparser/_c_ast.yaml new file mode 100644 index 0000000..c954e38 --- /dev/null +++ b/plugins_available/pycparser/pycparser/_c_ast.yaml @@ -0,0 +1,164 @@ +#----------------------------------------------------------------- +# pycparser: _c_ast_gen.yaml +# +# Defines the AST Node classes used in pycparser. +# +# Each entry is a Node sub-class name, listing the attributes +# and child nodes of the class: +# * - a child node +# ** - a sequence of child nodes +# - an attribute +# +# Copyright (C) 2008-2009, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + + +ArrayDecl: [type*, dim*] + +ArrayRef: [name*, subscript*] + +# op: =, +=, /= etc. +# +Assignment: [op, lvalue*, rvalue*] + +BinaryOp: [op, left*, right*] + +Break: [] + +Case: [expr*, stmt*] + +Cast: [to_type*, expr*] + +# Compound statement: { declarations... statements...} +# +Compound: [decls**, stmts**] + +# type: int, char, float, etc. see CLexer for constant token types +# +Constant: [type, value] + +Continue: [] + +# name: the variable being declared +# quals: list of qualifiers (const, volatile) +# storage: list of storage specifiers (extern, register, etc.) +# type: declaration type (probably nested with all the modifiers) +# init: initialization value, or None +# bitsize: bit field size, or None +# +Decl: [name, quals, storage, type*, init*, bitsize*] + +Default: [stmt*] + +DoWhile: [cond*, stmt*] + +# Represents the ellipsis (...) parameter in a function +# declaration +# +EllipsisParam: [] + +# Enumeration type specifier +# name: an optional ID +# values: an EnumeratorList +# +Enum: [name, values*] + +# A name/value pair for enumeration values +# +Enumerator: [name, value*] + +# A list of enumerators +# +EnumeratorList: [enumerators**] + +# a list of comma separated expressions +# +ExprList: [exprs**] + +# This is the top of the AST, representing a single C file (a +# translation unit in K&R jargon). It contains a list of +# "external-declaration"s, which is either declarations (Decl), +# Typedef or function definitions (FuncDef). +# +FileAST: [ext**] + +# for (init; cond; next) stmt +# +For: [init*, cond*, next*, stmt*] + +# name: Id +# args: ExprList +# +FuncCall: [name*, args*] + +# type (args) +# +FuncDecl: [args*, type*] + +# Function definition: a declarator for the function name and +# a body, which is a compound statement. +# There's an optional list of parameter declarations for old +# K&R-style definitions +# +FuncDef: [decl*, param_decls**, body*] + +Goto: [name] + +ID: [name] + +# Holder for types that are a simple identifier (e.g. the built +# ins void, char etc. and typedef-defined types) +# +IdentifierType: [names] + +If: [cond*, iftrue*, iffalse*] + +Label: [name, stmt*] + +# a list of comma separated function parameter declarations +# +ParamList: [params**] + +PtrDecl: [quals, type*] + +Return: [expr*] + +# name: struct tag name +# decls: declaration of members +# +Struct: [name, decls**] + +# type: . or -> +# name.field or name->field +# +StructRef: [name*, type, field*] + +Switch: [cond*, stmt*] + +# cond ? iftrue : iffalse +# +TernaryOp: [cond*, iftrue*, iffalse*] + +# A base type declaration +# +TypeDecl: [declname, quals, type*] + +# A typedef declaration. +# Very similar to Decl, but without some attributes +# +Typedef: [name, quals, storage, type*] + +Typename: [quals, type*] + +UnaryOp: [op, expr*] + +# name: union tag name +# decls: declaration of members +# +Union: [name, decls**] + +While: [cond*, stmt*] + + + diff --git a/plugins_available/pycparser/pycparser/c_ast.py b/plugins_available/pycparser/pycparser/c_ast.py new file mode 100644 index 0000000..90cfb79 --- /dev/null +++ b/plugins_available/pycparser/pycparser/c_ast.py @@ -0,0 +1,1163 @@ +#----------------------------------------------------------------- +# ** ATTENTION ** +# This code was automatically generated from the file: +# _c_ast.yaml +# +# Do not modify it directly. Modify the configuration file and +# run the generator again. +# ** ** *** ** ** +# +# pycparser: c_ast.py +# +# AST Node classes. +# +# Copyright (C) 2008, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + + +import sys + + +class Node(object): + """ Abstract base class for AST nodes. + """ + def children(self): + """ A sequence of all children that are Nodes + """ + pass + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + """ Pretty print the Node and all its attributes and + children (recursively) to a buffer. + + file: + Open IO buffer into which the Node is printed. + + offset: + Initial offset (amount of leading spaces) + + attrnames: + True if you want to see the attribute names in + name=value pairs. False to only see the values. + + showcoord: + Do you want the coordinates of each Node to be + displayed. + """ + pass + + +class NodeVisitor(object): + """ A base NodeVisitor class for visiting c_ast nodes. + Subclass it and define your own visit_XXX methods, where + XXX is the class name you want to visit with these + methods. + + For example: + + class ConstantVisitor(NodeVisitor): + def __init__(self): + self.values = [] + + def visit_Constant(self, node): + self.values.append(node.value) + + Creates a list of values of all the constant nodes + encountered below the given node. To use it: + + cv = ConstantVisitor() + cv.visit(node) + + Notes: + + * generic_visit() will be called for AST nodes for which + no visit_XXX method was defined. + * The children of nodes for which a visit_XXX was + defined will not be visited - if you need this, call + generic_visit() on the node. + You can use: + NodeVisitor.generic_visit(self, node) + * Modeled after Python's own AST visiting facilities + (the ast module of Python 3.0) + """ + def visit(self, node): + """ Visit a node. + """ + method = 'visit_' + node.__class__.__name__ + visitor = getattr(self, method, self.generic_visit) + return visitor(node) + + def generic_visit(self, node): + """ Called if no explicit visitor function exists for a + node. Implements preorder visiting of the node. + """ + for c in node.children(): + self.visit(c) + + +class Typedef(Node): + def __init__(self, name, quals, storage, type, coord=None): + self.name = name + self.quals = quals + self.storage = storage + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(self.type) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Typedef: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name)), ("quals", repr(self.quals)), ("storage", repr(self.storage))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name, self.quals, self.storage]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Struct(Node): + def __init__(self, name, decls, coord=None): + self.name = name + self.decls = decls + self.coord = coord + + def children(self): + nodelist = [] + if self.decls is not None: nodelist.extend(self.decls) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Struct: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class FuncCall(Node): + def __init__(self, name, args, coord=None): + self.name = name + self.args = args + self.coord = coord + + def children(self): + nodelist = [] + if self.name is not None: nodelist.append(self.name) + if self.args is not None: nodelist.append(self.args) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'FuncCall: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class UnaryOp(Node): + def __init__(self, op, expr, coord=None): + self.op = op + self.expr = expr + self.coord = coord + + def children(self): + nodelist = [] + if self.expr is not None: nodelist.append(self.expr) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'UnaryOp: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("op", repr(self.op))]) + else: + attrstr = ', '.join('%s' % v for v in [self.op]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Union(Node): + def __init__(self, name, decls, coord=None): + self.name = name + self.decls = decls + self.coord = coord + + def children(self): + nodelist = [] + if self.decls is not None: nodelist.extend(self.decls) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Union: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class TernaryOp(Node): + def __init__(self, cond, iftrue, iffalse, coord=None): + self.cond = cond + self.iftrue = iftrue + self.iffalse = iffalse + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(self.cond) + if self.iftrue is not None: nodelist.append(self.iftrue) + if self.iffalse is not None: nodelist.append(self.iffalse) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'TernaryOp: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Label(Node): + def __init__(self, name, stmt, coord=None): + self.name = name + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.stmt is not None: nodelist.append(self.stmt) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Label: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class IdentifierType(Node): + def __init__(self, names, coord=None): + self.names = names + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'IdentifierType: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("names", repr(self.names))]) + else: + attrstr = ', '.join('%s' % v for v in [self.names]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class FuncDef(Node): + def __init__(self, decl, param_decls, body, coord=None): + self.decl = decl + self.param_decls = param_decls + self.body = body + self.coord = coord + + def children(self): + nodelist = [] + if self.decl is not None: nodelist.append(self.decl) + if self.body is not None: nodelist.append(self.body) + if self.param_decls is not None: nodelist.extend(self.param_decls) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'FuncDef: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Enumerator(Node): + def __init__(self, name, value, coord=None): + self.name = name + self.value = value + self.coord = coord + + def children(self): + nodelist = [] + if self.value is not None: nodelist.append(self.value) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Enumerator: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class For(Node): + def __init__(self, init, cond, next, stmt, coord=None): + self.init = init + self.cond = cond + self.next = next + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.init is not None: nodelist.append(self.init) + if self.cond is not None: nodelist.append(self.cond) + if self.next is not None: nodelist.append(self.next) + if self.stmt is not None: nodelist.append(self.stmt) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'For: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Assignment(Node): + def __init__(self, op, lvalue, rvalue, coord=None): + self.op = op + self.lvalue = lvalue + self.rvalue = rvalue + self.coord = coord + + def children(self): + nodelist = [] + if self.lvalue is not None: nodelist.append(self.lvalue) + if self.rvalue is not None: nodelist.append(self.rvalue) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Assignment: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("op", repr(self.op))]) + else: + attrstr = ', '.join('%s' % v for v in [self.op]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class FuncDecl(Node): + def __init__(self, args, type, coord=None): + self.args = args + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.args is not None: nodelist.append(self.args) + if self.type is not None: nodelist.append(self.type) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'FuncDecl: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Enum(Node): + def __init__(self, name, values, coord=None): + self.name = name + self.values = values + self.coord = coord + + def children(self): + nodelist = [] + if self.values is not None: nodelist.append(self.values) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Enum: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class ExprList(Node): + def __init__(self, exprs, coord=None): + self.exprs = exprs + self.coord = coord + + def children(self): + nodelist = [] + if self.exprs is not None: nodelist.extend(self.exprs) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'ExprList: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Break(Node): + def __init__(self, coord=None): + self.coord = coord + + def children(self): + return () + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Break: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class DoWhile(Node): + def __init__(self, cond, stmt, coord=None): + self.cond = cond + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(self.cond) + if self.stmt is not None: nodelist.append(self.stmt) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'DoWhile: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class StructRef(Node): + def __init__(self, name, type, field, coord=None): + self.name = name + self.type = type + self.field = field + self.coord = coord + + def children(self): + nodelist = [] + if self.name is not None: nodelist.append(self.name) + if self.field is not None: nodelist.append(self.field) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'StructRef: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("type", repr(self.type))]) + else: + attrstr = ', '.join('%s' % v for v in [self.type]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class BinaryOp(Node): + def __init__(self, op, left, right, coord=None): + self.op = op + self.left = left + self.right = right + self.coord = coord + + def children(self): + nodelist = [] + if self.left is not None: nodelist.append(self.left) + if self.right is not None: nodelist.append(self.right) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'BinaryOp: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("op", repr(self.op))]) + else: + attrstr = ', '.join('%s' % v for v in [self.op]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Compound(Node): + def __init__(self, decls, stmts, coord=None): + self.decls = decls + self.stmts = stmts + self.coord = coord + + def children(self): + nodelist = [] + if self.decls is not None: nodelist.extend(self.decls) + if self.stmts is not None: nodelist.extend(self.stmts) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Compound: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class ArrayDecl(Node): + def __init__(self, type, dim, coord=None): + self.type = type + self.dim = dim + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(self.type) + if self.dim is not None: nodelist.append(self.dim) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'ArrayDecl: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Case(Node): + def __init__(self, expr, stmt, coord=None): + self.expr = expr + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.expr is not None: nodelist.append(self.expr) + if self.stmt is not None: nodelist.append(self.stmt) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Case: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Cast(Node): + def __init__(self, to_type, expr, coord=None): + self.to_type = to_type + self.expr = expr + self.coord = coord + + def children(self): + nodelist = [] + if self.to_type is not None: nodelist.append(self.to_type) + if self.expr is not None: nodelist.append(self.expr) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Cast: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class TypeDecl(Node): + def __init__(self, declname, quals, type, coord=None): + self.declname = declname + self.quals = quals + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(self.type) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'TypeDecl: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("declname", repr(self.declname)), ("quals", repr(self.quals))]) + else: + attrstr = ', '.join('%s' % v for v in [self.declname, self.quals]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Default(Node): + def __init__(self, stmt, coord=None): + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.stmt is not None: nodelist.append(self.stmt) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Default: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class PtrDecl(Node): + def __init__(self, quals, type, coord=None): + self.quals = quals + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(self.type) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'PtrDecl: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("quals", repr(self.quals))]) + else: + attrstr = ', '.join('%s' % v for v in [self.quals]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Switch(Node): + def __init__(self, cond, stmt, coord=None): + self.cond = cond + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(self.cond) + if self.stmt is not None: nodelist.append(self.stmt) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Switch: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Continue(Node): + def __init__(self, coord=None): + self.coord = coord + + def children(self): + return () + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Continue: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class ParamList(Node): + def __init__(self, params, coord=None): + self.params = params + self.coord = coord + + def children(self): + nodelist = [] + if self.params is not None: nodelist.extend(self.params) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'ParamList: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Return(Node): + def __init__(self, expr, coord=None): + self.expr = expr + self.coord = coord + + def children(self): + nodelist = [] + if self.expr is not None: nodelist.append(self.expr) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Return: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Typename(Node): + def __init__(self, quals, type, coord=None): + self.quals = quals + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(self.type) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Typename: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("quals", repr(self.quals))]) + else: + attrstr = ', '.join('%s' % v for v in [self.quals]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class ID(Node): + def __init__(self, name, coord=None): + self.name = name + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'ID: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Goto(Node): + def __init__(self, name, coord=None): + self.name = name + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Goto: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Decl(Node): + def __init__(self, name, quals, storage, type, init, bitsize, coord=None): + self.name = name + self.quals = quals + self.storage = storage + self.type = type + self.init = init + self.bitsize = bitsize + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(self.type) + if self.init is not None: nodelist.append(self.init) + if self.bitsize is not None: nodelist.append(self.bitsize) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Decl: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("name", repr(self.name)), ("quals", repr(self.quals)), ("storage", repr(self.storage))]) + else: + attrstr = ', '.join('%s' % v for v in [self.name, self.quals, self.storage]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class Constant(Node): + def __init__(self, type, value, coord=None): + self.type = type + self.value = value + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'Constant: ') + + if attrnames: + attrstr = ', '.join('%s=%s' % nv for nv in [("type", repr(self.type)), ("value", repr(self.value))]) + else: + attrstr = ', '.join('%s' % v for v in [self.type, self.value]) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class FileAST(Node): + def __init__(self, ext, coord=None): + self.ext = ext + self.coord = coord + + def children(self): + nodelist = [] + if self.ext is not None: nodelist.extend(self.ext) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'FileAST: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class ArrayRef(Node): + def __init__(self, name, subscript, coord=None): + self.name = name + self.subscript = subscript + self.coord = coord + + def children(self): + nodelist = [] + if self.name is not None: nodelist.append(self.name) + if self.subscript is not None: nodelist.append(self.subscript) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'ArrayRef: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class While(Node): + def __init__(self, cond, stmt, coord=None): + self.cond = cond + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(self.cond) + if self.stmt is not None: nodelist.append(self.stmt) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'While: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class EnumeratorList(Node): + def __init__(self, enumerators, coord=None): + self.enumerators = enumerators + self.coord = coord + + def children(self): + nodelist = [] + if self.enumerators is not None: nodelist.extend(self.enumerators) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'EnumeratorList: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class EllipsisParam(Node): + def __init__(self, coord=None): + self.coord = coord + + def children(self): + return () + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'EllipsisParam: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + +class If(Node): + def __init__(self, cond, iftrue, iffalse, coord=None): + self.cond = cond + self.iftrue = iftrue + self.iffalse = iffalse + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(self.cond) + if self.iftrue is not None: nodelist.append(self.iftrue) + if self.iffalse is not None: nodelist.append(self.iffalse) + return tuple(nodelist) + + def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False): + lead = ' ' * offset + buf.write(lead + 'If: ') + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for c in self.children(): + c.show(buf, offset + 2, attrnames, showcoord) + + diff --git a/plugins_available/pycparser/pycparser/c_lexer.py b/plugins_available/pycparser/pycparser/c_lexer.py new file mode 100644 index 0000000..28638df --- /dev/null +++ b/plugins_available/pycparser/pycparser/c_lexer.py @@ -0,0 +1,443 @@ +#----------------------------------------------------------------- +# pycparser: clex.py +# +# CLexer class: lexer for the C language +# +# Copyright (C) 2008, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + +import re +import sys + +import ply.lex +from ply.lex import TOKEN + + +class CLexer(object): + """ A lexer for the C language. After building it, set the + input text with input(), and call token() to get new + tokens. + + The public attribute filename can be set to an initial + filaneme, but the lexer will update it upon #line + directives. + """ + def __init__(self, error_func, type_lookup_func): + """ Create a new Lexer. + + error_func: + An error function. Will be called with an error + message, line and column as arguments, in case of + an error during lexing. + + type_lookup_func: + A type lookup function. Given a string, it must + return True IFF this string is a name of a type + that was defined with a typedef earlier. + """ + self.error_func = error_func + self.type_lookup_func = type_lookup_func + self.filename = '' + + # Allow either "# line" or "# " to support GCC's + # cpp output + # + self.line_pattern = re.compile('([ \t]*line\W)|([ \t]*\d+)') + + def build(self, **kwargs): + """ Builds the lexer from the specification. Must be + called after the lexer object is created. + + This method exists separately, because the PLY + manual warns against calling lex.lex inside + __init__ + """ + self.lexer = ply.lex.lex(object=self, **kwargs) + + def reset_lineno(self): + """ Resets the internal line number counter of the lexer. + """ + self.lexer.lineno = 1 + + def input(self, text): + self.lexer.input(text) + + def token(self): + g = self.lexer.token() + return g + + ######################-- PRIVATE --###################### + + ## + ## Internal auxiliary methods + ## + def _error(self, msg, token): + location = self._make_tok_location(token) + self.error_func(msg, location[0], location[1]) + self.lexer.skip(1) + + def _find_tok_column(self, token): + i = token.lexpos + while i > 0: + if self.lexer.lexdata[i] == '\n': break + i -= 1 + return (token.lexpos - i) + 1 + + def _make_tok_location(self, token): + return (token.lineno, self._find_tok_column(token)) + + ## + ## Reserved keywords + ## + keywords = ( + 'AUTO', 'BREAK', 'CASE', 'CHAR', 'CONST', 'CONTINUE', + 'DEFAULT', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN', + 'FLOAT', 'FOR', 'GOTO', 'IF', 'INT', 'LONG', 'REGISTER', + 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT', + 'SWITCH', 'TYPEDEF', 'UNION', 'UNSIGNED', 'VOID', + 'VOLATILE', 'WHILE', + ) + + keyword_map = {} + for r in keywords: + keyword_map[r.lower()] = r + + ## + ## All the tokens recognized by the lexer + ## + tokens = keywords + ( + # Identifiers + 'ID', + + # Type identifiers (identifiers previously defined as + # types with typedef) + 'TYPEID', + + # constants + 'INT_CONST_DEC', 'INT_CONST_OCT', 'INT_CONST_HEX', + 'FLOAT_CONST', + 'CHAR_CONST', + 'WCHAR_CONST', + + # String literals + 'STRING_LITERAL', + 'WSTRING_LITERAL', + + # Operators + 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MOD', + 'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT', + 'LOR', 'LAND', 'LNOT', + 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', + + # Assignment + 'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', + 'PLUSEQUAL', 'MINUSEQUAL', + 'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', + 'OREQUAL', + + # Increment/decrement + 'PLUSPLUS', 'MINUSMINUS', + + # Structure dereference (->) + 'ARROW', + + # Conditional operator (?) + 'CONDOP', + + # Delimeters + 'LPAREN', 'RPAREN', # ( ) + 'LBRACKET', 'RBRACKET', # [ ] + 'LBRACE', 'RBRACE', # { } + 'COMMA', 'PERIOD', # . , + 'SEMI', 'COLON', # ; : + + # Ellipsis (...) + 'ELLIPSIS', + + # pre-processor + 'PPHASH', # '#' + ) + + ## + ## Regexes for use in tokens + ## + ## + + # valid C identifiers (K&R2: A.2.3) + identifier = r'[a-zA-Z_][0-9a-zA-Z_]*' + + # integer constants (K&R2: A.2.5.1) + integer_suffix_opt = r'(([uU][lL])|([lL][uU])|[uU]|[lL])?' + decimal_constant = '(0'+integer_suffix_opt+')|([1-9][0-9]*'+integer_suffix_opt+')' + octal_constant = '0[0-7]*'+integer_suffix_opt + hex_constant = '0[xX][0-9a-fA-F]+'+integer_suffix_opt + + bad_octal_constant = '0[0-7]*[89]' + + # character constants (K&R2: A.2.5.2) + # Note: a-zA-Z are allowed as escape chars to support #line + # directives with Windows paths as filenames (\dir\file...) + # + simple_escape = r"""([a-zA-Z\\?'"])""" + octal_escape = r"""([0-7]{1,3})""" + hex_escape = r"""(x[0-9a-fA-F]+)""" + bad_escape = r"""([\\][^a-zA-Z\\?'"x0-7])""" + + escape_sequence = r"""(\\("""+simple_escape+'|'+octal_escape+'|'+hex_escape+'))' + cconst_char = r"""([^'\\\n]|"""+escape_sequence+')' + char_const = "'"+cconst_char+"'" + wchar_const = 'L'+char_const + unmatched_quote = "('"+cconst_char+"*\\n)|('"+cconst_char+"*$)" + bad_char_const = r"""('"""+cconst_char+"""[^'\n]+')|('')|('"""+bad_escape+r"""[^'\n]*')""" + + # string literals (K&R2: A.2.6) + string_char = r"""([^"\\\n]|"""+escape_sequence+')' + string_literal = '"'+string_char+'*"' + wstring_literal = 'L'+string_literal + bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"' + + # floating constants (K&R2: A.2.5.3) + exponent_part = r"""([eE][-+]?[0-9]+)""" + fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)""" + floating_constant = '(((('+fractional_constant+')'+exponent_part+'?)|([0-9]+'+exponent_part+'))[FfLl]?)' + + ## + ## Lexer states + ## + states = ( + # ppline: preprocessor line directives + # + ('ppline', 'exclusive'), + ) + + def t_PPHASH(self, t): + r'[ \t]*\#' + m = self.line_pattern.match( + t.lexer.lexdata, pos=t.lexer.lexpos) + + if m: + t.lexer.begin('ppline') + self.pp_line = self.pp_filename = None + #~ print "ppline starts on line %s" % t.lexer.lineno + else: + t.type = 'PPHASH' + return t + + ## + ## Rules for the ppline state + ## + @TOKEN(string_literal) + def t_ppline_FILENAME(self, t): + if self.pp_line is None: + self._error('filename before line number in #line', t) + else: + self.pp_filename = t.value.lstrip('"').rstrip('"') + #~ print "PP got filename: ", self.pp_filename + + @TOKEN(decimal_constant) + def t_ppline_LINE_NUMBER(self, t): + if self.pp_line is None: + self.pp_line = t.value + else: + # Ignore: GCC's cpp sometimes inserts a numeric flag + # after the file name + pass + + def t_ppline_NEWLINE(self, t): + r'\n' + + if self.pp_line is None: + self._error('line number missing in #line', t) + else: + self.lexer.lineno = int(self.pp_line) + + if self.pp_filename is not None: + self.filename = self.pp_filename + + t.lexer.begin('INITIAL') + + def t_ppline_PPLINE(self, t): + r'line' + pass + + t_ppline_ignore = ' \t' + + def t_ppline_error(self, t): + msg = 'invalid #line directive' + self._error(msg, t) + + ## + ## Rules for the normal state + ## + t_ignore = ' \t' + + # Newlines + def t_NEWLINE(self, t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + + # Operators + t_PLUS = r'\+' + t_MINUS = r'-' + t_TIMES = r'\*' + t_DIVIDE = r'/' + t_MOD = r'%' + t_OR = r'\|' + t_AND = r'&' + t_NOT = r'~' + t_XOR = r'\^' + t_LSHIFT = r'<<' + t_RSHIFT = r'>>' + t_LOR = r'\|\|' + t_LAND = r'&&' + t_LNOT = r'!' + t_LT = r'<' + t_GT = r'>' + t_LE = r'<=' + t_GE = r'>=' + t_EQ = r'==' + t_NE = r'!=' + + # Assignment operators + t_EQUALS = r'=' + t_TIMESEQUAL = r'\*=' + t_DIVEQUAL = r'/=' + t_MODEQUAL = r'%=' + t_PLUSEQUAL = r'\+=' + t_MINUSEQUAL = r'-=' + t_LSHIFTEQUAL = r'<<=' + t_RSHIFTEQUAL = r'>>=' + t_ANDEQUAL = r'&=' + t_OREQUAL = r'\|=' + t_XOREQUAL = r'^=' + + # Increment/decrement + t_PLUSPLUS = r'\+\+' + t_MINUSMINUS = r'--' + + # -> + t_ARROW = r'->' + + # ? + t_CONDOP = r'\?' + + # Delimeters + t_LPAREN = r'\(' + t_RPAREN = r'\)' + t_LBRACKET = r'\[' + t_RBRACKET = r'\]' + t_LBRACE = r'\{' + t_RBRACE = r'\}' + t_COMMA = r',' + t_PERIOD = r'\.' + t_SEMI = r';' + t_COLON = r':' + t_ELLIPSIS = r'\.\.\.' + + t_STRING_LITERAL = string_literal + + # The following floating and integer constants are defined as + # functions to impose a strict order (otherwise, decimal + # is placed before the others because its regex is longer, + # and this is bad) + # + @TOKEN(floating_constant) + def t_FLOAT_CONST(self, t): + return t + + @TOKEN(hex_constant) + def t_INT_CONST_HEX(self, t): + return t + + @TOKEN(bad_octal_constant) + def t_BAD_CONST_OCT(self, t): + msg = "Invalid octal constant" + self._error(msg, t) + + @TOKEN(octal_constant) + def t_INT_CONST_OCT(self, t): + return t + + @TOKEN(decimal_constant) + def t_INT_CONST_DEC(self, t): + return t + + # Must come before bad_char_const, to prevent it from + # catching valid char constants as invalid + # + @TOKEN(char_const) + def t_CHAR_CONST(self, t): + return t + + @TOKEN(wchar_const) + def t_WCHAR_CONST(self, t): + return t + + @TOKEN(unmatched_quote) + def t_UNMATCHED_QUOTE(self, t): + msg = "Unmatched '" + self._error(msg, t) + + @TOKEN(bad_char_const) + def t_BAD_CHAR_CONST(self, t): + msg = "Invalid char constant %s" % t.value + self._error(msg, t) + + @TOKEN(wstring_literal) + def t_WSTRING_LITERAL(self, t): + return t + + # unmatched string literals are caught by the preprocessor + + @TOKEN(bad_string_literal) + def t_BAD_STRING_LITERAL(self, t): + msg = "String contains invalid escape code" + self._error(msg, t) + + @TOKEN(identifier) + def t_ID(self, t): + t.type = self.keyword_map.get(t.value, "ID") + + if t.type == 'ID' and self.type_lookup_func(t.value): + t.type = "TYPEID" + + return t + + def t_error(self, t): + msg = 'Illegal character %s' % repr(t.value[0]) + self._error(msg, t) + + +if __name__ == "__main__": + filename = '../zp.c' + text = open(filename).read() + + #~ text = '"'+r"""ka \p ka"""+'"' + text = r""" + 546 + #line 66 "kwas\df.h" + id 4 + # 5 + dsf + """ + + def errfoo(msg, a, b): + print msg + sys.exit() + + def typelookup(namd): + return False + + clex = CLexer(errfoo, typelookup) + clex.build() + clex.input(text) + + while 1: + tok = clex.token() + if not tok: break + + #~ print type(tok) + print "-", tok.value, tok.type, tok.lineno, clex.filename, tok.lexpos + + diff --git a/plugins_available/pycparser/pycparser/c_parser.py b/plugins_available/pycparser/pycparser/c_parser.py new file mode 100644 index 0000000..6c03660 --- /dev/null +++ b/plugins_available/pycparser/pycparser/c_parser.py @@ -0,0 +1,1259 @@ +#----------------------------------------------------------------- +# pycparser: cparse.py +# +# CParser class: Parser and AST builder for the C language +# +# Copyright (C) 2008, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + +import re +import sys +from types import StringType + +import ply.yacc + +import c_ast +from c_lexer import CLexer +from plyparser import PLYParser, Coord, ParseError + + +class CParser(PLYParser): + def __init__( + self, + lex_optimize=True, + lextab='pycparser.lextab', + yacc_optimize=True, + yacctab='pycparser.yacctab', + yacc_debug=False): + """ Create a new CParser. + + Some arguments for controlling the debug/optimization + level of the parser are provided. The defaults are + tuned for release/performance mode. + The simple rules for using them are: + *) When tweaking CParser/CLexer, set these to False + *) When releasing a stable parser, set to True + + lex_optimize: + Set to False when you're modifying the lexer. + Otherwise, changes in the lexer won't be used, if + some lextab.py file exists. + When releasing with a stable lexer, set to True + to save the re-generation of the lexer table on + each run. + + lextab: + Points to the lex table that's used for optimized + mode. Only if you're modifying the lexer and want + some tests to avoid re-generating the table, make + this point to a local lex table file (that's been + earlier generated with lex_optimize=True) + + yacc_optimize: + Set to False when you're modifying the parser. + Otherwise, changes in the parser won't be used, if + some parsetab.py file exists. + When releasing with a stable parser, set to True + to save the re-generation of the parser table on + each run. + + yacctab: + Points to the yacc table that's used for optimized + mode. Only if you're modifying the parser, make + this point to a local yacc table file + + yacc_debug: + Generate a parser.out file that explains how yacc + built the parsing table from the grammar. + """ + self.clex = CLexer( + error_func=self._lex_error_func, + type_lookup_func=self._lex_type_lookup_func) + + self.clex.build( + optimize=lex_optimize, + lextab=lextab) + self.tokens = self.clex.tokens + + rules_with_opt = [ + 'abstract_declarator', + 'constant_expression', + 'declaration_list', + 'declaration_specifiers', + 'expression', + 'identifier_list', + 'init_declarator_list', + 'parameter_type_list', + 'specifier_qualifier_list', + 'statement_list', + 'type_qualifier_list', + ] + + for rule in rules_with_opt: + self._create_opt_rule(rule) + + self.cparser = ply.yacc.yacc( + module=self, + start='translation_unit', + debug=yacc_debug, + optimize=yacc_optimize, + tabmodule=yacctab) + + # A table of identifiers defined as typedef types during + # parsing. + # + self.typedef_table = set([]) + + def parse(self, text, filename='', debuglevel=0): + """ Parses C code and returns an AST. + + text: + A string containing the C source code + + filename: + Name of the file being parsed (for meaningful + error messages) + + debuglevel: + Debug level to yacc + """ + self.clex.filename = filename + self.clex.reset_lineno() + self.typedef_table = set([]) + return self.cparser.parse(text, lexer=self.clex, debug=debuglevel) + + ######################-- PRIVATE --###################### + + def _lex_error_func(self, msg, line, column): + self._parse_error(msg, self._coord(line, column)) + + def _lex_type_lookup_func(self, name): + """ Looks up types that were previously defined with + typedef. + Passed to the lexer for recognizing identifiers that + are types. + """ + return name in self.typedef_table + + def _add_typedef_type(self, name): + """ Adds names that were defined as new types with + typedef. + """ + self.typedef_table.add(name) + + # To understand what's going on here, read sections A.8.5 and + # A.8.6 of K&R2 very carefully. + # + # A C type consists of a basic type declaration, with a list + # of modifiers. For example: + # + # int *c[5]; + # + # The basic declaration here is 'int x', and the pointer and + # the array are the modifiers. + # + # Basic declarations are represented by TypeDecl (from module + # c_ast) and the modifiers are FuncDecl, PtrDecl and + # ArrayDecl. + # + # The standard states that whenever a new modifier is parsed, + # it should be added to the end of the list of modifiers. For + # example: + # + # K&R2 A.8.6.2: Array Declarators + # + # In a declaration T D where D has the form + # D1 [constant-expression-opt] + # and the type of the identifier in the declaration T D1 is + # "type-modifier T", the type of the + # identifier of D is "type-modifier array of T" + # + # This is what this method does. The declarator it receives + # can be a list of declarators ending with TypeDecl. It + # tacks the modifier to the end of this list, just before + # the TypeDecl. + # + # Additionally, the modifier may be a list itself. This is + # useful for pointers, that can come as a chain from the rule + # p_pointer. In this case, the whole modifier list is spliced + # into the new location. + # + def _type_modify_decl(self, decl, modifier): + """ Tacks a type modifier on a declarator, and returns + the modified declarator. + + Note: the declarator and modifier may be modified + """ + #~ print '****' + #~ decl.show(offset=3) + #~ modifier.show(offset=3) + #~ print '****' + + modifier_head = modifier + modifier_tail = modifier + + # The modifier may be a nested list. Reach its tail. + # + while modifier_tail.type: + modifier_tail = modifier_tail.type + + # If the decl is a basic type, just tack the modifier onto + # it + # + if isinstance(decl, c_ast.TypeDecl): + modifier_tail.type = decl + return modifier + else: + # Otherwise, the decl is a list of modifiers. Reach + # its tail and splice the modifier onto the tail, + # pointing to the underlying basic type. + # + decl_tail = decl + + while not isinstance(decl_tail.type, c_ast.TypeDecl): + decl_tail = decl_tail.type + + modifier_tail.type = decl_tail.type + decl_tail.type = modifier_head + return decl + + # Due to the order in which declarators are constructed, + # they have to be fixed in order to look like a normal AST. + # + # When a declaration arrives from syntax construction, it has + # these problems: + # * The innermost TypeDecl has no type (because the basic + # type is only known at the uppermost declaration level) + # * The declaration has no variable name, since that is saved + # in the innermost TypeDecl + # * The typename of the declaration is a list of type + # specifiers, and not a node. Here, basic identifier types + # should be separated from more complex types like enums + # and structs. + # + # This method fixes these problem. + # + def _fix_decl_name_type(self, decl, typename): + """ Fixes a declaration. Modifies decl. + """ + # Reach the underlying basic type + # + type = decl + while not isinstance(type, c_ast.TypeDecl): + type = type.type + + decl.name = type.declname + type.quals = decl.quals + + # The typename is a list of types. If any type in this + # list isn't a simple string type, it must be the only + # type in the list (it's illegal to declare "int enum .." + # If all the types are basic, they're collected in the + # IdentifierType holder. + # + for tn in typename: + if not isinstance(tn, StringType): + if len(typename) > 1: + self._parse_error( + "Invalid multiple types specified", tn.coord) + else: + type.type = tn + return decl + + type.type = c_ast.IdentifierType(typename) + return decl + + def _add_declaration_specifier(self, declspec, newspec, kind): + """ Declaration specifiers are represented by a dictionary + with 3 entries: + * qual: a list of type qualifiers + * storage: a list of storage type qualifiers + * type: a list of type specifiers + + This method is given a declaration specifier, and a + new specifier of a given kind. + Returns the declaration specifier, with the new + specifier incorporated. + """ + spec = declspec or dict(qual=[], storage=[], type=[]) + spec[kind].append(newspec) + return spec + + def _build_function_definition(self, decl, spec, param_decls, body): + """ Builds a function definition. + """ + declaration = c_ast.Decl( + name=None, + quals=spec['qual'], + storage=spec['storage'], + type=decl, + init=None, + bitsize=None, + coord=decl.coord) + + typename = spec['type'] + declaration = self._fix_decl_name_type(declaration, typename) + return c_ast.FuncDef( + decl=declaration, + param_decls=param_decls, + body=body, + coord=decl.coord) + + def _select_struct_union_class(self, token): + """ Given a token (either STRUCT or UNION), selects the + appropriate AST class. + """ + if token == 'struct': + return c_ast.Struct + else: + return c_ast.Union + + ## + ## Precedence and associativity of operators + ## + precedence = ( + ('left', 'LOR'), + ('left', 'LAND'), + ('left', 'OR'), + ('left', 'XOR'), + ('left', 'AND'), + ('left', 'EQ', 'NE'), + ('left', 'GT', 'GE', 'LT', 'LE'), + ('left', 'RSHIFT', 'LSHIFT'), + ('left', 'PLUS', 'MINUS'), + ('left', 'TIMES', 'DIVIDE', 'MOD') + ) + + ## + ## Grammar productions + ## Implementation of the BNF defined in K&R2 A.13 + ## + def p_translation_unit_1(self, p): + """ translation_unit : external_declaration + """ + # Note: external_declaration is already a list + # + p[0] = c_ast.FileAST(p[1]) + + def p_translation_unit_2(self, p): + """ translation_unit : translation_unit external_declaration + """ + p[1].ext.extend(p[2]) + p[0] = p[1] + + # Declarations always come as lists (because they can be + # several in one line), so we wrap the function definition + # into a list as well, to make the return value of + # external_declaration homogenous. + # + def p_external_declaration_1(self, p): + """ external_declaration : function_definition + """ + p[0] = [p[1]] + + def p_external_declaration_2(self, p): + """ external_declaration : declaration + """ + p[0] = p[1] + + def p_external_declaration_3(self, p): + """ external_declaration : pp_directive + """ + p[0] = p[1] + + def p_pp_directive(self, p): + """ pp_directive : PPHASH + """ + self._parse_error('Directives not supported yet', + self._coord(p.lineno(1))) + + # In function definitions, the declarator can be followed by + # a declaration list, for old "K&R style" function definitios. + # + def p_function_definition_1(self, p): + """ function_definition : declarator declaration_list_opt compound_statement + """ + # no declaration specifiers + spec = dict(qual=[], storage=[], type=[]) + + p[0] = self._build_function_definition( + decl=p[1], + spec=spec, + param_decls=p[2], + body=p[3]) + + def p_function_definition_2(self, p): + """ function_definition : declaration_specifiers declarator declaration_list_opt compound_statement + """ + spec = p[1] + + p[0] = self._build_function_definition( + decl=p[2], + spec=spec, + param_decls=p[3], + body=p[4]) + + def p_statement(self, p): + """ statement : labeled_statement + | expression_statement + | compound_statement + | selection_statement + | iteration_statement + | jump_statement + """ + p[0] = p[1] + + # In C, declarations can come several in a line: + # int x, *px, romulo = 5; + # + # However, for the AST, we will split them to separate Decl + # nodes. + # + # This rule splits its declarations and always returns a list + # of Decl nodes, even if it's one element long. + # + def p_decl_body(self, p): + """ decl_body : declaration_specifiers init_declarator_list_opt + """ + spec = p[1] + is_typedef = 'typedef' in spec['storage'] + decls = [] + + # p[2] (init_declarator_list_opt) is either a list or None + # + if p[2] is None: + # Then it's a declaration of a struct / enum tag, + # without an actual declarator. + # + type = spec['type'] + if len(type) > 1: + coord = '?' + for t in type: + if hasattr(t, 'coord'): + coord = t.coord + break + + self._parse_error('Multiple type specifiers with a type tag', coord) + + decl = c_ast.Decl( + name=None, + quals=spec['qual'], + storage=spec['storage'], + type=type[0], + init=None, + bitsize=None, + coord=type[0].coord) + decls = [decl] + else: + for decl, init in p[2] or []: + if is_typedef: + decl = c_ast.Typedef( + name=None, + quals=spec['qual'], + storage=spec['storage'], + type=decl, + coord=decl.coord) + else: + decl = c_ast.Decl( + name=None, + quals=spec['qual'], + storage=spec['storage'], + type=decl, + init=init, + bitsize=None, + coord=decl.coord) + + typename = spec['type'] + fixed_decl = self._fix_decl_name_type(decl, typename) + + # Add the type name defined by typedef to a + # symbol table (for usage in the lexer) + # + if is_typedef: + self._add_typedef_type(fixed_decl.name) + + decls.append(fixed_decl) + + p[0] = decls + + # The declaration has been split to a decl_body sub-rule and + # SEMI, because having them in a single rule created a problem + # for defining typedefs. + # + # If a typedef line was directly followed by a line using the + # type defined with the typedef, the type would not be + # recognized. This is because to reduce the declaration rule, + # the parser's lookahead asked for the token after SEMI, which + # was the type from the next line, and the lexer had no chance + # to see the updated type symbol table. + # + # Splitting solves this problem, because after seeing SEMI, + # the parser reduces decl_body, which actually adds the new + # type into the table to be seen by the lexer before the next + # line is reached. + # + def p_declaration(self, p): + """ declaration : decl_body SEMI + """ + p[0] = p[1] + + # Since each declaration is a list of declarations, this + # rule will combine all the declarations and return a single + # list + # + def p_declaration_list(self, p): + """ declaration_list : declaration + | declaration_list declaration + """ + p[0] = p[1] if len(p) == 2 else p[1] + p[2] + + def p_declaration_specifiers_1(self, p): + """ declaration_specifiers : type_qualifier declaration_specifiers_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'qual') + + def p_declaration_specifiers_2(self, p): + """ declaration_specifiers : type_specifier declaration_specifiers_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'type') + + def p_declaration_specifiers_3(self, p): + """ declaration_specifiers : storage_class_specifier declaration_specifiers_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'storage') + + def p_storage_class_specifier(self, p): + """ storage_class_specifier : AUTO + | REGISTER + | STATIC + | EXTERN + | TYPEDEF + """ + p[0] = p[1] + + def p_type_specifier_1(self, p): + """ type_specifier : VOID + | CHAR + | SHORT + | INT + | LONG + | FLOAT + | DOUBLE + | SIGNED + | UNSIGNED + | typedef_name + | enum_specifier + | struct_or_union_specifier + """ + p[0] = p[1] + + def p_type_qualifier(self, p): + """ type_qualifier : CONST + | VOLATILE + """ + p[0] = p[1] + + def p_init_declarator_list(self, p): + """ init_declarator_list : init_declarator + | init_declarator_list COMMA init_declarator + """ + p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] + + # Returns a (declarator, intializer) pair + # If there's no initializer, returns (declarator, None) + # + def p_init_declarator(self, p): + """ init_declarator : declarator + | declarator EQUALS initializer + """ + p[0] = (p[1], p[3] if len(p) > 2 else None) + + def p_specifier_qualifier_list_1(self, p): + """ specifier_qualifier_list : type_qualifier specifier_qualifier_list_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'qual') + + def p_specifier_qualifier_list_2(self, p): + """ specifier_qualifier_list : type_specifier specifier_qualifier_list_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'type') + + # TYPEID is allowed here (and in other struct/enum related tag names), because + # struct/enum tags reside in their own namespace and can be named the same as types + # + def p_struct_or_union_specifier_1(self, p): + """ struct_or_union_specifier : struct_or_union ID + | struct_or_union TYPEID + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=p[2], + decls=None, + coord=self._coord(p.lineno(2))) + + def p_struct_or_union_specifier_2(self, p): + """ struct_or_union_specifier : struct_or_union LBRACE struct_declaration_list RBRACE + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=None, + decls=p[3], + coord=self._coord(p.lineno(2))) + + def p_struct_or_union_specifier_3(self, p): + """ struct_or_union_specifier : struct_or_union ID LBRACE struct_declaration_list RBRACE + | struct_or_union TYPEID LBRACE struct_declaration_list RBRACE + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=p[2], + decls=p[4], + coord=self._coord(p.lineno(2))) + + def p_struct_or_union(self, p): + """ struct_or_union : STRUCT + | UNION + """ + p[0] = p[1] + + # Combine all declarations into a single list + # + def p_struct_declaration_list(self, p): + """ struct_declaration_list : struct_declaration + | struct_declaration_list struct_declaration + """ + p[0] = p[1] if len(p) == 2 else p[1] + p[2] + + def p_struct_declaration_1(self, p): + """ struct_declaration : specifier_qualifier_list struct_declarator_list SEMI + """ + spec = p[1] + decls = [] + + for struct_decl in p[2]: + decl = c_ast.Decl( + name=None, + quals=spec['qual'], + storage=spec['storage'], + type=struct_decl['decl'], + init=None, + bitsize=struct_decl['bitsize'], + coord=struct_decl['decl'].coord) + + typename = spec['type'] + decls.append(self._fix_decl_name_type(decl, typename)) + + p[0] = decls + + def p_struct_declarator_list(self, p): + """ struct_declarator_list : struct_declarator + | struct_declarator_list COMMA struct_declarator + """ + p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] + + # struct_declarator passes up a dict with the keys: decl (for + # the underlying declarator) and bitsize (for the bitsize) + # + def p_struct_declarator_1(self, p): + """ struct_declarator : declarator + """ + p[0] = {'decl': p[1], 'bitsize': None} + + def p_struct_declarator_2(self, p): + """ struct_declarator : declarator COLON constant_expression + | COLON constant_expression + """ + if len(p) > 3: + p[0] = {'decl': p[1], 'bitsize': p[3]} + else: + p[0] = {'decl': None, 'bitsize': p[2]} + + def p_enum_specifier_1(self, p): + """ enum_specifier : ENUM ID + | ENUM TYPEID + """ + p[0] = c_ast.Enum(p[2], None, self._coord(p.lineno(1))) + + def p_enum_specifier_2(self, p): + """ enum_specifier : ENUM LBRACE enumerator_list RBRACE + """ + p[0] = c_ast.Enum(None, p[3], self._coord(p.lineno(1))) + + def p_enum_specifier_3(self, p): + """ enum_specifier : ENUM ID LBRACE enumerator_list RBRACE + | ENUM TYPEID LBRACE enumerator_list RBRACE + """ + p[0] = c_ast.Enum(p[2], p[4], self._coord(p.lineno(1))) + + def p_enumerator_list(self, p): + """ enumerator_list : enumerator + | enumerator_list COMMA + | enumerator_list COMMA enumerator + """ + if len(p) == 2: + p[0] = c_ast.EnumeratorList([p[1]], p[1].coord) + elif len(p) == 3: + p[0] = p[1] + else: + p[1].enumerators.append(p[3]) + p[0] = p[1] + + def p_enumerator(self, p): + """ enumerator : ID + | ID EQUALS constant_expression + """ + if len(p) == 2: + p[0] = c_ast.Enumerator( + p[1], None, + self._coord(p.lineno(1))) + else: + p[0] = c_ast.Enumerator( + p[1], p[3], + self._coord(p.lineno(1))) + + def p_declarator_1(self, p): + """ declarator : direct_declarator + """ + p[0] = p[1] + + def p_declarator_2(self, p): + """ declarator : pointer direct_declarator + """ + p[0] = self._type_modify_decl(p[2], p[1]) + + def p_direct_declarator_1(self, p): + """ direct_declarator : ID + """ + p[0] = c_ast.TypeDecl( + declname=p[1], + type=None, + quals=None, + coord=self._coord(p.lineno(1))) + + def p_direct_declarator_2(self, p): + """ direct_declarator : LPAREN declarator RPAREN + """ + p[0] = p[2] + + def p_direct_declarator_3(self, p): + """ direct_declarator : direct_declarator LBRACKET constant_expression_opt RBRACKET + """ + arr = c_ast.ArrayDecl( + type=None, + dim=p[3], + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=arr) + + def p_direct_declarator_4(self, p): + """ direct_declarator : direct_declarator LPAREN parameter_type_list RPAREN + | direct_declarator LPAREN identifier_list_opt RPAREN + """ + func = c_ast.FuncDecl( + args=p[3], + type=None, + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=func) + + def p_pointer(self, p): + """ pointer : TIMES type_qualifier_list_opt + | TIMES type_qualifier_list_opt pointer + """ + coord = self._coord(p.lineno(1)) + + p[0] = c_ast.PtrDecl( + quals=p[2] or [], + type=p[3] if len(p) > 3 else None, + coord=coord) + + def p_type_qualifier_list(self, p): + """ type_qualifier_list : type_qualifier + | type_qualifier_list type_qualifier + """ + p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]] + + def p_parameter_type_list(self, p): + """ parameter_type_list : parameter_list + | parameter_list COMMA ELLIPSIS + """ + if len(p) > 2: + p[1].params.append(c_ast.EllipsisParam()) + + p[0] = p[1] + + def p_parameter_list(self, p): + """ parameter_list : parameter_declaration + | parameter_list COMMA parameter_declaration + """ + if len(p) == 2: # single parameter + p[0] = c_ast.ParamList([p[1]], p[1].coord) + else: + p[1].params.append(p[3]) + p[0] = p[1] + + def p_parameter_declaration_1(self, p): + """ parameter_declaration : declaration_specifiers declarator + """ + spec = p[1] + decl = p[2] + + decl = c_ast.Decl( + name=None, + quals=spec['qual'], + storage=spec['storage'], + type=decl, + init=None, + bitsize=None, + coord=decl.coord) + + typename = spec['type'] or ['int'] + p[0] = self._fix_decl_name_type(decl, typename) + + def p_parameter_declaration_2(self, p): + """ parameter_declaration : declaration_specifiers abstract_declarator_opt + """ + spec = p[1] + decl = c_ast.Typename( + quals=spec['qual'], + type=p[2] or c_ast.TypeDecl(None, None, None)) + + typename = spec['type'] or ['int'] + + p[0] = self._fix_decl_name_type(decl, typename) + + def p_identifier_list(self, p): + """ identifier_list : identifier + | identifier_list COMMA identifier + """ + if len(p) == 2: # single parameter + p[0] = c_ast.ParamList([p[1]], p[1].coord) + else: + p[1].params.append(p[3]) + p[0] = p[1] + + def p_initializer_1(self, p): + """ initializer : assignment_expression + """ + p[0] = p[1] + + def p_initializer_2(self, p): + """ initializer : LBRACE initializer_list RBRACE + | LBRACE initializer_list COMMA RBRACE + """ + p[0] = p[2] + + def p_initializer_list(self, p): + """ initializer_list : initializer + | initializer_list COMMA initializer + """ + if len(p) == 2: # single initializer + p[0] = c_ast.ExprList([p[1]], p[1].coord) + else: + p[1].exprs.append(p[3]) + p[0] = p[1] + + def p_type_name(self, p): + """ type_name : specifier_qualifier_list abstract_declarator_opt + """ + #~ print '==========' + #~ print p[1] + #~ print p[2] + #~ print p[2].children() + #~ print '==========' + + typename = c_ast.Typename( + quals=p[1]['qual'], + type=p[2] or c_ast.TypeDecl(None, None, None)) + + p[0] = self._fix_decl_name_type(typename, p[1]['type']) + + def p_abstract_declarator_1(self, p): + """ abstract_declarator : pointer + """ + dummytype = c_ast.TypeDecl(None, None, None) + p[0] = self._type_modify_decl( + decl=dummytype, + modifier=p[1]) + + def p_abstract_declarator_2(self, p): + """ abstract_declarator : pointer direct_abstract_declarator + """ + p[0] = self._type_modify_decl(p[2], p[1]) + + def p_abstract_declarator_3(self, p): + """ abstract_declarator : direct_abstract_declarator + """ + p[0] = p[1] + + # Creating and using direct_abstract_declarator_opt here + # instead of listing both direct_abstract_declarator and the + # lack of it in the beginning of _1 and _2 caused two + # shift/reduce errors. + # + def p_direct_abstract_declarator_1(self, p): + """ direct_abstract_declarator : LPAREN abstract_declarator RPAREN """ + p[0] = p[2] + + def p_direct_abstract_declarator_2(self, p): + """ direct_abstract_declarator : direct_abstract_declarator LBRACKET constant_expression_opt RBRACKET + """ + arr = c_ast.ArrayDecl( + type=None, + dim=p[3], + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=arr) + + def p_direct_abstract_declarator_3(self, p): + """ direct_abstract_declarator : LBRACKET constant_expression_opt RBRACKET + """ + p[0] = c_ast.ArrayDecl( + type=c_ast.TypeDecl(None, None, None), + dim=p[2], + coord=self._coord(p.lineno(1))) + + def p_direct_abstract_declarator_4(self, p): + """ direct_abstract_declarator : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN + """ + func = c_ast.FuncDecl( + args=p[3], + type=None, + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=func) + + def p_direct_abstract_declarator_5(self, p): + """ direct_abstract_declarator : LPAREN parameter_type_list_opt RPAREN + """ + p[0] = c_ast.FuncDecl( + args=p[2], + type=c_ast.TypeDecl(None, None, None), + coord=p[1].coord) + + def p_compound_statement_1(self, p): + """ compound_statement : LBRACE statement_list_opt RBRACE """ + p[0] = c_ast.Compound( + decls=None, + stmts=p[2], + coord=self._coord(p.lineno(1))) + + def p_compound_statement_2(self, p): + """ compound_statement : LBRACE declaration_list RBRACE """ + p[0] = c_ast.Compound( + decls=p[2], + stmts=None, + coord=self._coord(p.lineno(1))) + + def p_compound_statement_3(self, p): + """ compound_statement : LBRACE declaration_list statement_list RBRACE """ + #~ print '((((((' + #~ print p[2] + #~ print p[3] + #~ print '((((((' + p[0] = c_ast.Compound( + decls=p[2], + stmts=p[3], + coord=self._coord(p.lineno(1))) + + # Note: this doesn't create an AST node, but a list of AST + # nodes that will be used as the statement list of a compound + # + def p_statement_list(self, p): + """ statement_list : statement + | statement_list statement + """ + if len(p) == 2: # single expr + p[0] = [p[1]] if p[1] else [] + else: + p[0] = p[1] + ([p[2]] if p[2] else []) + + def p_labeled_statement_1(self, p): + """ labeled_statement : ID COLON statement """ + p[0] = c_ast.Label(p[1], p[3], self._coord(p.lineno(1))) + + def p_labeled_statement_2(self, p): + """ labeled_statement : CASE constant_expression COLON statement """ + p[0] = c_ast.Case(p[2], p[4], self._coord(p.lineno(1))) + + def p_labeled_statement_3(self, p): + """ labeled_statement : DEFAULT COLON statement """ + p[0] = c_ast.Default(p[3], self._coord(p.lineno(1))) + + def p_selection_statement_1(self, p): + """ selection_statement : IF LPAREN expression RPAREN statement """ + p[0] = c_ast.If(p[3], p[5], None, self._coord(p.lineno(1))) + + def p_selection_statement_2(self, p): + """ selection_statement : IF LPAREN expression RPAREN statement ELSE statement """ + p[0] = c_ast.If(p[3], p[5], p[7], self._coord(p.lineno(1))) + + def p_selection_statement_3(self, p): + """ selection_statement : SWITCH LPAREN expression RPAREN statement """ + p[0] = c_ast.Switch(p[3], p[5], self._coord(p.lineno(1))) + + def p_iteration_statement_1(self, p): + """ iteration_statement : WHILE LPAREN expression RPAREN statement """ + p[0] = c_ast.While(p[3], p[5], self._coord(p.lineno(1))) + + def p_iteration_statement_2(self, p): + """ iteration_statement : DO statement WHILE LPAREN expression RPAREN """ + p[0] = c_ast.DoWhile(p[5], p[2], self._coord(p.lineno(1))) + + def p_iteration_statement_3(self, p): + """ iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN statement """ + p[0] = c_ast.For(p[3], p[5], p[7], p[9], self._coord(p.lineno(1))) + + def p_jump_statement_1(self, p): + """ jump_statement : GOTO ID SEMI """ + p[0] = c_ast.Goto(p[2], self._coord(p.lineno(1))) + + def p_jump_statement_2(self, p): + """ jump_statement : BREAK SEMI """ + p[0] = c_ast.Break(self._coord(p.lineno(1))) + + def p_jump_statement_3(self, p): + """ jump_statement : CONTINUE SEMI """ + p[0] = c_ast.Continue(self._coord(p.lineno(1))) + + def p_jump_statement_4(self, p): + """ jump_statement : RETURN expression SEMI + | RETURN SEMI + """ + p[0] = c_ast.Return(p[2] if len(p) == 4 else None, self._coord(p.lineno(1))) + + def p_expression_statement(self, p): + """ expression_statement : expression_opt SEMI """ + p[0] = p[1] + + def p_expression(self, p): + """ expression : assignment_expression + | expression COMMA assignment_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + if not isinstance(p[1], c_ast.ExprList): + p[1] = c_ast.ExprList([p[1]], p[1].coord) + + p[1].exprs.append(p[3]) + p[0] = p[1] + + def p_typedef_name(self, p): + """ typedef_name : TYPEID """ + p[0] = p[1] + + def p_assignment_expression(self, p): + """ assignment_expression : conditional_expression + | unary_expression assignment_operator assignment_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = c_ast.Assignment(p[2], p[1], p[3], p[1].coord) + + # K&R2 defines these as many separate rules, to encode + # precedence and associativity. Why work hard ? I'll just use + # the built in precedence/associativity specification feature + # of PLY. (see precedence declaration above) + # + def p_assignment_operator(self, p): + """ assignment_operator : EQUALS + | XOREQUAL + | TIMESEQUAL + | DIVEQUAL + | MODEQUAL + | PLUSEQUAL + | MINUSEQUAL + | LSHIFTEQUAL + | RSHIFTEQUAL + | ANDEQUAL + | OREQUAL + """ + p[0] = p[1] + + def p_constant_expression(self, p): + """ constant_expression : conditional_expression """ + p[0] = p[1] + + def p_conditional_expression(self, p): + """ conditional_expression : binary_expression + | binary_expression CONDOP expression COLON conditional_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = c_ast.TernaryOp(p[1], p[3], p[5], p[1].coord) + + def p_binary_expression(self, p): + """ binary_expression : cast_expression + | binary_expression TIMES binary_expression + | binary_expression DIVIDE binary_expression + | binary_expression MOD binary_expression + | binary_expression PLUS binary_expression + | binary_expression MINUS binary_expression + | binary_expression RSHIFT binary_expression + | binary_expression LSHIFT binary_expression + | binary_expression LT binary_expression + | binary_expression LE binary_expression + | binary_expression GE binary_expression + | binary_expression GT binary_expression + | binary_expression EQ binary_expression + | binary_expression NE binary_expression + | binary_expression AND binary_expression + | binary_expression OR binary_expression + | binary_expression XOR binary_expression + | binary_expression LAND binary_expression + | binary_expression LOR binary_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = c_ast.BinaryOp(p[2], p[1], p[3], p[1].coord) + + def p_cast_expression_1(self, p): + """ cast_expression : unary_expression """ + p[0] = p[1] + + def p_cast_expression_2(self, p): + """ cast_expression : LPAREN type_name RPAREN cast_expression """ + p[0] = c_ast.Cast(p[2], p[4], p[2].coord) + + def p_unary_expression_1(self, p): + """ unary_expression : postfix_expression """ + p[0] = p[1] + + def p_unary_expression_2(self, p): + """ unary_expression : PLUSPLUS unary_expression + | MINUSMINUS unary_expression + | unary_operator cast_expression + """ + p[0] = c_ast.UnaryOp(p[1], p[2], p[2].coord) + + def p_unary_expression_3(self, p): + """ unary_expression : SIZEOF unary_expression + | SIZEOF LPAREN type_name RPAREN + """ + p[0] = c_ast.UnaryOp( + p[1], + p[2] if len(p) == 3 else p[3], + self._coord(p.lineno(1))) + + def p_unary_operator(self, p): + """ unary_operator : AND + | TIMES + | PLUS + | MINUS + | NOT + | LNOT + """ + p[0] = p[1] + + def p_postfix_exptession_1(self, p): + """ postfix_expression : primary_expression """ + p[0] = p[1] + + def p_postfix_exptession_2(self, p): + """ postfix_expression : postfix_expression LBRACKET expression RBRACKET """ + p[0] = c_ast.ArrayRef(p[1], p[3], p[1].coord) + + def p_postfix_exptession_3(self, p): + """ postfix_expression : postfix_expression LPAREN argument_expression_list RPAREN + | postfix_expression LPAREN RPAREN + """ + p[0] = c_ast.FuncCall(p[1], p[3] if len(p) == 5 else None) + + def p_postfix_expression_4(self, p): + """ postfix_expression : postfix_expression PERIOD identifier + | postfix_expression ARROW identifier + """ + p[0] = c_ast.StructRef(p[1], p[2], p[3], p[1].coord) + + def p_postfix_expression_5(self, p): + """ postfix_expression : postfix_expression PLUSPLUS + | postfix_expression MINUSMINUS + """ + p[0] = c_ast.UnaryOp('p' + p[2], p[1], p[1].coord) + + def p_primary_expression_1(self, p): + """ primary_expression : identifier """ + p[0] = p[1] + + def p_primary_expression_2(self, p): + """ primary_expression : constant """ + p[0] = p[1] + + def p_primary_expression_3(self, p): + """ primary_expression : STRING_LITERAL + | WSTRING_LITERAL + """ + p[0] = c_ast.Constant( + 'string', p[1], self._coord(p.lineno(1))) + + def p_primary_expression_4(self, p): + """ primary_expression : LPAREN expression RPAREN """ + p[0] = p[2] + + def p_argument_expression_list(self, p): + """ argument_expression_list : assignment_expression + | argument_expression_list COMMA assignment_expression + """ + if len(p) == 2: # single expr + p[0] = c_ast.ExprList([p[1]], p[1].coord) + else: + p[1].exprs.append(p[3]) + p[0] = p[1] + + def p_identifier(self, p): + """ identifier : ID """ + p[0] = c_ast.ID(p[1], self._coord(p.lineno(1))) + + def p_constant_1(self, p): + """ constant : INT_CONST_DEC + | INT_CONST_OCT + | INT_CONST_HEX + """ + p[0] = c_ast.Constant( + 'int', p[1], self._coord(p.lineno(1))) + + def p_constant_2(self, p): + """ constant : FLOAT_CONST """ + p[0] = c_ast.Constant( + 'float', p[1], self._coord(p.lineno(1))) + + def p_constant_3(self, p): + """ constant : CHAR_CONST + | WCHAR_CONST + """ + p[0] = c_ast.Constant( + 'char', p[1], self._coord(p.lineno(1))) + + def p_empty(self, p): + 'empty : ' + p[0] = None + + def p_error(self, p): + if p: + self._parse_error( + 'before: %s' % p.value, + self._coord(p.lineno)) + else: + self._parse_error('At end of input', '') + + +if __name__ == "__main__": + import pprint + import time + + t1 = time.time() + parser = CParser(lex_optimize=True, yacc_debug=True, yacc_optimize=False) + print time.time() - t1 + + buf = ''' + int (*k)(int); + ''' + + # set debuglevel to 2 for debugging + t = parser.parse(buf, 'x.c', debuglevel=0) + t.show(showcoord=True) diff --git a/plugins_available/pycparser/pycparser/ply/__init__.py b/plugins_available/pycparser/pycparser/ply/__init__.py new file mode 100644 index 0000000..853a985 --- /dev/null +++ b/plugins_available/pycparser/pycparser/ply/__init__.py @@ -0,0 +1,4 @@ +# PLY package +# Author: David Beazley (dave@dabeaz.com) + +__all__ = ['lex','yacc'] diff --git a/plugins_available/pycparser/pycparser/ply/cpp.py b/plugins_available/pycparser/pycparser/ply/cpp.py new file mode 100644 index 0000000..39f9d47 --- /dev/null +++ b/plugins_available/pycparser/pycparser/ply/cpp.py @@ -0,0 +1,898 @@ +# ----------------------------------------------------------------------------- +# cpp.py +# +# Author: David Beazley (http://www.dabeaz.com) +# Copyright (C) 2007 +# All rights reserved +# +# This module implements an ANSI-C style lexical preprocessor for PLY. +# ----------------------------------------------------------------------------- +from __future__ import generators + +# ----------------------------------------------------------------------------- +# Default preprocessor lexer definitions. These tokens are enough to get +# a basic preprocessor working. Other modules may import these if they want +# ----------------------------------------------------------------------------- + +tokens = ( + 'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT', 'CPP_POUND','CPP_DPOUND' +) + +literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\"" + +# Whitespace +def t_CPP_WS(t): + r'\s+' + t.lexer.lineno += t.value.count("\n") + return t + +t_CPP_POUND = r'\#' +t_CPP_DPOUND = r'\#\#' + +# Identifier +t_CPP_ID = r'[A-Za-z_][\w_]*' + +# Integer literal +def CPP_INTEGER(t): + r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU]|[lL]|[uU][lL]|[lL][uU])?)' + return t + +t_CPP_INTEGER = CPP_INTEGER + +# Floating literal +t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' + +# String literal +def t_CPP_STRING(t): + r'\"([^\\\n]|(\\(.|\n)))*?\"' + t.lexer.lineno += t.value.count("\n") + return t + +# Character constant 'c' or L'c' +def t_CPP_CHAR(t): + r'(L)?\'([^\\\n]|(\\(.|\n)))*?\'' + t.lexer.lineno += t.value.count("\n") + return t + +# Comment +def t_CPP_COMMENT(t): + r'(/\*(.|\n)*?\*/)|(//.*?\n)' + t.lexer.lineno += t.value.count("\n") + return t + +def t_error(t): + t.type = t.value[0] + t.value = t.value[0] + t.lexer.skip(1) + return t + +import re +import copy +import time +import os.path + +# ----------------------------------------------------------------------------- +# trigraph() +# +# Given an input string, this function replaces all trigraph sequences. +# The following mapping is used: +# +# ??= # +# ??/ \ +# ??' ^ +# ??( [ +# ??) ] +# ??! | +# ??< { +# ??> } +# ??- ~ +# ----------------------------------------------------------------------------- + +_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''') +_trigraph_rep = { + '=':'#', + '/':'\\', + "'":'^', + '(':'[', + ')':']', + '!':'|', + '<':'{', + '>':'}', + '-':'~' +} + +def trigraph(input): + return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input) + +# ------------------------------------------------------------------ +# Macro object +# +# This object holds information about preprocessor macros +# +# .name - Macro name (string) +# .value - Macro value (a list of tokens) +# .arglist - List of argument names +# .variadic - Boolean indicating whether or not variadic macro +# .vararg - Name of the variadic parameter +# +# When a macro is created, the macro replacement token sequence is +# pre-scanned and used to create patch lists that are later used +# during macro expansion +# ------------------------------------------------------------------ + +class Macro(object): + def __init__(self,name,value,arglist=None,variadic=False): + self.name = name + self.value = value + self.arglist = arglist + self.variadic = variadic + if variadic: + self.vararg = arglist[-1] + self.source = None + +# ------------------------------------------------------------------ +# Preprocessor object +# +# Object representing a preprocessor. Contains macro definitions, +# include directories, and other information +# ------------------------------------------------------------------ + +class Preprocessor(object): + def __init__(self,lexer=None): + if lexer is None: + lexer = lex.lexer + self.lexer = lexer + self.macros = { } + self.path = [] + self.temp_path = [] + + # Probe the lexer for selected tokens + self.lexprobe() + + tm = time.localtime() + self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm)) + self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm)) + self.parser = None + + # ----------------------------------------------------------------------------- + # tokenize() + # + # Utility function. Given a string of text, tokenize into a list of tokens + # ----------------------------------------------------------------------------- + + def tokenize(self,text): + tokens = [] + self.lexer.input(text) + while True: + tok = self.lexer.token() + if not tok: break + tokens.append(tok) + return tokens + + # --------------------------------------------------------------------- + # error() + # + # Report a preprocessor error/warning of some kind + # ---------------------------------------------------------------------- + + def error(self,file,line,msg): + print >>sys.stderr,"%s:%d %s" % (file,line,msg) + + # ---------------------------------------------------------------------- + # lexprobe() + # + # This method probes the preprocessor lexer object to discover + # the token types of symbols that are important to the preprocessor. + # If this works right, the preprocessor will simply "work" + # with any suitable lexer regardless of how tokens have been named. + # ---------------------------------------------------------------------- + + def lexprobe(self): + + # Determine the token type for identifiers + self.lexer.input("identifier") + tok = self.lexer.token() + if not tok or tok.value != "identifier": + print "Couldn't determine identifier type" + else: + self.t_ID = tok.type + + # Determine the token type for integers + self.lexer.input("12345") + tok = self.lexer.token() + if not tok or int(tok.value) != 12345: + print "Couldn't determine integer type" + else: + self.t_INTEGER = tok.type + self.t_INTEGER_TYPE = type(tok.value) + + # Determine the token type for strings enclosed in double quotes + self.lexer.input("\"filename\"") + tok = self.lexer.token() + if not tok or tok.value != "\"filename\"": + print "Couldn't determine string type" + else: + self.t_STRING = tok.type + + # Determine the token type for whitespace--if any + self.lexer.input(" ") + tok = self.lexer.token() + if not tok or tok.value != " ": + self.t_SPACE = None + else: + self.t_SPACE = tok.type + + # Determine the token type for newlines + self.lexer.input("\n") + tok = self.lexer.token() + if not tok or tok.value != "\n": + self.t_NEWLINE = None + print "Couldn't determine token for newlines" + else: + self.t_NEWLINE = tok.type + + self.t_WS = (self.t_SPACE, self.t_NEWLINE) + + # Check for other characters used by the preprocessor + chars = [ '<','>','#','##','\\','(',')',',','.'] + for c in chars: + self.lexer.input(c) + tok = self.lexer.token() + if not tok or tok.value != c: + print "Unable to lex '%s' required for preprocessor" % c + + # ---------------------------------------------------------------------- + # add_path() + # + # Adds a search path to the preprocessor. + # ---------------------------------------------------------------------- + + def add_path(self,path): + self.path.append(path) + + # ---------------------------------------------------------------------- + # group_lines() + # + # Given an input string, this function splits it into lines. Trailing whitespace + # is removed. Any line ending with \ is grouped with the next line. This + # function forms the lowest level of the preprocessor---grouping into text into + # a line-by-line format. + # ---------------------------------------------------------------------- + + def group_lines(self,input): + lex = self.lexer.clone() + lines = [x.rstrip() for x in input.splitlines()] + for i in xrange(len(lines)): + j = i+1 + while lines[i].endswith('\\') and (j < len(lines)): + lines[i] = lines[i][:-1]+lines[j] + lines[j] = "" + j += 1 + + input = "\n".join(lines) + lex.input(input) + lex.lineno = 1 + + current_line = [] + while True: + tok = lex.token() + if not tok: + break + current_line.append(tok) + if tok.type in self.t_WS and '\n' in tok.value: + yield current_line + current_line = [] + + if current_line: + yield current_line + + # ---------------------------------------------------------------------- + # tokenstrip() + # + # Remove leading/trailing whitespace tokens from a token list + # ---------------------------------------------------------------------- + + def tokenstrip(self,tokens): + i = 0 + while i < len(tokens) and tokens[i].type in self.t_WS: + i += 1 + del tokens[:i] + i = len(tokens)-1 + while i >= 0 and tokens[i].type in self.t_WS: + i -= 1 + del tokens[i+1:] + return tokens + + + # ---------------------------------------------------------------------- + # collect_args() + # + # Collects comma separated arguments from a list of tokens. The arguments + # must be enclosed in parenthesis. Returns a tuple (tokencount,args,positions) + # where tokencount is the number of tokens consumed, args is a list of arguments, + # and positions is a list of integers containing the starting index of each + # argument. Each argument is represented by a list of tokens. + # + # When collecting arguments, leading and trailing whitespace is removed + # from each argument. + # + # This function properly handles nested parenthesis and commas---these do not + # define new arguments. + # ---------------------------------------------------------------------- + + def collect_args(self,tokenlist): + args = [] + positions = [] + current_arg = [] + nesting = 1 + tokenlen = len(tokenlist) + + # Search for the opening '('. + i = 0 + while (i < tokenlen) and (tokenlist[i].type in self.t_WS): + i += 1 + + if (i < tokenlen) and (tokenlist[i].value == '('): + positions.append(i+1) + else: + self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments") + return 0, [], [] + + i += 1 + + while i < tokenlen: + t = tokenlist[i] + if t.value == '(': + current_arg.append(t) + nesting += 1 + elif t.value == ')': + nesting -= 1 + if nesting == 0: + if current_arg: + args.append(self.tokenstrip(current_arg)) + positions.append(i) + return i+1,args,positions + current_arg.append(t) + elif t.value == ',' and nesting == 1: + args.append(self.tokenstrip(current_arg)) + positions.append(i+1) + current_arg = [] + else: + current_arg.append(t) + i += 1 + + # Missing end argument + self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments") + return 0, [],[] + + # ---------------------------------------------------------------------- + # macro_prescan() + # + # Examine the macro value (token sequence) and identify patch points + # This is used to speed up macro expansion later on---we'll know + # right away where to apply patches to the value to form the expansion + # ---------------------------------------------------------------------- + + def macro_prescan(self,macro): + macro.patch = [] # Standard macro arguments + macro.str_patch = [] # String conversion expansion + macro.var_comma_patch = [] # Variadic macro comma patch + i = 0 + while i < len(macro.value): + if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist: + argnum = macro.arglist.index(macro.value[i].value) + # Conversion of argument to a string + if i > 0 and macro.value[i-1].value == '#': + macro.value[i] = copy.copy(macro.value[i]) + macro.value[i].type = self.t_STRING + del macro.value[i-1] + macro.str_patch.append((argnum,i-1)) + continue + # Concatenation + elif (i > 0 and macro.value[i-1].value == '##'): + macro.patch.append(('c',argnum,i-1)) + del macro.value[i-1] + continue + elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'): + macro.patch.append(('c',argnum,i)) + i += 1 + continue + # Standard expansion + else: + macro.patch.append(('e',argnum,i)) + elif macro.value[i].value == '##': + if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \ + ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \ + (macro.value[i+1].value == macro.vararg): + macro.var_comma_patch.append(i-1) + i += 1 + macro.patch.sort(key=lambda x: x[2],reverse=True) + + # ---------------------------------------------------------------------- + # macro_expand_args() + # + # Given a Macro and list of arguments (each a token list), this method + # returns an expanded version of a macro. The return value is a token sequence + # representing the replacement macro tokens + # ---------------------------------------------------------------------- + + def macro_expand_args(self,macro,args): + # Make a copy of the macro token sequence + rep = [copy.copy(_x) for _x in macro.value] + + # Make string expansion patches. These do not alter the length of the replacement sequence + + str_expansion = {} + for argnum, i in macro.str_patch: + if argnum not in str_expansion: + str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\") + rep[i] = copy.copy(rep[i]) + rep[i].value = str_expansion[argnum] + + # Make the variadic macro comma patch. If the variadic macro argument is empty, we get rid + comma_patch = False + if macro.variadic and not args[-1]: + for i in macro.var_comma_patch: + rep[i] = None + comma_patch = True + + # Make all other patches. The order of these matters. It is assumed that the patch list + # has been sorted in reverse order of patch location since replacements will cause the + # size of the replacement sequence to expand from the patch point. + + expanded = { } + for ptype, argnum, i in macro.patch: + # Concatenation. Argument is left unexpanded + if ptype == 'c': + rep[i:i+1] = args[argnum] + # Normal expansion. Argument is macro expanded first + elif ptype == 'e': + if argnum not in expanded: + expanded[argnum] = self.expand_macros(args[argnum]) + rep[i:i+1] = expanded[argnum] + + # Get rid of removed comma if necessary + if comma_patch: + rep = [_i for _i in rep if _i] + + return rep + + + # ---------------------------------------------------------------------- + # expand_macros() + # + # Given a list of tokens, this function performs macro expansion. + # The expanded argument is a dictionary that contains macros already + # expanded. This is used to prevent infinite recursion. + # ---------------------------------------------------------------------- + + def expand_macros(self,tokens,expanded=None): + if expanded is None: + expanded = {} + i = 0 + while i < len(tokens): + t = tokens[i] + if t.type == self.t_ID: + if t.value in self.macros and t.value not in expanded: + # Yes, we found a macro match + expanded[t.value] = True + + m = self.macros[t.value] + if not m.arglist: + # A simple macro + ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded) + for e in ex: + e.lineno = t.lineno + tokens[i:i+1] = ex + i += len(ex) + else: + # A macro with arguments + j = i + 1 + while j < len(tokens) and tokens[j].type in self.t_WS: + j += 1 + if tokens[j].value == '(': + tokcount,args,positions = self.collect_args(tokens[j:]) + if not m.variadic and len(args) != len(m.arglist): + self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist))) + i = j + tokcount + elif m.variadic and len(args) < len(m.arglist)-1: + if len(m.arglist) > 2: + self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1)) + else: + self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1)) + i = j + tokcount + else: + if m.variadic: + if len(args) == len(m.arglist)-1: + args.append([]) + else: + args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1] + del args[len(m.arglist):] + + # Get macro replacement text + rep = self.macro_expand_args(m,args) + rep = self.expand_macros(rep,expanded) + for r in rep: + r.lineno = t.lineno + tokens[i:j+tokcount] = rep + i += len(rep) + del expanded[t.value] + continue + elif t.value == '__LINE__': + t.type = self.t_INTEGER + t.value = self.t_INTEGER_TYPE(t.lineno) + + i += 1 + return tokens + + # ---------------------------------------------------------------------- + # evalexpr() + # + # Evaluate an expression token sequence for the purposes of evaluating + # integral expressions. + # ---------------------------------------------------------------------- + + def evalexpr(self,tokens): + # tokens = tokenize(line) + # Search for defined macros + i = 0 + while i < len(tokens): + if tokens[i].type == self.t_ID and tokens[i].value == 'defined': + j = i + 1 + needparen = False + result = "0L" + while j < len(tokens): + if tokens[j].type in self.t_WS: + j += 1 + continue + elif tokens[j].type == self.t_ID: + if tokens[j].value in self.macros: + result = "1L" + else: + result = "0L" + if not needparen: break + elif tokens[j].value == '(': + needparen = True + elif tokens[j].value == ')': + break + else: + self.error(self.source,tokens[i].lineno,"Malformed defined()") + j += 1 + tokens[i].type = self.t_INTEGER + tokens[i].value = self.t_INTEGER_TYPE(result) + del tokens[i+1:j+1] + i += 1 + tokens = self.expand_macros(tokens) + for i,t in enumerate(tokens): + if t.type == self.t_ID: + tokens[i] = copy.copy(t) + tokens[i].type = self.t_INTEGER + tokens[i].value = self.t_INTEGER_TYPE("0L") + elif t.type == self.t_INTEGER: + tokens[i] = copy.copy(t) + # Strip off any trailing suffixes + tokens[i].value = str(tokens[i].value) + while tokens[i].value[-1] not in "0123456789abcdefABCDEF": + tokens[i].value = tokens[i].value[:-1] + + expr = "".join([str(x.value) for x in tokens]) + expr = expr.replace("&&"," and ") + expr = expr.replace("||"," or ") + expr = expr.replace("!"," not ") + try: + result = eval(expr) + except StandardError: + self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression") + result = 0 + return result + + # ---------------------------------------------------------------------- + # parsegen() + # + # Parse an input string/ + # ---------------------------------------------------------------------- + def parsegen(self,input,source=None): + + # Replace trigraph sequences + t = trigraph(input) + lines = self.group_lines(t) + + if not source: + source = "" + + self.define("__FILE__ \"%s\"" % source) + + self.source = source + chunk = [] + enable = True + iftrigger = False + ifstack = [] + + for x in lines: + for i,tok in enumerate(x): + if tok.type not in self.t_WS: break + if tok.value == '#': + # Preprocessor directive + + for tok in x: + if tok in self.t_WS and '\n' in tok.value: + chunk.append(tok) + + dirtokens = self.tokenstrip(x[i+1:]) + if dirtokens: + name = dirtokens[0].value + args = self.tokenstrip(dirtokens[1:]) + else: + name = "" + args = [] + + if name == 'define': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + self.define(args) + elif name == 'include': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + oldfile = self.macros['__FILE__'] + for tok in self.include(args): + yield tok + self.macros['__FILE__'] = oldfile + self.source = source + elif name == 'undef': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + self.undef(args) + elif name == 'ifdef': + ifstack.append((enable,iftrigger)) + if enable: + if not args[0].value in self.macros: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'ifndef': + ifstack.append((enable,iftrigger)) + if enable: + if args[0].value in self.macros: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'if': + ifstack.append((enable,iftrigger)) + if enable: + result = self.evalexpr(args) + if not result: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'elif': + if ifstack: + if ifstack[-1][0]: # We only pay attention if outer "if" allows this + if enable: # If already true, we flip enable False + enable = False + elif not iftrigger: # If False, but not triggered yet, we'll check expression + result = self.evalexpr(args) + if result: + enable = True + iftrigger = True + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #elif") + + elif name == 'else': + if ifstack: + if ifstack[-1][0]: + if enable: + enable = False + elif not iftrigger: + enable = True + iftrigger = True + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #else") + + elif name == 'endif': + if ifstack: + enable,iftrigger = ifstack.pop() + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #endif") + else: + # Unknown preprocessor directive + pass + + else: + # Normal text + if enable: + chunk.extend(x) + + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + + # ---------------------------------------------------------------------- + # include() + # + # Implementation of file-inclusion + # ---------------------------------------------------------------------- + + def include(self,tokens): + # Try to extract the filename and then process an include file + if not tokens: + return + if tokens: + if tokens[0].value != '<' and tokens[0].type != self.t_STRING: + tokens = self.expand_macros(tokens) + + if tokens[0].value == '<': + # Include <...> + i = 1 + while i < len(tokens): + if tokens[i].value == '>': + break + i += 1 + else: + print "Malformed #include <...>" + return + filename = "".join([x.value for x in tokens[1:i]]) + path = self.path + [""] + self.temp_path + elif tokens[0].type == self.t_STRING: + filename = tokens[0].value[1:-1] + path = self.temp_path + [""] + self.path + else: + print "Malformed #include statement" + return + for p in path: + iname = os.path.join(p,filename) + try: + data = open(iname,"r").read() + dname = os.path.dirname(iname) + if dname: + self.temp_path.insert(0,dname) + for tok in self.parsegen(data,filename): + yield tok + if dname: + del self.temp_path[0] + break + except IOError,e: + pass + else: + print "Couldn't find '%s'" % filename + + # ---------------------------------------------------------------------- + # define() + # + # Define a new macro + # ---------------------------------------------------------------------- + + def define(self,tokens): + if isinstance(tokens,(str,unicode)): + tokens = self.tokenize(tokens) + + linetok = tokens + try: + name = linetok[0] + if len(linetok) > 1: + mtype = linetok[1] + else: + mtype = None + if not mtype: + m = Macro(name.value,[]) + self.macros[name.value] = m + elif mtype.type in self.t_WS: + # A normal macro + m = Macro(name.value,self.tokenstrip(linetok[2:])) + self.macros[name.value] = m + elif mtype.value == '(': + # A macro with arguments + tokcount, args, positions = self.collect_args(linetok[1:]) + variadic = False + for a in args: + if variadic: + print "No more arguments may follow a variadic argument" + break + astr = "".join([str(_i.value) for _i in a]) + if astr == "...": + variadic = True + a[0].type = self.t_ID + a[0].value = '__VA_ARGS__' + variadic = True + del a[1:] + continue + elif astr[-3:] == "..." and a[0].type == self.t_ID: + variadic = True + del a[1:] + # If, for some reason, "." is part of the identifier, strip off the name for the purposes + # of macro expansion + if a[0].value[-3:] == '...': + a[0].value = a[0].value[:-3] + continue + if len(a) > 1 or a[0].type != self.t_ID: + print "Invalid macro argument" + break + else: + mvalue = self.tokenstrip(linetok[1+tokcount:]) + i = 0 + while i < len(mvalue): + if i+1 < len(mvalue): + if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##': + del mvalue[i] + continue + elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS: + del mvalue[i+1] + i += 1 + m = Macro(name.value,mvalue,[x[0].value for x in args],variadic) + self.macro_prescan(m) + self.macros[name.value] = m + else: + print "Bad macro definition" + except LookupError: + print "Bad macro definition" + + # ---------------------------------------------------------------------- + # undef() + # + # Undefine a macro + # ---------------------------------------------------------------------- + + def undef(self,tokens): + id = tokens[0].value + try: + del self.macros[id] + except LookupError: + pass + + # ---------------------------------------------------------------------- + # parse() + # + # Parse input text. + # ---------------------------------------------------------------------- + def parse(self,input,source=None,ignore={}): + self.ignore = ignore + self.parser = self.parsegen(input,source) + + # ---------------------------------------------------------------------- + # token() + # + # Method to return individual tokens + # ---------------------------------------------------------------------- + def token(self): + try: + while True: + tok = self.parser.next() + if tok.type not in self.ignore: return tok + except StopIteration: + self.parser = None + return None + +if __name__ == '__main__': + import ply.lex as lex + lexer = lex.lex() + + # Run a preprocessor + import sys + f = open(sys.argv[1]) + input = f.read() + + p = Preprocessor(lexer) + p.parse(input,sys.argv[1]) + while True: + tok = p.token() + if not tok: break + print p.source, tok + + + + + + + + + + + diff --git a/plugins_available/pycparser/pycparser/ply/ctokens.py b/plugins_available/pycparser/pycparser/ply/ctokens.py new file mode 100644 index 0000000..dd5f102 --- /dev/null +++ b/plugins_available/pycparser/pycparser/ply/ctokens.py @@ -0,0 +1,133 @@ +# ---------------------------------------------------------------------- +# ctokens.py +# +# Token specifications for symbols in ANSI C and C++. This file is +# meant to be used as a library in other tokenizers. +# ---------------------------------------------------------------------- + +# Reserved words + +tokens = [ + # Literals (identifier, integer constant, float constant, string constant, char const) + 'ID', 'TYPEID', 'ICONST', 'FCONST', 'SCONST', 'CCONST', + + # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=) + 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MOD', + 'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT', + 'LOR', 'LAND', 'LNOT', + 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', + + # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=) + 'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL', + 'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL', + + # Increment/decrement (++,--) + 'PLUSPLUS', 'MINUSMINUS', + + # Structure dereference (->) + 'ARROW', + + # Ternary operator (?) + 'TERNARY', + + # Delimeters ( ) [ ] { } , . ; : + 'LPAREN', 'RPAREN', + 'LBRACKET', 'RBRACKET', + 'LBRACE', 'RBRACE', + 'COMMA', 'PERIOD', 'SEMI', 'COLON', + + # Ellipsis (...) + 'ELLIPSIS', +] + +# Operators +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_MODULO = r'%' +t_OR = r'\|' +t_AND = r'&' +t_NOT = r'~' +t_XOR = r'\^' +t_LSHIFT = r'<<' +t_RSHIFT = r'>>' +t_LOR = r'\|\|' +t_LAND = r'&&' +t_LNOT = r'!' +t_LT = r'<' +t_GT = r'>' +t_LE = r'<=' +t_GE = r'>=' +t_EQ = r'==' +t_NE = r'!=' + +# Assignment operators + +t_EQUALS = r'=' +t_TIMESEQUAL = r'\*=' +t_DIVEQUAL = r'/=' +t_MODEQUAL = r'%=' +t_PLUSEQUAL = r'\+=' +t_MINUSEQUAL = r'-=' +t_LSHIFTEQUAL = r'<<=' +t_RSHIFTEQUAL = r'>>=' +t_ANDEQUAL = r'&=' +t_OREQUAL = r'\|=' +t_XOREQUAL = r'^=' + +# Increment/decrement +t_INCREMENT = r'\+\+' +t_DECREMENT = r'--' + +# -> +t_ARROW = r'->' + +# ? +t_TERNARY = r'\?' + +# Delimeters +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_LBRACKET = r'\[' +t_RBRACKET = r'\]' +t_LBRACE = r'\{' +t_RBRACE = r'\}' +t_COMMA = r',' +t_PERIOD = r'\.' +t_SEMI = r';' +t_COLON = r':' +t_ELLIPSIS = r'\.\.\.' + +# Identifiers +t_ID = r'[A-Za-z_][A-Za-z0-9_]*' + +# Integer literal +t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?' + +# Floating literal +t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' + +# String literal +t_STRING = r'\"([^\\\n]|(\\.))*?\"' + +# Character constant 'c' or L'c' +t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\'' + +# Comment (C-Style) +def t_COMMENT(t): + r'/\*(.|\n)*?\*/' + t.lexer.lineno += t.value.count('\n') + return t + +# Comment (C++-Style) +def t_CPPCOMMENT(t): + r'//.*\n' + t.lexer.lineno += 1 + return t + + + + + + diff --git a/plugins_available/pycparser/pycparser/ply/lex.py b/plugins_available/pycparser/pycparser/ply/lex.py new file mode 100644 index 0000000..37a9993 --- /dev/null +++ b/plugins_available/pycparser/pycparser/ply/lex.py @@ -0,0 +1,1021 @@ +# ----------------------------------------------------------------------------- +# ply: lex.py +# +# Copyright (C) 2001-2009, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__version__ = "3.3" +__tabversion__ = "3.2" # Version of table file used + +import re, sys, types, copy, os + +# This tuple contains known string types +try: + # Python 2.6 + StringTypes = (types.StringType, types.UnicodeType) +except AttributeError: + # Python 3.0 + StringTypes = (str, bytes) + +# Extract the code attribute of a function. Different implementations +# are for Python 2/3 compatibility. + +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# This regular expression is used to match valid token names +_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') + +# Exception thrown when invalid token encountered and no default error +# handler is defined. + +class LexError(Exception): + def __init__(self,message,s): + self.args = (message,) + self.text = s + +# Token class. This class is used to represent the tokens produced. +class LexToken(object): + def __str__(self): + return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos) + def __repr__(self): + return str(self) + +# This object is a stand-in for a logging object created by the +# logging module. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def critical(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + info = critical + debug = critical + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# ----------------------------------------------------------------------------- +# === Lexing Engine === +# +# The following Lexer class implements the lexer runtime. There are only +# a few public methods and attributes: +# +# input() - Store a new string in the lexer +# token() - Get the next token +# clone() - Clone the lexer +# +# lineno - Current line number +# lexpos - Current position in the input string +# ----------------------------------------------------------------------------- + +class Lexer: + def __init__(self): + self.lexre = None # Master regular expression. This is a list of + # tuples (re,findex) where re is a compiled + # regular expression and findex is a list + # mapping regex group numbers to rules + self.lexretext = None # Current regular expression strings + self.lexstatere = {} # Dictionary mapping lexer states to master regexs + self.lexstateretext = {} # Dictionary mapping lexer states to regex strings + self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names + self.lexstate = "INITIAL" # Current lexer state + self.lexstatestack = [] # Stack of lexer states + self.lexstateinfo = None # State information + self.lexstateignore = {} # Dictionary of ignored characters for each state + self.lexstateerrorf = {} # Dictionary of error functions for each state + self.lexreflags = 0 # Optional re compile flags + self.lexdata = None # Actual input data (as a string) + self.lexpos = 0 # Current position in input text + self.lexlen = 0 # Length of the input text + self.lexerrorf = None # Error rule (if any) + self.lextokens = None # List of valid tokens + self.lexignore = "" # Ignored characters + self.lexliterals = "" # Literal characters that can be passed through + self.lexmodule = None # Module + self.lineno = 1 # Current line number + self.lexoptimize = 0 # Optimized mode + + def clone(self,object=None): + c = copy.copy(self) + + # If the object parameter has been supplied, it means we are attaching the + # lexer to a new object. In this case, we have to rebind all methods in + # the lexstatere and lexstateerrorf tables. + + if object: + newtab = { } + for key, ritem in self.lexstatere.items(): + newre = [] + for cre, findex in ritem: + newfindex = [] + for f in findex: + if not f or not f[0]: + newfindex.append(f) + continue + newfindex.append((getattr(object,f[0].__name__),f[1])) + newre.append((cre,newfindex)) + newtab[key] = newre + c.lexstatere = newtab + c.lexstateerrorf = { } + for key, ef in self.lexstateerrorf.items(): + c.lexstateerrorf[key] = getattr(object,ef.__name__) + c.lexmodule = object + return c + + # ------------------------------------------------------------ + # writetab() - Write lexer information to a table file + # ------------------------------------------------------------ + def writetab(self,tabfile,outputdir=""): + return + + # ------------------------------------------------------------ + # readtab() - Read lexer information from a tab file + # ------------------------------------------------------------ + def readtab(self,tabfile,fdict): + if isinstance(tabfile,types.ModuleType): + lextab = tabfile + else: + if sys.version_info[0] < 3: + exec("import %s as lextab" % tabfile) + else: + env = { } + exec("import %s as lextab" % tabfile, env,env) + lextab = env['lextab'] + + if getattr(lextab,"_tabversion","0.0") != __version__: + raise ImportError("Inconsistent PLY version") + + self.lextokens = lextab._lextokens + self.lexreflags = lextab._lexreflags + self.lexliterals = lextab._lexliterals + self.lexstateinfo = lextab._lexstateinfo + self.lexstateignore = lextab._lexstateignore + self.lexstatere = { } + self.lexstateretext = { } + for key,lre in lextab._lexstatere.items(): + titem = [] + txtitem = [] + for i in range(len(lre)): + titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict))) + txtitem.append(lre[i][0]) + self.lexstatere[key] = titem + self.lexstateretext[key] = txtitem + self.lexstateerrorf = { } + for key,ef in lextab._lexstateerrorf.items(): + self.lexstateerrorf[key] = fdict[ef] + self.begin('INITIAL') + + # ------------------------------------------------------------ + # input() - Push a new string into the lexer + # ------------------------------------------------------------ + def input(self,s): + # Pull off the first character to see if s looks like a string + c = s[:1] + if not isinstance(c,StringTypes): + raise ValueError("Expected a string") + self.lexdata = s + self.lexpos = 0 + self.lexlen = len(s) + + # ------------------------------------------------------------ + # begin() - Changes the lexing state + # ------------------------------------------------------------ + def begin(self,state): + if not state in self.lexstatere: + raise ValueError("Undefined state") + self.lexre = self.lexstatere[state] + self.lexretext = self.lexstateretext[state] + self.lexignore = self.lexstateignore.get(state,"") + self.lexerrorf = self.lexstateerrorf.get(state,None) + self.lexstate = state + + # ------------------------------------------------------------ + # push_state() - Changes the lexing state and saves old on stack + # ------------------------------------------------------------ + def push_state(self,state): + self.lexstatestack.append(self.lexstate) + self.begin(state) + + # ------------------------------------------------------------ + # pop_state() - Restores the previous state + # ------------------------------------------------------------ + def pop_state(self): + self.begin(self.lexstatestack.pop()) + + # ------------------------------------------------------------ + # current_state() - Returns the current lexing state + # ------------------------------------------------------------ + def current_state(self): + return self.lexstate + + # ------------------------------------------------------------ + # skip() - Skip ahead n characters + # ------------------------------------------------------------ + def skip(self,n): + self.lexpos += n + + # ------------------------------------------------------------ + # opttoken() - Return the next token from the Lexer + # + # Note: This function has been carefully implemented to be as fast + # as possible. Don't make changes unless you really know what + # you are doing + # ------------------------------------------------------------ + def token(self): + # Make local copies of frequently referenced attributes + lexpos = self.lexpos + lexlen = self.lexlen + lexignore = self.lexignore + lexdata = self.lexdata + + while lexpos < lexlen: + # This code provides some short-circuit code for whitespace, tabs, and other ignored characters + if lexdata[lexpos] in lexignore: + lexpos += 1 + continue + + # Look for a regular expression match + for lexre,lexindexfunc in self.lexre: + m = lexre.match(lexdata,lexpos) + if not m: continue + + # Create a token for return + tok = LexToken() + tok.value = m.group() + tok.lineno = self.lineno + tok.lexpos = lexpos + + i = m.lastindex + func,tok.type = lexindexfunc[i] + + if not func: + # If no token type was set, it's an ignored token + if tok.type: + self.lexpos = m.end() + return tok + else: + lexpos = m.end() + break + + lexpos = m.end() + + # If token is processed by a function, call it + + tok.lexer = self # Set additional attributes useful in token rules + self.lexmatch = m + self.lexpos = lexpos + + newtok = func(tok) + + # Every function must return a token, if nothing, we just move to next token + if not newtok: + lexpos = self.lexpos # This is here in case user has updated lexpos. + lexignore = self.lexignore # This is here in case there was a state change + break + + # Verify type of the token. If not in the token map, raise an error + if not self.lexoptimize: + if not newtok.type in self.lextokens: + raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( + func_code(func).co_filename, func_code(func).co_firstlineno, + func.__name__, newtok.type),lexdata[lexpos:]) + + return newtok + else: + # No match, see if in literals + if lexdata[lexpos] in self.lexliterals: + tok = LexToken() + tok.value = lexdata[lexpos] + tok.lineno = self.lineno + tok.type = tok.value + tok.lexpos = lexpos + self.lexpos = lexpos + 1 + return tok + + # No match. Call t_error() if defined. + if self.lexerrorf: + tok = LexToken() + tok.value = self.lexdata[lexpos:] + tok.lineno = self.lineno + tok.type = "error" + tok.lexer = self + tok.lexpos = lexpos + self.lexpos = lexpos + newtok = self.lexerrorf(tok) + if lexpos == self.lexpos: + # Error method didn't change text position at all. This is an error. + raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) + lexpos = self.lexpos + if not newtok: continue + return newtok + + self.lexpos = lexpos + raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:]) + + self.lexpos = lexpos + 1 + if self.lexdata is None: + raise RuntimeError("No input string given with input()") + return None + + # Iterator interface + def __iter__(self): + return self + + def next(self): + t = self.token() + if t is None: + raise StopIteration + return t + + __next__ = next + +# ----------------------------------------------------------------------------- +# ==== Lex Builder === +# +# The functions and classes below are used to collect lexing information +# and build a Lexer object from it. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# _funcs_to_names() +# +# Given a list of regular expression functions, this converts it to a list +# suitable for output to a table file +# ----------------------------------------------------------------------------- + +def _funcs_to_names(funclist,namelist): + result = [] + for f,name in zip(funclist,namelist): + if f and f[0]: + result.append((name, f[1])) + else: + result.append(f) + return result + +# ----------------------------------------------------------------------------- +# _names_to_funcs() +# +# Given a list of regular expression function names, this converts it back to +# functions. +# ----------------------------------------------------------------------------- + +def _names_to_funcs(namelist,fdict): + result = [] + for n in namelist: + if n and n[0]: + result.append((fdict[n[0]],n[1])) + else: + result.append(n) + return result + +# ----------------------------------------------------------------------------- +# _form_master_re() +# +# This function takes a list of all of the regex components and attempts to +# form the master regular expression. Given limitations in the Python re +# module, it may be necessary to break the master regex into separate expressions. +# ----------------------------------------------------------------------------- + +def _form_master_re(relist,reflags,ldict,toknames): + if not relist: return [] + regex = "|".join(relist) + try: + lexre = re.compile(regex,re.VERBOSE | reflags) + + # Build the index to function map for the matching engine + lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1) + lexindexnames = lexindexfunc[:] + + for f,i in lexre.groupindex.items(): + handle = ldict.get(f,None) + if type(handle) in (types.FunctionType, types.MethodType): + lexindexfunc[i] = (handle,toknames[f]) + lexindexnames[i] = f + elif handle is not None: + lexindexnames[i] = f + if f.find("ignore_") > 0: + lexindexfunc[i] = (None,None) + else: + lexindexfunc[i] = (None, toknames[f]) + + return [(lexre,lexindexfunc)],[regex],[lexindexnames] + except Exception: + m = int(len(relist)/2) + if m == 0: m = 1 + llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames) + rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames) + return llist+rlist, lre+rre, lnames+rnames + +# ----------------------------------------------------------------------------- +# def _statetoken(s,names) +# +# Given a declaration name s of the form "t_" and a dictionary whose keys are +# state names, this function returns a tuple (states,tokenname) where states +# is a tuple of state names and tokenname is the name of the token. For example, +# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') +# ----------------------------------------------------------------------------- + +def _statetoken(s,names): + nonstate = 1 + parts = s.split("_") + for i in range(1,len(parts)): + if not parts[i] in names and parts[i] != 'ANY': break + if i > 1: + states = tuple(parts[1:i]) + else: + states = ('INITIAL',) + + if 'ANY' in states: + states = tuple(names) + + tokenname = "_".join(parts[i:]) + return (states,tokenname) + + +# ----------------------------------------------------------------------------- +# LexerReflect() +# +# This class represents information needed to build a lexer as extracted from a +# user's input file. +# ----------------------------------------------------------------------------- +class LexerReflect(object): + def __init__(self,ldict,log=None,reflags=0): + self.ldict = ldict + self.error_func = None + self.tokens = [] + self.reflags = reflags + self.stateinfo = { 'INITIAL' : 'inclusive'} + self.files = {} + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_tokens() + self.get_literals() + self.get_states() + self.get_rules() + + # Validate all of the information + def validate_all(self): + self.validate_tokens() + self.validate_literals() + self.validate_rules() + return self.error + + # Get the tokens map + def get_tokens(self): + tokens = self.ldict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + terminals = {} + for n in self.tokens: + if not _is_identifier.match(n): + self.log.error("Bad token name '%s'",n) + self.error = 1 + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the literals specifier + def get_literals(self): + self.literals = self.ldict.get("literals","") + + # Validate literals + def validate_literals(self): + try: + for c in self.literals: + if not isinstance(c,StringTypes) or len(c) > 1: + self.log.error("Invalid literal %s. Must be a single character", repr(c)) + self.error = 1 + continue + + except TypeError: + self.log.error("Invalid literals specification. literals must be a sequence of characters") + self.error = 1 + + def get_states(self): + self.states = self.ldict.get("states",None) + # Build statemap + if self.states: + if not isinstance(self.states,(tuple,list)): + self.log.error("states must be defined as a tuple or list") + self.error = 1 + else: + for s in self.states: + if not isinstance(s,tuple) or len(s) != 2: + self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s)) + self.error = 1 + continue + name, statetype = s + if not isinstance(name,StringTypes): + self.log.error("State name %s must be a string", repr(name)) + self.error = 1 + continue + if not (statetype == 'inclusive' or statetype == 'exclusive'): + self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name) + self.error = 1 + continue + if name in self.stateinfo: + self.log.error("State '%s' already defined",name) + self.error = 1 + continue + self.stateinfo[name] = statetype + + # Get all of the symbols with a t_ prefix and sort them into various + # categories (functions, strings, error functions, and ignore characters) + + def get_rules(self): + tsymbols = [f for f in self.ldict if f[:2] == 't_' ] + + # Now build up a list of functions and a list of strings + + self.toknames = { } # Mapping of symbols to token names + self.funcsym = { } # Symbols defined as functions + self.strsym = { } # Symbols defined as strings + self.ignore = { } # Ignore strings by state + self.errorf = { } # Error functions by state + + for s in self.stateinfo: + self.funcsym[s] = [] + self.strsym[s] = [] + + if len(tsymbols) == 0: + self.log.error("No rules of the form t_rulename are defined") + self.error = 1 + return + + for f in tsymbols: + t = self.ldict[f] + states, tokname = _statetoken(f,self.stateinfo) + self.toknames[f] = tokname + + if hasattr(t,"__call__"): + if tokname == 'error': + for s in states: + self.errorf[s] = t + elif tokname == 'ignore': + line = func_code(t).co_firstlineno + file = func_code(t).co_filename + self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__) + self.error = 1 + else: + for s in states: + self.funcsym[s].append((f,t)) + elif isinstance(t, StringTypes): + if tokname == 'ignore': + for s in states: + self.ignore[s] = t + if "\\" in t: + self.log.warning("%s contains a literal backslash '\\'",f) + + elif tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", f) + self.error = 1 + else: + for s in states: + self.strsym[s].append((f,t)) + else: + self.log.error("%s not defined as a function or string", f) + self.error = 1 + + # Sort the functions by line number + for f in self.funcsym.values(): + if sys.version_info[0] < 3: + f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno)) + else: + # Python 3.0 + f.sort(key=lambda x: func_code(x[1]).co_firstlineno) + + # Sort the strings by regular expression length + for s in self.strsym.values(): + if sys.version_info[0] < 3: + s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1]))) + else: + # Python 3.0 + s.sort(key=lambda x: len(x[1]),reverse=True) + + # Validate all of the t_rules collected + def validate_rules(self): + for state in self.stateinfo: + # Validate all rules defined by functions + + + + for fname, f in self.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + tokname = self.toknames[fname] + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + continue + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + continue + + if not f.__doc__: + self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags) + if c.match(""): + self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e) + if '#' in f.__doc__: + self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__) + self.error = 1 + + # Validate all rules defined by strings + for name,r in self.strsym[state]: + tokname = self.toknames[name] + if tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", name) + self.error = 1 + continue + + if not tokname in self.tokens and tokname.find("ignore_") < 0: + self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags) + if (c.match("")): + self.log.error("Regular expression for rule '%s' matches empty string",name) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("Invalid regular expression for rule '%s'. %s",name,e) + if '#' in r: + self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name) + self.error = 1 + + if not self.funcsym[state] and not self.strsym[state]: + self.log.error("No rules defined for state '%s'",state) + self.error = 1 + + # Validate the error function + efunc = self.errorf.get(state,None) + if efunc: + f = efunc + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + + for f in self.files: + self.validate_file(f) + + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This checks to see if there are duplicated t_rulename() functions or strings + # in the parser input file. This is done using a simple regular expression + # match on each line in the given file. + # ----------------------------------------------------------------------------- + + def validate_file(self,filename): + import os.path + base,ext = os.path.splitext(filename) + if ext != '.py': return # No idea what the file is. Return OK + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + return # Couldn't find the file. Don't worry about it + + fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') + sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') + + counthash = { } + linen = 1 + for l in lines: + m = fre.match(l) + if not m: + m = sre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev) + self.error = 1 + linen += 1 + +# ----------------------------------------------------------------------------- +# lex(module) +# +# Build all of the regular expression rules from definitions in the supplied module +# ----------------------------------------------------------------------------- +def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None): + global lexer + ldict = None + stateinfo = { 'INITIAL' : 'inclusive'} + lexobj = Lexer() + lexobj.lexoptimize = optimize + global token,input + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + if debug: + if debuglog is None: + debuglog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the lexer + if object: module = object + + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + ldict = dict(_items) + else: + ldict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + linfo = LexerReflect(ldict,log=errorlog,reflags=reflags) + linfo.get_all() + if not optimize: + if linfo.validate_all(): + raise SyntaxError("Can't build lexer") + + if optimize and lextab: + try: + lexobj.readtab(lextab,ldict) + token = lexobj.token + input = lexobj.input + lexer = lexobj + return lexobj + + except ImportError: + pass + + # Dump some basic debugging information + if debug: + debuglog.info("lex: tokens = %r", linfo.tokens) + debuglog.info("lex: literals = %r", linfo.literals) + debuglog.info("lex: states = %r", linfo.stateinfo) + + # Build a dictionary of valid token names + lexobj.lextokens = { } + for n in linfo.tokens: + lexobj.lextokens[n] = 1 + + # Get literals specification + if isinstance(linfo.literals,(list,tuple)): + lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) + else: + lexobj.lexliterals = linfo.literals + + # Get the stateinfo dictionary + stateinfo = linfo.stateinfo + + regexs = { } + # Build the master regular expressions + for state in stateinfo: + regex_list = [] + + # Add rules defined by functions first + for fname, f in linfo.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + regex_list.append("(?P<%s>%s)" % (fname,f.__doc__)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state) + + # Now add all of the simple rules + for name,r in linfo.strsym[state]: + regex_list.append("(?P<%s>%s)" % (name,r)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state) + + regexs[state] = regex_list + + # Build the master regular expressions + + if debug: + debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====") + + for state in regexs: + lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames) + lexobj.lexstatere[state] = lexre + lexobj.lexstateretext[state] = re_text + lexobj.lexstaterenames[state] = re_names + if debug: + for i in range(len(re_text)): + debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i]) + + # For inclusive states, we need to add the regular expressions from the INITIAL state + for state,stype in stateinfo.items(): + if state != "INITIAL" and stype == 'inclusive': + lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) + lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) + lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) + + lexobj.lexstateinfo = stateinfo + lexobj.lexre = lexobj.lexstatere["INITIAL"] + lexobj.lexretext = lexobj.lexstateretext["INITIAL"] + lexobj.lexreflags = reflags + + # Set up ignore variables + lexobj.lexstateignore = linfo.ignore + lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","") + + # Set up error functions + lexobj.lexstateerrorf = linfo.errorf + lexobj.lexerrorf = linfo.errorf.get("INITIAL",None) + if not lexobj.lexerrorf: + errorlog.warning("No t_error rule is defined") + + # Check state information for ignore and error rules + for s,stype in stateinfo.items(): + if stype == 'exclusive': + if not s in linfo.errorf: + errorlog.warning("No error rule is defined for exclusive state '%s'", s) + if not s in linfo.ignore and lexobj.lexignore: + errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) + elif stype == 'inclusive': + if not s in linfo.errorf: + linfo.errorf[s] = linfo.errorf.get("INITIAL",None) + if not s in linfo.ignore: + linfo.ignore[s] = linfo.ignore.get("INITIAL","") + + # Create global versions of the token() and input() functions + token = lexobj.token + input = lexobj.input + lexer = lexobj + + # If in optimize mode, we write the lextab + if lextab and optimize: + lexobj.writetab(lextab,outputdir) + + return lexobj + +# ----------------------------------------------------------------------------- +# runmain() +# +# This runs the lexer as a main program +# ----------------------------------------------------------------------------- + +def runmain(lexer=None,data=None): + if not data: + try: + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + except IndexError: + sys.stdout.write("Reading from standard input (type EOF to end):\n") + data = sys.stdin.read() + + if lexer: + _input = lexer.input + else: + _input = input + _input(data) + if lexer: + _token = lexer.token + else: + _token = token + + while 1: + tok = _token() + if not tok: break + sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos)) + +# ----------------------------------------------------------------------------- +# @TOKEN(regex) +# +# This decorator function can be used to set the regex expression on a function +# when its docstring might need to be set in an alternative way +# ----------------------------------------------------------------------------- + +def TOKEN(r): + def set_doc(f): + if hasattr(r,"__call__"): + f.__doc__ = r.__doc__ + else: + f.__doc__ = r + return f + return set_doc + +# Alternative spelling of the TOKEN decorator +Token = TOKEN + diff --git a/plugins_available/pycparser/pycparser/ply/yacc.py b/plugins_available/pycparser/pycparser/ply/yacc.py new file mode 100644 index 0000000..818de6d --- /dev/null +++ b/plugins_available/pycparser/pycparser/ply/yacc.py @@ -0,0 +1,3165 @@ +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2009, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# +# This implements an LR parser that is constructed from grammar rules defined +# as Python functions. The grammer is specified by supplying the BNF inside +# Python documentation strings. The inspiration for this technique was borrowed +# from John Aycock's Spark parsing system. PLY might be viewed as cross between +# Spark and the GNU bison utility. +# +# The current implementation is only somewhat object-oriented. The +# LR parser itself is defined in terms of an object (which allows multiple +# parsers to co-exist). However, most of the variables used during table +# construction are defined in terms of global variables. Users shouldn't +# notice unless they are trying to define multiple parsers at the same +# time using threads (in which case they should have their head examined). +# +# This implementation supports both SLR and LALR(1) parsing. LALR(1) +# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), +# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, +# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced +# by the more efficient DeRemer and Pennello algorithm. +# +# :::::::: WARNING ::::::: +# +# Construction of LR parsing tables is fairly complicated and expensive. +# To make this module run fast, a *LOT* of work has been put into +# optimization---often at the expensive of readability and what might +# consider to be good Python "coding style." Modify the code at your +# own risk! +# ---------------------------------------------------------------------------- + +__version__ = "3.3" +__tabversion__ = "3.2" # Table version + +#----------------------------------------------------------------------------- +# === User configurable parameters === +# +# Change these to modify the default behavior of yacc (if you wish) +#----------------------------------------------------------------------------- + +yaccdebug = 1 # Debugging mode. If set, yacc generates a + # a 'parser.out' file in the current directory + +debug_file = 'parser.out' # Default name of the debugging file +tab_module = 'parsetab' # Default name of the table module +default_lr = 'LALR' # Default LR table generation method + +error_count = 3 # Number of symbols that must be shifted to leave recovery mode + +yaccdevel = 0 # Set to True if developing yacc. This turns off optimized + # implementations of certain functions. + +resultlimit = 40 # Size limit of results when running in debug mode. + +pickle_protocol = 0 # Protocol to use when writing pickle files + +import re, types, sys, os.path + +# Compatibility function for python 2.6/3.0 +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# Compatibility +try: + MAXINT = sys.maxint +except AttributeError: + MAXINT = sys.maxsize + +# Python 2.x/3.0 compatibility. +def load_ply_lex(): + if sys.version_info[0] < 3: + import lex + else: + import ply.lex as lex + return lex + +# This object is a stand-in for a logging object created by the +# logging module. PLY will use this by default to create things +# such as the parser.out file. If a user wants more detailed +# information, they can create their own logging object and pass +# it into PLY. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def debug(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + info = debug + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + critical = debug + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# Exception raised for yacc-related errors +class YaccError(Exception): pass + +# Format the result message that the parser produces when running in debug mode. +def format_result(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) > resultlimit: + repr_str = repr_str[:resultlimit]+" ..." + result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str) + return result + + +# Format stack entries when the parser is running in debug mode +def format_stack_entry(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) < 16: + return repr_str + else: + return "<%s @ 0x%x>" % (type(r).__name__,id(r)) + +#----------------------------------------------------------------------------- +# === LR Parsing Engine === +# +# The following classes are used for the LR parser itself. These are not +# used during table construction and are independent of the actual LR +# table generation algorithm +#----------------------------------------------------------------------------- + +# This class is used to hold non-terminal grammar symbols during parsing. +# It normally has the following attributes set: +# .type = Grammar symbol type +# .value = Symbol value +# .lineno = Starting line number +# .endlineno = Ending line number (optional, set automatically) +# .lexpos = Starting lex position +# .endlexpos = Ending lex position (optional, set automatically) + +class YaccSymbol: + def __str__(self): return self.type + def __repr__(self): return str(self) + +# This class is a wrapper around the objects actually passed to each +# grammar rule. Index lookup and assignment actually assign the +# .value attribute of the underlying YaccSymbol object. +# The lineno() method returns the line number of a given +# item (or 0 if not defined). The linespan() method returns +# a tuple of (startline,endline) representing the range of lines +# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) +# representing the range of positional information for a symbol. + +class YaccProduction: + def __init__(self,s,stack=None): + self.slice = s + self.stack = stack + self.lexer = None + self.parser= None + def __getitem__(self,n): + if n >= 0: return self.slice[n].value + else: return self.stack[n].value + + def __setitem__(self,n,v): + self.slice[n].value = v + + def __getslice__(self,i,j): + return [s.value for s in self.slice[i:j]] + + def __len__(self): + return len(self.slice) + + def lineno(self,n): + return getattr(self.slice[n],"lineno",0) + + def set_lineno(self,n,lineno): + self.slice[n].lineno = lineno + + def linespan(self,n): + startline = getattr(self.slice[n],"lineno",0) + endline = getattr(self.slice[n],"endlineno",startline) + return startline,endline + + def lexpos(self,n): + return getattr(self.slice[n],"lexpos",0) + + def lexspan(self,n): + startpos = getattr(self.slice[n],"lexpos",0) + endpos = getattr(self.slice[n],"endlexpos",startpos) + return startpos,endpos + + def error(self): + raise SyntaxError + + +# ----------------------------------------------------------------------------- +# == LRParser == +# +# The LR Parsing engine. +# ----------------------------------------------------------------------------- + +class LRParser: + def __init__(self,lrtab,errorf): + self.productions = lrtab.lr_productions + self.action = lrtab.lr_action + self.goto = lrtab.lr_goto + self.errorfunc = errorf + + def errok(self): + self.errorok = 1 + + def restart(self): + del self.statestack[:] + del self.symstack[:] + sym = YaccSymbol() + sym.type = '$end' + self.symstack.append(sym) + self.statestack.append(0) + + def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + if debug or yaccdevel: + if isinstance(debug,int): + debug = PlyLogger(sys.stderr) + return self.parsedebug(input,lexer,debug,tracking,tokenfunc) + elif tracking: + return self.parseopt(input,lexer,debug,tracking,tokenfunc) + else: + return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc) + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parsedebug(). + # + # This is the debugging enabled version of parse(). All changes made to the + # parsing engine should be made here. For the non-debugging version, + # copy this code to a method parseopt() and delete all of the sections + # enclosed in: + # + # #--! DEBUG + # statements + # #--! DEBUG + # + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # --! DEBUG + debug.info("PLY: PARSE DEBUG START") + # --! DEBUG + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = "$end" + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + # --! DEBUG + debug.debug('') + debug.debug('State : %s', state) + # --! DEBUG + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = "$end" + + # --! DEBUG + debug.debug('Stack : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + # --! DEBUG + debug.debug("Action : Shift and goto state %s", t) + # --! DEBUG + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + # --! DEBUG + if plen: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t) + else: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t) + + # --! DEBUG + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n,"value",None) + # --! DEBUG + debug.info("Done : Returning %s", format_result(result)) + debug.info("PLY: PARSE DEBUG END") + # --! DEBUG + return result + + if t == None: + + # --! DEBUG + debug.error('Error : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == "$end": + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != "$end": + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == "$end": + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt(). + # + # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY. + # Edit the debug version above, then copy any modifications to the method + # below while removing #--! DEBUG sections. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt_notrack(). + # + # Optimized version of parseopt() with line number tracking removed. + # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove + # code in the #--! TRACKING sections + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + +# ----------------------------------------------------------------------------- +# === Grammar Representation === +# +# The following functions, classes, and variables are used to represent and +# manipulate the rules that make up a grammar. +# ----------------------------------------------------------------------------- + +import re + +# regex matching identifiers +_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') + +# ----------------------------------------------------------------------------- +# class Production: +# +# This class stores the raw information about a single production or grammar rule. +# A grammar rule refers to a specification such as this: +# +# expr : expr PLUS term +# +# Here are the basic attributes defined on all productions +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','PLUS','term'] +# prec - Production precedence level +# number - Production number. +# func - Function that executes on reduce +# file - File where production function is defined +# lineno - Line number where production function is defined +# +# The following attributes are defined or optional. +# +# len - Length of the production (number of symbols on right hand side) +# usyms - Set of unique symbols found in the production +# ----------------------------------------------------------------------------- + +class Production(object): + reduced = 0 + def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0): + self.name = name + self.prod = tuple(prod) + self.number = number + self.func = func + self.callable = None + self.file = file + self.line = line + self.prec = precedence + + # Internal settings used during table construction + + self.len = len(self.prod) # Length of the production + + # Create a list of unique production symbols used in the production + self.usyms = [ ] + for s in self.prod: + if s not in self.usyms: + self.usyms.append(s) + + # List of all LR items for the production + self.lr_items = [] + self.lr_next = None + + # Create a string representation + if self.prod: + self.str = "%s -> %s" % (self.name," ".join(self.prod)) + else: + self.str = "%s -> " % self.name + + def __str__(self): + return self.str + + def __repr__(self): + return "Production("+str(self)+")" + + def __len__(self): + return len(self.prod) + + def __nonzero__(self): + return 1 + + def __getitem__(self,index): + return self.prod[index] + + # Return the nth lr_item from the production (or None if at the end) + def lr_item(self,n): + if n > len(self.prod): return None + p = LRItem(self,n) + + # Precompute the list of productions immediately following. Hack. Remove later + try: + p.lr_after = Prodnames[p.prod[n+1]] + except (IndexError,KeyError): + p.lr_after = [] + try: + p.lr_before = p.prod[n-1] + except IndexError: + p.lr_before = None + + return p + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + +# This class serves as a minimal standin for Production objects when +# reading table data from files. It only contains information +# actually used by the LR parsing engine, plus some additional +# debugging information. +class MiniProduction(object): + def __init__(self,str,name,len,func,file,line): + self.name = name + self.len = len + self.func = func + self.callable = None + self.file = file + self.line = line + self.str = str + def __str__(self): + return self.str + def __repr__(self): + return "MiniProduction(%s)" % self.str + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + + +# ----------------------------------------------------------------------------- +# class LRItem +# +# This class represents a specific stage of parsing a production rule. For +# example: +# +# expr : expr . PLUS term +# +# In the above, the "." represents the current location of the parse. Here +# basic attributes: +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] +# number - Production number. +# +# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' +# then lr_next refers to 'expr -> expr PLUS . term' +# lr_index - LR item index (location of the ".") in the prod list. +# lookaheads - LALR lookahead symbols for this item +# len - Length of the production (number of symbols on right hand side) +# lr_after - List of all productions that immediately follow +# lr_before - Grammar symbol immediately before +# ----------------------------------------------------------------------------- + +class LRItem(object): + def __init__(self,p,n): + self.name = p.name + self.prod = list(p.prod) + self.number = p.number + self.lr_index = n + self.lookaheads = { } + self.prod.insert(n,".") + self.prod = tuple(self.prod) + self.len = len(self.prod) + self.usyms = p.usyms + + def __str__(self): + if self.prod: + s = "%s -> %s" % (self.name," ".join(self.prod)) + else: + s = "%s -> " % self.name + return s + + def __repr__(self): + return "LRItem("+str(self)+")" + +# ----------------------------------------------------------------------------- +# rightmost_terminal() +# +# Return the rightmost terminal from a list of symbols. Used in add_production() +# ----------------------------------------------------------------------------- +def rightmost_terminal(symbols, terminals): + i = len(symbols) - 1 + while i >= 0: + if symbols[i] in terminals: + return symbols[i] + i -= 1 + return None + +# ----------------------------------------------------------------------------- +# === GRAMMAR CLASS === +# +# The following class represents the contents of the specified grammar along +# with various computed properties such as first sets, follow sets, LR items, etc. +# This data is used for critical parts of the table generation process later. +# ----------------------------------------------------------------------------- + +class GrammarError(YaccError): pass + +class Grammar(object): + def __init__(self,terminals): + self.Productions = [None] # A list of all of the productions. The first + # entry is always reserved for the purpose of + # building an augmented grammar + + self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all + # productions of that nonterminal. + + self.Prodmap = { } # A dictionary that is only used to detect duplicate + # productions. + + self.Terminals = { } # A dictionary mapping the names of terminal symbols to a + # list of the rules where they are used. + + for term in terminals: + self.Terminals[term] = [] + + self.Terminals['error'] = [] + + self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list + # of rule numbers where they are used. + + self.First = { } # A dictionary of precomputed FIRST(x) symbols + + self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols + + self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the + # form ('right',level) or ('nonassoc', level) or ('left',level) + + self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer. + # This is only used to provide error checking and to generate + # a warning about unused precedence rules. + + self.Start = None # Starting symbol for the grammar + + + def __len__(self): + return len(self.Productions) + + def __getitem__(self,index): + return self.Productions[index] + + # ----------------------------------------------------------------------------- + # set_precedence() + # + # Sets the precedence for a given terminal. assoc is the associativity such as + # 'left','right', or 'nonassoc'. level is a numeric level. + # + # ----------------------------------------------------------------------------- + + def set_precedence(self,term,assoc,level): + assert self.Productions == [None],"Must call set_precedence() before add_production()" + if term in self.Precedence: + raise GrammarError("Precedence already specified for terminal '%s'" % term) + if assoc not in ['left','right','nonassoc']: + raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") + self.Precedence[term] = (assoc,level) + + # ----------------------------------------------------------------------------- + # add_production() + # + # Given an action function, this function assembles a production rule and + # computes its precedence level. + # + # The production rule is supplied as a list of symbols. For example, + # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and + # symbols ['expr','PLUS','term']. + # + # Precedence is determined by the precedence of the right-most non-terminal + # or the precedence of a terminal specified by %prec. + # + # A variety of error checks are performed to make sure production symbols + # are valid and that %prec is used correctly. + # ----------------------------------------------------------------------------- + + def add_production(self,prodname,syms,func=None,file='',line=0): + + if prodname in self.Terminals: + raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname)) + if prodname == 'error': + raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname)) + if not _is_identifier.match(prodname): + raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname)) + + # Look for literal tokens + for n,s in enumerate(syms): + if s[0] in "'\"": + try: + c = eval(s) + if (len(c) > 1): + raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname)) + if not c in self.Terminals: + self.Terminals[c] = [] + syms[n] = c + continue + except SyntaxError: + pass + if not _is_identifier.match(s) and s != '%prec': + raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname)) + + # Determine the precedence level + if '%prec' in syms: + if syms[-1] == '%prec': + raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line)) + if syms[-2] != '%prec': + raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line)) + precname = syms[-1] + prodprec = self.Precedence.get(precname,None) + if not prodprec: + raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname)) + else: + self.UsedPrecedence[precname] = 1 + del syms[-2:] # Drop %prec from the rule + else: + # If no %prec, precedence is determined by the rightmost terminal symbol + precname = rightmost_terminal(syms,self.Terminals) + prodprec = self.Precedence.get(precname,('right',0)) + + # See if the rule is already in the rulemap + map = "%s -> %s" % (prodname,syms) + if map in self.Prodmap: + m = self.Prodmap[map] + raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) + + "Previous definition at %s:%d" % (m.file, m.line)) + + # From this point on, everything is valid. Create a new Production instance + pnumber = len(self.Productions) + if not prodname in self.Nonterminals: + self.Nonterminals[prodname] = [ ] + + # Add the production number to Terminals and Nonterminals + for t in syms: + if t in self.Terminals: + self.Terminals[t].append(pnumber) + else: + if not t in self.Nonterminals: + self.Nonterminals[t] = [ ] + self.Nonterminals[t].append(pnumber) + + # Create a production and add it to the list of productions + p = Production(pnumber,prodname,syms,prodprec,func,file,line) + self.Productions.append(p) + self.Prodmap[map] = p + + # Add to the global productions list + try: + self.Prodnames[prodname].append(p) + except KeyError: + self.Prodnames[prodname] = [ p ] + return 0 + + # ----------------------------------------------------------------------------- + # set_start() + # + # Sets the starting symbol and creates the augmented grammar. Production + # rule 0 is S' -> start where start is the start symbol. + # ----------------------------------------------------------------------------- + + def set_start(self,start=None): + if not start: + start = self.Productions[1].name + if start not in self.Nonterminals: + raise GrammarError("start symbol %s undefined" % start) + self.Productions[0] = Production(0,"S'",[start]) + self.Nonterminals[start].append(0) + self.Start = start + + # ----------------------------------------------------------------------------- + # find_unreachable() + # + # Find all of the nonterminal symbols that can't be reached from the starting + # symbol. Returns a list of nonterminals that can't be reached. + # ----------------------------------------------------------------------------- + + def find_unreachable(self): + + # Mark all symbols that are reachable from a symbol s + def mark_reachable_from(s): + if reachable[s]: + # We've already reached symbol s. + return + reachable[s] = 1 + for p in self.Prodnames.get(s,[]): + for r in p.prod: + mark_reachable_from(r) + + reachable = { } + for s in list(self.Terminals) + list(self.Nonterminals): + reachable[s] = 0 + + mark_reachable_from( self.Productions[0].prod[0] ) + + return [s for s in list(self.Nonterminals) + if not reachable[s]] + + # ----------------------------------------------------------------------------- + # infinite_cycles() + # + # This function looks at the various parsing rules and tries to detect + # infinite recursion cycles (grammar rules where there is no possible way + # to derive a string of only terminals). + # ----------------------------------------------------------------------------- + + def infinite_cycles(self): + terminates = {} + + # Terminals: + for t in self.Terminals: + terminates[t] = 1 + + terminates['$end'] = 1 + + # Nonterminals: + + # Initialize to false: + for n in self.Nonterminals: + terminates[n] = 0 + + # Then propagate termination until no change: + while 1: + some_change = 0 + for (n,pl) in self.Prodnames.items(): + # Nonterminal n terminates iff any of its productions terminates. + for p in pl: + # Production p terminates iff all of its rhs symbols terminate. + for s in p.prod: + if not terminates[s]: + # The symbol s does not terminate, + # so production p does not terminate. + p_terminates = 0 + break + else: + # didn't break from the loop, + # so every symbol s terminates + # so production p terminates. + p_terminates = 1 + + if p_terminates: + # symbol n terminates! + if not terminates[n]: + terminates[n] = 1 + some_change = 1 + # Don't need to consider any more productions for this n. + break + + if not some_change: + break + + infinite = [] + for (s,term) in terminates.items(): + if not term: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + # s is used-but-not-defined, and we've already warned of that, + # so it would be overkill to say that it's also non-terminating. + pass + else: + infinite.append(s) + + return infinite + + + # ----------------------------------------------------------------------------- + # undefined_symbols() + # + # Find all symbols that were used the grammar, but not defined as tokens or + # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol + # and prod is the production where the symbol was used. + # ----------------------------------------------------------------------------- + def undefined_symbols(self): + result = [] + for p in self.Productions: + if not p: continue + + for s in p.prod: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + result.append((s,p)) + return result + + # ----------------------------------------------------------------------------- + # unused_terminals() + # + # Find all terminals that were defined, but not used by the grammar. Returns + # a list of all symbols. + # ----------------------------------------------------------------------------- + def unused_terminals(self): + unused_tok = [] + for s,v in self.Terminals.items(): + if s != 'error' and not v: + unused_tok.append(s) + + return unused_tok + + # ------------------------------------------------------------------------------ + # unused_rules() + # + # Find all grammar rules that were defined, but not used (maybe not reachable) + # Returns a list of productions. + # ------------------------------------------------------------------------------ + + def unused_rules(self): + unused_prod = [] + for s,v in self.Nonterminals.items(): + if not v: + p = self.Prodnames[s][0] + unused_prod.append(p) + return unused_prod + + # ----------------------------------------------------------------------------- + # unused_precedence() + # + # Returns a list of tuples (term,precedence) corresponding to precedence + # rules that were never used by the grammar. term is the name of the terminal + # on which precedence was applied and precedence is a string such as 'left' or + # 'right' corresponding to the type of precedence. + # ----------------------------------------------------------------------------- + + def unused_precedence(self): + unused = [] + for termname in self.Precedence: + if not (termname in self.Terminals or termname in self.UsedPrecedence): + unused.append((termname,self.Precedence[termname][0])) + + return unused + + # ------------------------------------------------------------------------- + # _first() + # + # Compute the value of FIRST1(beta) where beta is a tuple of symbols. + # + # During execution of compute_first1, the result may be incomplete. + # Afterward (e.g., when called from compute_follow()), it will be complete. + # ------------------------------------------------------------------------- + def _first(self,beta): + + # We are computing First(x1,x2,x3,...,xn) + result = [ ] + for x in beta: + x_produces_empty = 0 + + # Add all the non- symbols of First[x] to the result. + for f in self.First[x]: + if f == '': + x_produces_empty = 1 + else: + if f not in result: result.append(f) + + if x_produces_empty: + # We have to consider the next x in beta, + # i.e. stay in the loop. + pass + else: + # We don't have to consider any further symbols in beta. + break + else: + # There was no 'break' from the loop, + # so x_produces_empty was true for all x in beta, + # so beta produces empty as well. + result.append('') + + return result + + # ------------------------------------------------------------------------- + # compute_first() + # + # Compute the value of FIRST1(X) for all symbols + # ------------------------------------------------------------------------- + def compute_first(self): + if self.First: + return self.First + + # Terminals: + for t in self.Terminals: + self.First[t] = [t] + + self.First['$end'] = ['$end'] + + # Nonterminals: + + # Initialize to the empty set: + for n in self.Nonterminals: + self.First[n] = [] + + # Then propagate symbols until no change: + while 1: + some_change = 0 + for n in self.Nonterminals: + for p in self.Prodnames[n]: + for f in self._first(p.prod): + if f not in self.First[n]: + self.First[n].append( f ) + some_change = 1 + if not some_change: + break + + return self.First + + # --------------------------------------------------------------------- + # compute_follow() + # + # Computes all of the follow sets for every non-terminal symbol. The + # follow set is the set of all symbols that might follow a given + # non-terminal. See the Dragon book, 2nd Ed. p. 189. + # --------------------------------------------------------------------- + def compute_follow(self,start=None): + # If already computed, return the result + if self.Follow: + return self.Follow + + # If first sets not computed yet, do that first. + if not self.First: + self.compute_first() + + # Add '$end' to the follow list of the start symbol + for k in self.Nonterminals: + self.Follow[k] = [ ] + + if not start: + start = self.Productions[1].name + + self.Follow[start] = [ '$end' ] + + while 1: + didadd = 0 + for p in self.Productions[1:]: + # Here is the production set + for i in range(len(p.prod)): + B = p.prod[i] + if B in self.Nonterminals: + # Okay. We got a non-terminal in a production + fst = self._first(p.prod[i+1:]) + hasempty = 0 + for f in fst: + if f != '' and f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if f == '': + hasempty = 1 + if hasempty or i == (len(p.prod)-1): + # Add elements of follow(a) to follow(b) + for f in self.Follow[p.name]: + if f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if not didadd: break + return self.Follow + + + # ----------------------------------------------------------------------------- + # build_lritems() + # + # This function walks the list of productions and builds a complete set of the + # LR items. The LR items are stored in two ways: First, they are uniquely + # numbered and placed in the list _lritems. Second, a linked list of LR items + # is built for each production. For example: + # + # E -> E PLUS E + # + # Creates the list + # + # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] + # ----------------------------------------------------------------------------- + + def build_lritems(self): + for p in self.Productions: + lastlri = p + i = 0 + lr_items = [] + while 1: + if i > len(p): + lri = None + else: + lri = LRItem(p,i) + # Precompute the list of productions immediately following + try: + lri.lr_after = self.Prodnames[lri.prod[i+1]] + except (IndexError,KeyError): + lri.lr_after = [] + try: + lri.lr_before = lri.prod[i-1] + except IndexError: + lri.lr_before = None + + lastlri.lr_next = lri + if not lri: break + lr_items.append(lri) + lastlri = lri + i += 1 + p.lr_items = lr_items + +# ----------------------------------------------------------------------------- +# == Class LRTable == +# +# This basic class represents a basic table of LR parsing information. +# Methods for generating the tables are not defined here. They are defined +# in the derived class LRGeneratedTable. +# ----------------------------------------------------------------------------- + +class VersionError(YaccError): pass + +class LRTable(object): + def __init__(self): + self.lr_action = None + self.lr_goto = None + self.lr_productions = None + self.lr_method = None + + def read_table(self,module): + if isinstance(module,types.ModuleType): + parsetab = module + else: + if sys.version_info[0] < 3: + exec("import %s as parsetab" % module) + else: + env = { } + exec("import %s as parsetab" % module, env, env) + parsetab = env['parsetab'] + + if parsetab._tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + + self.lr_action = parsetab._lr_action + self.lr_goto = parsetab._lr_goto + + self.lr_productions = [] + for p in parsetab._lr_productions: + self.lr_productions.append(MiniProduction(*p)) + + self.lr_method = parsetab._lr_method + return parsetab._lr_signature + + def read_pickle(self,filename): + try: + import cPickle as pickle + except ImportError: + import pickle + + in_f = open(filename,"rb") + + tabversion = pickle.load(in_f) + if tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + self.lr_method = pickle.load(in_f) + signature = pickle.load(in_f) + self.lr_action = pickle.load(in_f) + self.lr_goto = pickle.load(in_f) + productions = pickle.load(in_f) + + self.lr_productions = [] + for p in productions: + self.lr_productions.append(MiniProduction(*p)) + + in_f.close() + return signature + + # Bind all production function names to callable objects in pdict + def bind_callables(self,pdict): + for p in self.lr_productions: + p.bind(pdict) + +# ----------------------------------------------------------------------------- +# === LR Generator === +# +# The following classes and functions are used to generate LR parsing tables on +# a grammar. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# digraph() +# traverse() +# +# The following two functions are used to compute set valued functions +# of the form: +# +# F(x) = F'(x) U U{F(y) | x R y} +# +# This is used to compute the values of Read() sets as well as FOLLOW sets +# in LALR(1) generation. +# +# Inputs: X - An input set +# R - A relation +# FP - Set-valued function +# ------------------------------------------------------------------------------ + +def digraph(X,R,FP): + N = { } + for x in X: + N[x] = 0 + stack = [] + F = { } + for x in X: + if N[x] == 0: traverse(x,N,stack,F,X,R,FP) + return F + +def traverse(x,N,stack,F,X,R,FP): + stack.append(x) + d = len(stack) + N[x] = d + F[x] = FP(x) # F(X) <- F'(x) + + rel = R(x) # Get y's related to x + for y in rel: + if N[y] == 0: + traverse(y,N,stack,F,X,R,FP) + N[x] = min(N[x],N[y]) + for a in F.get(y,[]): + if a not in F[x]: F[x].append(a) + if N[x] == d: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + while element != x: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + +class LALRError(YaccError): pass + +# ----------------------------------------------------------------------------- +# == LRGeneratedTable == +# +# This class implements the LR table generation algorithm. There are no +# public methods except for write() +# ----------------------------------------------------------------------------- + +class LRGeneratedTable(LRTable): + def __init__(self,grammar,method='LALR',log=None): + if method not in ['SLR','LALR']: + raise LALRError("Unsupported method %s" % method) + + self.grammar = grammar + self.lr_method = method + + # Set up the logger + if not log: + log = NullLogger() + self.log = log + + # Internal attributes + self.lr_action = {} # Action table + self.lr_goto = {} # Goto table + self.lr_productions = grammar.Productions # Copy of grammar Production array + self.lr_goto_cache = {} # Cache of computed gotos + self.lr0_cidhash = {} # Cache of closures + + self._add_count = 0 # Internal counter used to detect cycles + + # Diagonistic information filled in by the table generator + self.sr_conflict = 0 + self.rr_conflict = 0 + self.conflicts = [] # List of conflicts + + self.sr_conflicts = [] + self.rr_conflicts = [] + + # Build the tables + self.grammar.build_lritems() + self.grammar.compute_first() + self.grammar.compute_follow() + self.lr_parse_table() + + # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. + + def lr0_closure(self,I): + self._add_count += 1 + + # Add everything in I to J + J = I[:] + didadd = 1 + while didadd: + didadd = 0 + for j in J: + for x in j.lr_after: + if getattr(x,"lr0_added",0) == self._add_count: continue + # Add B --> .G to J + J.append(x.lr_next) + x.lr0_added = self._add_count + didadd = 1 + + return J + + # Compute the LR(0) goto function goto(I,X) where I is a set + # of LR(0) items and X is a grammar symbol. This function is written + # in a way that guarantees uniqueness of the generated goto sets + # (i.e. the same goto set will never be returned as two different Python + # objects). With uniqueness, we can later do fast set comparisons using + # id(obj) instead of element-wise comparison. + + def lr0_goto(self,I,x): + # First we look for a previously cached entry + g = self.lr_goto_cache.get((id(I),x),None) + if g: return g + + # Now we generate the goto set in a way that guarantees uniqueness + # of the result + + s = self.lr_goto_cache.get(x,None) + if not s: + s = { } + self.lr_goto_cache[x] = s + + gs = [ ] + for p in I: + n = p.lr_next + if n and n.lr_before == x: + s1 = s.get(id(n),None) + if not s1: + s1 = { } + s[id(n)] = s1 + gs.append(n) + s = s1 + g = s.get('$end',None) + if not g: + if gs: + g = self.lr0_closure(gs) + s['$end'] = g + else: + s['$end'] = gs + self.lr_goto_cache[(id(I),x)] = g + return g + + # Compute the LR(0) sets of item function + def lr0_items(self): + + C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ] + i = 0 + for I in C: + self.lr0_cidhash[id(I)] = i + i += 1 + + # Loop over the items in C and each grammar symbols + i = 0 + while i < len(C): + I = C[i] + i += 1 + + # Collect all of the symbols that could possibly be in the goto(I,X) sets + asyms = { } + for ii in I: + for s in ii.usyms: + asyms[s] = None + + for x in asyms: + g = self.lr0_goto(I,x) + if not g: continue + if id(g) in self.lr0_cidhash: continue + self.lr0_cidhash[id(g)] = len(C) + C.append(g) + + return C + + # ----------------------------------------------------------------------------- + # ==== LALR(1) Parsing ==== + # + # LALR(1) parsing is almost exactly the same as SLR except that instead of + # relying upon Follow() sets when performing reductions, a more selective + # lookahead set that incorporates the state of the LR(0) machine is utilized. + # Thus, we mainly just have to focus on calculating the lookahead sets. + # + # The method used here is due to DeRemer and Pennelo (1982). + # + # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) + # Lookahead Sets", ACM Transactions on Programming Languages and Systems, + # Vol. 4, No. 4, Oct. 1982, pp. 615-649 + # + # Further details can also be found in: + # + # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", + # McGraw-Hill Book Company, (1985). + # + # ----------------------------------------------------------------------------- + + # ----------------------------------------------------------------------------- + # compute_nullable_nonterminals() + # + # Creates a dictionary containing all of the non-terminals that might produce + # an empty production. + # ----------------------------------------------------------------------------- + + def compute_nullable_nonterminals(self): + nullable = {} + num_nullable = 0 + while 1: + for p in self.grammar.Productions[1:]: + if p.len == 0: + nullable[p.name] = 1 + continue + for t in p.prod: + if not t in nullable: break + else: + nullable[p.name] = 1 + if len(nullable) == num_nullable: break + num_nullable = len(nullable) + return nullable + + # ----------------------------------------------------------------------------- + # find_nonterminal_trans(C) + # + # Given a set of LR(0) items, this functions finds all of the non-terminal + # transitions. These are transitions in which a dot appears immediately before + # a non-terminal. Returns a list of tuples of the form (state,N) where state + # is the state number and N is the nonterminal symbol. + # + # The input C is the set of LR(0) items. + # ----------------------------------------------------------------------------- + + def find_nonterminal_transitions(self,C): + trans = [] + for state in range(len(C)): + for p in C[state]: + if p.lr_index < p.len - 1: + t = (state,p.prod[p.lr_index+1]) + if t[1] in self.grammar.Nonterminals: + if t not in trans: trans.append(t) + state = state + 1 + return trans + + # ----------------------------------------------------------------------------- + # dr_relation() + # + # Computes the DR(p,A) relationships for non-terminal transitions. The input + # is a tuple (state,N) where state is a number and N is a nonterminal symbol. + # + # Returns a list of terminals. + # ----------------------------------------------------------------------------- + + def dr_relation(self,C,trans,nullable): + dr_set = { } + state,N = trans + terms = [] + + g = self.lr0_goto(C[state],N) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index+1] + if a in self.grammar.Terminals: + if a not in terms: terms.append(a) + + # This extra bit is to handle the start state + if state == 0 and N == self.grammar.Productions[0].prod[0]: + terms.append('$end') + + return terms + + # ----------------------------------------------------------------------------- + # reads_relation() + # + # Computes the READS() relation (p,A) READS (t,C). + # ----------------------------------------------------------------------------- + + def reads_relation(self,C, trans, empty): + # Look for empty transitions + rel = [] + state, N = trans + + g = self.lr0_goto(C[state],N) + j = self.lr0_cidhash.get(id(g),-1) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index + 1] + if a in empty: + rel.append((j,a)) + + return rel + + # ----------------------------------------------------------------------------- + # compute_lookback_includes() + # + # Determines the lookback and includes relations + # + # LOOKBACK: + # + # This relation is determined by running the LR(0) state machine forward. + # For example, starting with a production "N : . A B C", we run it forward + # to obtain "N : A B C ." We then build a relationship between this final + # state and the starting state. These relationships are stored in a dictionary + # lookdict. + # + # INCLUDES: + # + # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). + # + # This relation is used to determine non-terminal transitions that occur + # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) + # if the following holds: + # + # B -> LAT, where T -> epsilon and p' -L-> p + # + # L is essentially a prefix (which may be empty), T is a suffix that must be + # able to derive an empty string. State p' must lead to state p with the string L. + # + # ----------------------------------------------------------------------------- + + def compute_lookback_includes(self,C,trans,nullable): + + lookdict = {} # Dictionary of lookback relations + includedict = {} # Dictionary of include relations + + # Make a dictionary of non-terminal transitions + dtrans = {} + for t in trans: + dtrans[t] = 1 + + # Loop over all transitions and compute lookbacks and includes + for state,N in trans: + lookb = [] + includes = [] + for p in C[state]: + if p.name != N: continue + + # Okay, we have a name match. We now follow the production all the way + # through the state machine until we get the . on the right hand side + + lr_index = p.lr_index + j = state + while lr_index < p.len - 1: + lr_index = lr_index + 1 + t = p.prod[lr_index] + + # Check to see if this symbol and state are a non-terminal transition + if (j,t) in dtrans: + # Yes. Okay, there is some chance that this is an includes relation + # the only way to know for certain is whether the rest of the + # production derives empty + + li = lr_index + 1 + while li < p.len: + if p.prod[li] in self.grammar.Terminals: break # No forget it + if not p.prod[li] in nullable: break + li = li + 1 + else: + # Appears to be a relation between (j,t) and (state,N) + includes.append((j,t)) + + g = self.lr0_goto(C[j],t) # Go to next set + j = self.lr0_cidhash.get(id(g),-1) # Go to next state + + # When we get here, j is the final state, now we have to locate the production + for r in C[j]: + if r.name != p.name: continue + if r.len != p.len: continue + i = 0 + # This look is comparing a production ". A B C" with "A B C ." + while i < r.lr_index: + if r.prod[i] != p.prod[i+1]: break + i = i + 1 + else: + lookb.append((j,r)) + for i in includes: + if not i in includedict: includedict[i] = [] + includedict[i].append((state,N)) + lookdict[(state,N)] = lookb + + return lookdict,includedict + + # ----------------------------------------------------------------------------- + # compute_read_sets() + # + # Given a set of LR(0) items, this function computes the read sets. + # + # Inputs: C = Set of LR(0) items + # ntrans = Set of nonterminal transitions + # nullable = Set of empty transitions + # + # Returns a set containing the read sets + # ----------------------------------------------------------------------------- + + def compute_read_sets(self,C, ntrans, nullable): + FP = lambda x: self.dr_relation(C,x,nullable) + R = lambda x: self.reads_relation(C,x,nullable) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # compute_follow_sets() + # + # Given a set of LR(0) items, a set of non-terminal transitions, a readset, + # and an include set, this function computes the follow sets + # + # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} + # + # Inputs: + # ntrans = Set of nonterminal transitions + # readsets = Readset (previously computed) + # inclsets = Include sets (previously computed) + # + # Returns a set containing the follow sets + # ----------------------------------------------------------------------------- + + def compute_follow_sets(self,ntrans,readsets,inclsets): + FP = lambda x: readsets[x] + R = lambda x: inclsets.get(x,[]) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # add_lookaheads() + # + # Attaches the lookahead symbols to grammar rules. + # + # Inputs: lookbacks - Set of lookback relations + # followset - Computed follow set + # + # This function directly attaches the lookaheads to productions contained + # in the lookbacks set + # ----------------------------------------------------------------------------- + + def add_lookaheads(self,lookbacks,followset): + for trans,lb in lookbacks.items(): + # Loop over productions in lookback + for state,p in lb: + if not state in p.lookaheads: + p.lookaheads[state] = [] + f = followset.get(trans,[]) + for a in f: + if a not in p.lookaheads[state]: p.lookaheads[state].append(a) + + # ----------------------------------------------------------------------------- + # add_lalr_lookaheads() + # + # This function does all of the work of adding lookahead information for use + # with LALR parsing + # ----------------------------------------------------------------------------- + + def add_lalr_lookaheads(self,C): + # Determine all of the nullable nonterminals + nullable = self.compute_nullable_nonterminals() + + # Find all non-terminal transitions + trans = self.find_nonterminal_transitions(C) + + # Compute read sets + readsets = self.compute_read_sets(C,trans,nullable) + + # Compute lookback/includes relations + lookd, included = self.compute_lookback_includes(C,trans,nullable) + + # Compute LALR FOLLOW sets + followsets = self.compute_follow_sets(trans,readsets,included) + + # Add all of the lookaheads + self.add_lookaheads(lookd,followsets) + + # ----------------------------------------------------------------------------- + # lr_parse_table() + # + # This function constructs the parse tables for SLR or LALR + # ----------------------------------------------------------------------------- + def lr_parse_table(self): + Productions = self.grammar.Productions + Precedence = self.grammar.Precedence + goto = self.lr_goto # Goto array + action = self.lr_action # Action array + log = self.log # Logger for output + + actionp = { } # Action production array (temporary) + + log.info("Parsing method: %s", self.lr_method) + + # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items + # This determines the number of states + + C = self.lr0_items() + + if self.lr_method == 'LALR': + self.add_lalr_lookaheads(C) + + # Build the parser table, state by state + st = 0 + for I in C: + # Loop over each production in I + actlist = [ ] # List of actions + st_action = { } + st_actionp = { } + st_goto = { } + log.info("") + log.info("state %d", st) + log.info("") + for p in I: + log.info(" (%d) %s", p.number, str(p)) + log.info("") + + for p in I: + if p.len == p.lr_index + 1: + if p.name == "S'": + # Start symbol. Accept! + st_action["$end"] = 0 + st_actionp["$end"] = p + else: + # We are at the end of a production. Reduce! + if self.lr_method == 'LALR': + laheads = p.lookaheads[st] + else: + laheads = self.grammar.Follow[p.name] + for a in laheads: + actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p))) + r = st_action.get(a,None) + if r is not None: + # Whoa. Have a shift/reduce or reduce/reduce conflict + if r > 0: + # Need to decide on shift or reduce here + # By default we favor shifting. Need to add + # some precedence rules here. + sprec,slevel = Productions[st_actionp[a].number].prec + rprec,rlevel = Precedence.get(a,('right',0)) + if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): + # We really need to reduce here. + st_action[a] = -p.number + st_actionp[a] = p + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + Productions[p.number].reduced += 1 + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the shift + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif r < 0: + # Reduce/reduce conflict. In this case, we favor the rule + # that was defined first in the grammar file + oldp = Productions[-r] + pp = Productions[p.number] + if oldp.line > pp.line: + st_action[a] = -p.number + st_actionp[a] = p + chosenp,rejectp = pp,oldp + Productions[p.number].reduced += 1 + Productions[oldp.number].reduced -= 1 + else: + chosenp,rejectp = oldp,pp + self.rr_conflicts.append((st,chosenp,rejectp)) + log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a]) + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = -p.number + st_actionp[a] = p + Productions[p.number].reduced += 1 + else: + i = p.lr_index + a = p.prod[i+1] # Get symbol right after the "." + if a in self.grammar.Terminals: + g = self.lr0_goto(I,a) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + # We are in a shift state + actlist.append((a,p,"shift and go to state %d" % j)) + r = st_action.get(a,None) + if r is not None: + # Whoa have a shift/reduce or shift/shift conflict + if r > 0: + if r != j: + raise LALRError("Shift/shift conflict in state %d" % st) + elif r < 0: + # Do a precedence check. + # - if precedence of reduce rule is higher, we reduce. + # - if precedence of reduce is same and left assoc, we reduce. + # - otherwise we shift + rprec,rlevel = Productions[st_actionp[a].number].prec + sprec,slevel = Precedence.get(a,('right',0)) + if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): + # We decide to shift here... highest precedence to shift + Productions[st_actionp[a].number].reduced -= 1 + st_action[a] = j + st_actionp[a] = p + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the reduce + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = j + st_actionp[a] = p + + # Print the actions associated with each terminal + _actprint = { } + for a,p,m in actlist: + if a in st_action: + if p is st_actionp[a]: + log.info(" %-15s %s",a,m) + _actprint[(a,m)] = 1 + log.info("") + # Print the actions that were not used. (debugging) + not_used = 0 + for a,p,m in actlist: + if a in st_action: + if p is not st_actionp[a]: + if not (a,m) in _actprint: + log.debug(" ! %-15s [ %s ]",a,m) + not_used = 1 + _actprint[(a,m)] = 1 + if not_used: + log.debug("") + + # Construct the goto table for this state + + nkeys = { } + for ii in I: + for s in ii.usyms: + if s in self.grammar.Nonterminals: + nkeys[s] = None + for n in nkeys: + g = self.lr0_goto(I,n) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + st_goto[n] = j + log.info(" %-30s shift and go to state %d",n,j) + + action[st] = st_action + actionp[st] = st_actionp + goto[st] = st_goto + st += 1 + + + # ----------------------------------------------------------------------------- + # write() + # + # This function writes the LR parsing tables to a file + # ----------------------------------------------------------------------------- + + def write_table(self,modulename,outputdir='',signature=""): + return + + + # ----------------------------------------------------------------------------- + # pickle_table() + # + # This function pickles the LR parsing tables to a supplied file object + # ----------------------------------------------------------------------------- + + def pickle_table(self,filename,signature=""): + try: + import cPickle as pickle + except ImportError: + import pickle + outf = open(filename,"wb") + pickle.dump(__tabversion__,outf,pickle_protocol) + pickle.dump(self.lr_method,outf,pickle_protocol) + pickle.dump(signature,outf,pickle_protocol) + pickle.dump(self.lr_action,outf,pickle_protocol) + pickle.dump(self.lr_goto,outf,pickle_protocol) + + outp = [] + for p in self.lr_productions: + if p.func: + outp.append((p.str,p.name, p.len, p.func,p.file,p.line)) + else: + outp.append((str(p),p.name,p.len,None,None,None)) + pickle.dump(outp,outf,pickle_protocol) + outf.close() + +# ----------------------------------------------------------------------------- +# === INTROSPECTION === +# +# The following functions and classes are used to implement the PLY +# introspection features followed by the yacc() function itself. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# parse_grammar() +# +# This takes a raw grammar rule string and parses it into production data +# ----------------------------------------------------------------------------- +def parse_grammar(doc,file,line): + grammar = [] + # Split the doc string into lines + pstrings = doc.splitlines() + lastp = None + dline = line + for ps in pstrings: + dline += 1 + p = ps.split() + if not p: continue + try: + if p[0] == '|': + # This is a continuation of a previous rule + if not lastp: + raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline)) + prodname = lastp + syms = p[1:] + else: + prodname = p[0] + lastp = prodname + syms = p[2:] + assign = p[1] + if assign != ':' and assign != '::=': + raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline)) + + grammar.append((file,dline,prodname,syms)) + except SyntaxError: + raise + except Exception: + raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip())) + + return grammar + +# ----------------------------------------------------------------------------- +# ParserReflect() +# +# This class represents information extracted for building a parser including +# start symbol, error function, tokens, precedence list, action functions, +# etc. +# ----------------------------------------------------------------------------- +class ParserReflect(object): + def __init__(self,pdict,log=None): + self.pdict = pdict + self.start = None + self.error_func = None + self.tokens = None + self.files = {} + self.grammar = [] + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_start() + self.get_error_func() + self.get_tokens() + self.get_precedence() + self.get_pfunctions() + + # Validate all of the information + def validate_all(self): + self.validate_start() + self.validate_error_func() + self.validate_tokens() + self.validate_precedence() + self.validate_pfunctions() + self.validate_files() + return self.error + + # Compute a signature over the grammar + def signature(self): + try: + from hashlib import md5 + except ImportError: + from md5 import md5 + try: + sig = md5() + if self.start: + sig.update(self.start.encode('latin-1')) + if self.prec: + sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1')) + if self.tokens: + sig.update(" ".join(self.tokens).encode('latin-1')) + for f in self.pfuncs: + if f[3]: + sig.update(f[3].encode('latin-1')) + except (TypeError,ValueError): + pass + return sig.digest() + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This method checks to see if there are duplicated p_rulename() functions + # in the parser module file. Without this function, it is really easy for + # users to make mistakes by cutting and pasting code fragments (and it's a real + # bugger to try and figure out why the resulting parser doesn't work). Therefore, + # we just do a little regular expression pattern matching of def statements + # to try and detect duplicates. + # ----------------------------------------------------------------------------- + + def validate_files(self): + # Match def p_funcname( + fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') + + for filename in self.files.keys(): + base,ext = os.path.splitext(filename) + if ext != '.py': return 1 # No idea. Assume it's okay. + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + continue + + counthash = { } + for linen,l in enumerate(lines): + linen += 1 + m = fre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev) + + # Get the start symbol + def get_start(self): + self.start = self.pdict.get('start') + + # Validate the start symbol + def validate_start(self): + if self.start is not None: + if not isinstance(self.start,str): + self.log.error("'start' must be a string") + + # Look for error handler + def get_error_func(self): + self.error_func = self.pdict.get('p_error') + + # Validate the error function + def validate_error_func(self): + if self.error_func: + if isinstance(self.error_func,types.FunctionType): + ismethod = 0 + elif isinstance(self.error_func, types.MethodType): + ismethod = 1 + else: + self.log.error("'p_error' defined, but is not a function or method") + self.error = 1 + return + + eline = func_code(self.error_func).co_firstlineno + efile = func_code(self.error_func).co_filename + self.files[efile] = 1 + + if (func_code(self.error_func).co_argcount != 1+ismethod): + self.log.error("%s:%d: p_error() requires 1 argument",efile,eline) + self.error = 1 + + # Get the tokens map + def get_tokens(self): + tokens = self.pdict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + # Validate the tokens. + if 'error' in self.tokens: + self.log.error("Illegal token name 'error'. Is a reserved word") + self.error = 1 + return + + terminals = {} + for n in self.tokens: + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the precedence map (if any) + def get_precedence(self): + self.prec = self.pdict.get("precedence",None) + + # Validate and parse the precedence map + def validate_precedence(self): + preclist = [] + if self.prec: + if not isinstance(self.prec,(list,tuple)): + self.log.error("precedence must be a list or tuple") + self.error = 1 + return + for level,p in enumerate(self.prec): + if not isinstance(p,(list,tuple)): + self.log.error("Bad precedence table") + self.error = 1 + return + + if len(p) < 2: + self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p) + self.error = 1 + return + assoc = p[0] + if not isinstance(assoc,str): + self.log.error("precedence associativity must be a string") + self.error = 1 + return + for term in p[1:]: + if not isinstance(term,str): + self.log.error("precedence items must be strings") + self.error = 1 + return + preclist.append((term,assoc,level+1)) + self.preclist = preclist + + # Get all p_functions from the grammar + def get_pfunctions(self): + p_functions = [] + for name, item in self.pdict.items(): + if name[:2] != 'p_': continue + if name == 'p_error': continue + if isinstance(item,(types.FunctionType,types.MethodType)): + line = func_code(item).co_firstlineno + file = func_code(item).co_filename + p_functions.append((line,file,name,item.__doc__)) + + # Sort all of the actions by line number + p_functions.sort() + self.pfuncs = p_functions + + + # Validate all of the p_functions + def validate_pfunctions(self): + grammar = [] + # Check for non-empty symbols + if len(self.pfuncs) == 0: + self.log.error("no rules of the form p_rulename are defined") + self.error = 1 + return + + for line, file, name, doc in self.pfuncs: + func = self.pdict[name] + if isinstance(func, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + if func_code(func).co_argcount > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__) + self.error = 1 + elif func_code(func).co_argcount < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__) + self.error = 1 + elif not func.__doc__: + self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__) + else: + try: + parsed_g = parse_grammar(doc,file,line) + for g in parsed_g: + grammar.append((name, g)) + except SyntaxError: + e = sys.exc_info()[1] + self.log.error(str(e)) + self.error = 1 + + # Looks like a valid grammar rule + # Mark the file in which defined. + self.files[file] = 1 + + # Secondary validation step that looks for p_ definitions that are not functions + # or functions that look like they might be grammar rules. + + for n,v in self.pdict.items(): + if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue + if n[0:2] == 't_': continue + if n[0:2] == 'p_' and n != 'p_error': + self.log.warning("'%s' not defined as a function", n) + if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or + (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)): + try: + doc = v.__doc__.split(" ") + if doc[1] == ':': + self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix", + func_code(v).co_filename, func_code(v).co_firstlineno,n) + except Exception: + pass + + self.grammar = grammar + +# ----------------------------------------------------------------------------- +# yacc(module) +# +# Build a parser +# ----------------------------------------------------------------------------- + +def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, + check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='', + debuglog=None, errorlog = None, picklefile=None): + + global parse # Reference to the parsing method of the last built parser + + # If pickling is enabled, table files are not created + + if picklefile: + write_tables = 0 + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the parser + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + pdict = dict(_items) + else: + pdict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + pinfo = ParserReflect(pdict,log=errorlog) + pinfo.get_all() + + if pinfo.error: + raise YaccError("Unable to build parser") + + # Check signature against table files (if any) + signature = pinfo.signature() + + # Read the tables + try: + lr = LRTable() + if picklefile: + read_signature = lr.read_pickle(picklefile) + else: + read_signature = lr.read_table(tabmodule) + if optimize or (read_signature == signature): + try: + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + parse = parser.parse + return parser + except Exception: + e = sys.exc_info()[1] + errorlog.warning("There was a problem loading the table file: %s", repr(e)) + except VersionError: + e = sys.exc_info() + errorlog.warning(str(e)) + except Exception: + pass + + if debuglog is None: + if debug: + debuglog = PlyLogger(open(debugfile,"w")) + else: + debuglog = NullLogger() + + debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__) + + + errors = 0 + + # Validate the parser information + if pinfo.validate_all(): + raise YaccError("Unable to build parser") + + if not pinfo.error_func: + errorlog.warning("no p_error() function is defined") + + # Create a grammar object + grammar = Grammar(pinfo.tokens) + + # Set precedence level for terminals + for term, assoc, level in pinfo.preclist: + try: + grammar.set_precedence(term,assoc,level) + except GrammarError: + e = sys.exc_info()[1] + errorlog.warning("%s",str(e)) + + # Add productions to the grammar + for funcname, gram in pinfo.grammar: + file, line, prodname, syms = gram + try: + grammar.add_production(prodname,syms,funcname,file,line) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error("%s",str(e)) + errors = 1 + + # Set the grammar start symbols + try: + if start is None: + grammar.set_start(pinfo.start) + else: + grammar.set_start(start) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error(str(e)) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Verify the grammar structure + undefined_symbols = grammar.undefined_symbols() + for sym, prod in undefined_symbols: + errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym) + errors = 1 + + unused_terminals = grammar.unused_terminals() + if unused_terminals: + debuglog.info("") + debuglog.info("Unused terminals:") + debuglog.info("") + for term in unused_terminals: + errorlog.warning("Token '%s' defined, but not used", term) + debuglog.info(" %s", term) + + # Print out all productions to the debug log + if debug: + debuglog.info("") + debuglog.info("Grammar") + debuglog.info("") + for n,p in enumerate(grammar.Productions): + debuglog.info("Rule %-5d %s", n, p) + + # Find unused non-terminals + unused_rules = grammar.unused_rules() + for prod in unused_rules: + errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name) + + if len(unused_terminals) == 1: + errorlog.warning("There is 1 unused token") + if len(unused_terminals) > 1: + errorlog.warning("There are %d unused tokens", len(unused_terminals)) + + if len(unused_rules) == 1: + errorlog.warning("There is 1 unused rule") + if len(unused_rules) > 1: + errorlog.warning("There are %d unused rules", len(unused_rules)) + + if debug: + debuglog.info("") + debuglog.info("Terminals, with rules where they appear") + debuglog.info("") + terms = list(grammar.Terminals) + terms.sort() + for term in terms: + debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]])) + + debuglog.info("") + debuglog.info("Nonterminals, with rules where they appear") + debuglog.info("") + nonterms = list(grammar.Nonterminals) + nonterms.sort() + for nonterm in nonterms: + debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]])) + debuglog.info("") + + if check_recursion: + unreachable = grammar.find_unreachable() + for u in unreachable: + errorlog.warning("Symbol '%s' is unreachable",u) + + infinite = grammar.infinite_cycles() + for inf in infinite: + errorlog.error("Infinite recursion detected for symbol '%s'", inf) + errors = 1 + + unused_prec = grammar.unused_precedence() + for term, assoc in unused_prec: + errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Run the LRGeneratedTable on the grammar + if debug: + errorlog.debug("Generating %s tables", method) + + lr = LRGeneratedTable(grammar,method,debuglog) + + if debug: + num_sr = len(lr.sr_conflicts) + + # Report shift/reduce and reduce/reduce conflicts + if num_sr == 1: + errorlog.warning("1 shift/reduce conflict") + elif num_sr > 1: + errorlog.warning("%d shift/reduce conflicts", num_sr) + + num_rr = len(lr.rr_conflicts) + if num_rr == 1: + errorlog.warning("1 reduce/reduce conflict") + elif num_rr > 1: + errorlog.warning("%d reduce/reduce conflicts", num_rr) + + # Write out conflicts to the output file + if debug and (lr.sr_conflicts or lr.rr_conflicts): + debuglog.warning("") + debuglog.warning("Conflicts:") + debuglog.warning("") + + for state, tok, resolution in lr.sr_conflicts: + debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution) + + already_reported = {} + for state, rule, rejected in lr.rr_conflicts: + if (state,id(rule),id(rejected)) in already_reported: + continue + debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + debuglog.warning("rejected rule (%s) in state %d", rejected,state) + errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + errorlog.warning("rejected rule (%s) in state %d", rejected, state) + already_reported[state,id(rule),id(rejected)] = 1 + + warned_never = [] + for state, rule, rejected in lr.rr_conflicts: + if not rejected.reduced and (rejected not in warned_never): + debuglog.warning("Rule (%s) is never reduced", rejected) + errorlog.warning("Rule (%s) is never reduced", rejected) + warned_never.append(rejected) + + # Write the table file if requested + if write_tables: + lr.write_table(tabmodule,outputdir,signature) + + # Write a pickled version of the tables + if picklefile: + lr.pickle_table(picklefile,signature) + + # Build the parser + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + + parse = parser.parse + return parser diff --git a/plugins_available/pycparser/pycparser/plyparser.py b/plugins_available/pycparser/pycparser/plyparser.py new file mode 100644 index 0000000..8e9e573 --- /dev/null +++ b/plugins_available/pycparser/pycparser/plyparser.py @@ -0,0 +1,67 @@ +#----------------------------------------------------------------- +# plyparser.py +# +# PLYParser class and other utilites for simplifying programming +# parsers with PLY +# +# Copyright (C) 2008-2009, Eli Bendersky +# License: LGPL +#----------------------------------------------------------------- + + +class Coord(object): + """ Coordinates of a syntactic element. Consists of: + - File name + - Line number + - (optional) column number, for the Lexer + """ + def __init__(self, file, line, column=None): + self.file = file + self.line = line + self.column = column + + def __str__(self): + str = "%s:%s" % (self.file, self.line) + if self.column: str += ":%s" % self.column + return str + + +class ParseError(Exception): pass + + +class PLYParser(object): + def _create_opt_rule(self, rulename): + """ Given a rule name, creates an optional ply.yacc rule + for it. The name of the optional rule is + _opt + """ + optname = rulename + '_opt' + + def optrule(self, p): + p[0] = p[1] + + optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename) + optrule.__name__ = 'p_%s' % optname + setattr(self.__class__, optrule.__name__, optrule) + + def _coord(self, lineno, column=None): + return Coord( + file=self.clex.filename, + line=lineno, + column=column) + + def _parse_error(self, msg, coord): + raise ParseError("%s: %s" % (coord, msg)) + + +if __name__ == '__main__': + pp = PLYParser() + pp._create_opt_rule('java') + + ar = [4, 6] + pp.p_java_opt(ar) + + print ar + print pp.p_java_opt.__doc__ + + print dir(pp) diff --git a/plugins_available/pycparser/yacctab.py b/plugins_available/pycparser/yacctab.py new file mode 100644 index 0000000..544f84c --- /dev/null +++ b/plugins_available/pycparser/yacctab.py @@ -0,0 +1,250 @@ + +# yacctab.py +# This file is automatically generated. Do not edit. +_tabversion = '3.2' + +_lr_method = 'LALR' + +_lr_signature = "\n\x9d\xda\xf6\xeb\xf5\x97'\n\xff\xf7b(G7V" + +_lr_action_items = {'VOID':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[6,6,-51,-61,-60,-49,-46,-47,-27,-23,6,-45,-57,-53,-58,-44,6,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,6,-56,-54,-59,-62,6,-70,6,-69,-95,-24,6,-85,-84,6,-38,-39,6,-97,6,6,6,-76,6,6,6,6,-29,-40,6,-77,-71,6,6,-98,-100,-99,6,6,-86,-30,6,-73,-78,-72,6,6,-88,-87,-127,-128,-129,]),'LBRACKET':([1,2,3,5,6,8,9,12,14,15,16,17,19,21,22,23,25,28,31,33,34,36,37,38,39,40,42,43,44,46,48,49,50,54,56,57,58,59,60,67,73,76,78,83,84,85,86,87,90,95,97,101,102,106,107,111,116,125,126,136,137,138,139,140,149,153,160,183,184,185,186,191,192,197,229,231,236,238,239,242,258,259,260,288,292,295,320,321,327,328,329,347,348,],[-221,-51,-61,-60,-49,-46,-47,-221,-45,-57,-53,-58,-44,-48,-149,52,-221,-96,-50,-52,-55,-221,-56,-54,-59,-62,-43,-7,-8,-70,-69,-42,52,-85,-84,-22,-101,-103,-21,-41,-97,-221,-221,-219,-210,-220,-218,-208,151,-217,-199,-214,-209,-216,-215,-207,189,-104,-102,-71,-17,-68,-18,-67,-205,-206,189,-98,-100,-99,189,286,189,-86,-214,-73,-72,-204,-203,-202,189,189,-211,286,-88,-87,-200,-201,-126,-122,-124,-123,-125,]),'WCHAR_CONST':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[85,-38,-39,-198,85,-196,-195,85,85,-194,85,85,-193,-197,85,85,-40,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,-32,-35,-31,85,85,-33,85,85,-130,-36,85,-34,85,-155,-158,-156,-152,-153,-157,-159,85,-161,-162,-154,-160,85,85,85,-127,-146,85,-128,-145,85,-143,-131,85,85,85,-142,85,85,85,85,85,-141,-129,-144,-134,85,-132,85,85,-133,85,85,85,-138,-137,-135,85,-139,85,-136,85,-140,]),'FLOAT_CONST':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[86,-38,-39,-198,86,-196,-195,86,86,-194,86,86,-193,-197,86,86,-40,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,-32,-35,-31,86,86,-33,86,86,-130,-36,86,-34,86,-155,-158,-156,-152,-153,-157,-159,86,-161,-162,-154,-160,86,86,86,-127,-146,86,-128,-145,86,-143,-131,86,86,86,-142,86,86,86,86,86,-141,-129,-144,-134,86,-132,86,86,-133,86,86,86,-138,-137,-135,86,-139,86,-136,86,-140,]),'MINUS':([52,66,70,81,82,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,99,100,101,102,103,104,106,107,110,111,128,131,133,143,146,147,149,151,152,153,154,155,156,157,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,229,232,238,239,242,244,245,246,247,248,249,250,251,252,253,254,255,256,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,319,320,321,322,324,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[89,-38,-39,-198,-185,-219,-210,-220,-218,-208,89,-196,-187,-195,89,89,-217,-194,-199,89,-166,89,-214,-209,-193,167,-216,-215,-197,-207,89,89,-40,89,89,-191,-205,89,89,-206,-190,89,-188,-185,-189,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,-32,-35,-31,89,89,-33,89,89,-130,-36,89,-34,-214,89,-204,-203,-202,-155,-158,-156,-152,-153,-157,-159,89,-161,-162,-154,-160,89,-211,89,167,167,167,-171,167,167,167,-170,167,167,-168,-167,167,167,167,167,167,-169,89,-127,-146,89,-128,-145,89,-143,-131,89,89,89,-142,89,89,-192,-200,-201,89,-186,89,89,-141,-129,-144,-134,89,-132,89,89,-133,89,89,89,-138,-137,-135,89,-139,89,-136,89,-140,]),'RPAREN':([1,2,3,5,6,8,9,12,14,15,16,17,19,21,22,23,25,28,31,33,34,36,37,38,39,40,42,43,44,45,46,48,49,50,53,54,56,57,58,59,60,67,73,76,78,82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,112,113,114,115,116,117,118,119,125,126,136,137,138,139,140,147,149,152,153,154,156,157,158,159,160,161,162,163,183,184,185,186,187,188,190,191,192,193,197,218,231,236,237,238,239,241,242,243,257,258,259,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,281,282,283,284,287,288,289,290,291,292,295,302,319,320,321,323,324,325,327,328,329,331,339,341,344,345,346,347,348,360,362,365,],[-221,-51,-61,-60,-49,-46,-47,-221,-45,-57,-53,-58,-44,-48,-149,-94,-221,-96,-50,-52,-55,-221,-56,-54,-59,-62,-43,-7,-8,73,-70,-69,-42,-95,-221,-85,-84,-22,-101,-103,-21,-41,-97,-221,-221,-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,-164,-216,-215,-207,184,-11,185,-107,-221,-12,-105,-111,-104,-102,-71,-17,-68,-18,-67,-191,-205,242,-206,-190,-188,-185,256,-147,-221,-150,260,-189,-98,-100,-99,-221,-110,-2,-109,-121,-119,-1,-86,-10,-73,-72,319,-204,-203,-212,-202,321,-118,-221,-119,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,-181,-169,-15,-16,327,328,-221,-120,-112,-106,-108,-88,-87,-9,-192,-200,-201,-151,-186,-148,-126,-122,-124,348,352,354,356,-213,-165,-123,-125,363,-221,367,]),'LONG':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[16,16,-51,-61,-60,-49,-46,-47,-27,-23,16,-45,-57,-53,-58,-44,16,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,16,-56,-54,-59,-62,16,-70,16,-69,-95,-24,16,-85,-84,16,-38,-39,16,-97,16,16,16,-76,16,16,16,16,-29,-40,16,-77,-71,16,16,-98,-100,-99,16,16,-86,-30,16,-73,-78,-72,16,16,-88,-87,-127,-128,-129,]),'PLUS':([52,66,70,81,82,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,99,100,101,102,103,104,106,107,110,111,128,131,133,143,146,147,149,151,152,153,154,155,156,157,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,229,232,238,239,242,244,245,246,247,248,249,250,251,252,253,254,255,256,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,319,320,321,322,324,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[91,-38,-39,-198,-185,-219,-210,-220,-218,-208,91,-196,-187,-195,91,91,-217,-194,-199,91,-166,91,-214,-209,-193,171,-216,-215,-197,-207,91,91,-40,91,91,-191,-205,91,91,-206,-190,91,-188,-185,-189,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,-32,-35,-31,91,91,-33,91,91,-130,-36,91,-34,-214,91,-204,-203,-202,-155,-158,-156,-152,-153,-157,-159,91,-161,-162,-154,-160,91,-211,91,171,171,171,-171,171,171,171,-170,171,171,-168,-167,171,171,171,171,171,-169,91,-127,-146,91,-128,-145,91,-143,-131,91,91,91,-142,91,91,-192,-200,-201,91,-186,91,91,-141,-129,-144,-134,91,-132,91,91,-133,91,91,91,-138,-137,-135,91,-139,91,-136,91,-140,]),'ELLIPSIS':([195,],[290,]),'GT':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,172,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,172,-173,-171,-175,172,-174,-170,-177,172,-168,-167,-176,172,172,172,172,-169,-192,-200,-201,-186,]),'RBRACE':([66,70,75,77,82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,109,111,121,122,123,131,133,134,135,145,147,149,153,154,156,157,161,163,196,198,200,203,206,207,209,211,212,214,217,223,224,226,228,235,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,293,294,296,297,299,300,301,303,304,307,308,313,319,320,321,323,324,332,333,334,335,336,338,343,346,349,350,353,358,359,361,363,366,368,],[-38,-39,136,-76,-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,-164,-216,-215,-163,-207,-89,197,-92,-221,-40,231,-77,236,-191,-205,-206,-190,-188,-185,-150,-189,292,-90,295,-113,-32,-35,299,-31,303,-33,-20,-130,-36,-19,-34,-78,-204,-203,-202,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,-181,-169,-91,-93,-116,333,-127,-146,335,-128,-145,-143,-131,-142,-192,-200,-201,-151,-186,350,-114,-141,-129,-144,-134,-132,-165,-117,-115,-133,-138,-137,-135,-139,-136,-140,]),'ENUM':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[24,24,-51,-61,-60,-49,-46,-47,-27,-23,24,-45,-57,-53,-58,-44,24,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,24,-56,-54,-59,-62,24,-70,24,-69,-95,-24,24,-85,-84,24,-38,-39,24,-97,24,24,24,-76,24,24,24,24,-29,-40,24,-77,-71,24,24,-98,-100,-99,24,24,-86,-30,24,-73,-78,-72,24,24,-88,-87,-127,-128,-129,]),'PERIOD':([83,84,85,86,87,90,95,97,101,102,106,107,111,149,153,229,238,239,242,260,320,321,],[-219,-210,-220,-218,-208,150,-217,-199,-214,-209,-216,-215,-207,-205,-206,-214,-204,-203,-202,-211,-200,-201,]),'GE':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,176,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,176,-173,-171,-175,176,-174,-170,-177,176,-168,-167,-176,176,176,176,176,-169,-192,-200,-201,-186,]),'INT_CONST_DEC':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[107,-38,-39,-198,107,-196,-195,107,107,-194,107,107,-193,-197,107,107,-40,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,-32,-35,-31,107,107,-33,107,107,-130,-36,107,-34,107,-155,-158,-156,-152,-153,-157,-159,107,-161,-162,-154,-160,107,107,107,-127,-146,107,-128,-145,107,-143,-131,107,107,107,-142,107,107,107,107,107,-141,-129,-144,-134,107,-132,107,107,-133,107,107,107,-138,-137,-135,107,-139,107,-136,107,-140,]),'ARROW':([83,84,85,86,87,90,95,97,101,102,106,107,111,149,153,229,238,239,242,260,320,321,],[-219,-210,-220,-218,-208,148,-217,-199,-214,-209,-216,-215,-207,-205,-206,-214,-204,-203,-202,-211,-200,-201,]),'DOUBLE':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[34,34,-51,-61,-60,-49,-46,-47,-27,-23,34,-45,-57,-53,-58,-44,34,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,34,-56,-54,-59,-62,34,-70,34,-69,-95,-24,34,-85,-84,34,-38,-39,34,-97,34,34,34,-76,34,34,34,34,-29,-40,34,-77,-71,34,34,-98,-100,-99,34,34,-86,-30,34,-73,-78,-72,34,34,-88,-87,-127,-128,-129,]),'MINUSEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,245,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'INT_CONST_OCT':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[106,-38,-39,-198,106,-196,-195,106,106,-194,106,106,-193,-197,106,106,-40,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,-32,-35,-31,106,106,-33,106,106,-130,-36,106,-34,106,-155,-158,-156,-152,-153,-157,-159,106,-161,-162,-154,-160,106,106,106,-127,-146,106,-128,-145,106,-143,-131,106,106,106,-142,106,106,106,106,106,-141,-129,-144,-134,106,-132,106,106,-133,106,106,106,-138,-137,-135,106,-139,106,-136,106,-140,]),'TIMESEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,254,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'OR':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,181,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,181,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,181,-178,-180,-181,-169,-192,-200,-201,-186,]),'SHORT':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[2,2,-51,-61,-60,-49,-46,-47,-27,-23,2,-45,-57,-53,-58,-44,2,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,2,-56,-54,-59,-62,2,-70,2,-69,-95,-24,2,-85,-84,2,-38,-39,2,-97,2,2,2,-76,2,2,2,2,-29,-40,2,-77,-71,2,2,-98,-100,-99,2,2,-86,-30,2,-73,-78,-72,2,2,-88,-87,-127,-128,-129,]),'RETURN':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,213,-40,-32,-35,-31,213,-33,213,-130,-36,213,-34,-127,-146,213,-128,-145,-143,-131,213,-142,213,-141,-129,-144,-134,213,-132,213,-133,213,213,-138,-137,-135,-139,213,-136,213,-140,]),'RSHIFTEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,255,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'STATIC':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,48,50,51,53,54,56,64,66,70,72,73,131,132,133,136,183,184,185,186,195,197,205,212,231,236,258,287,292,295,299,303,335,],[8,8,-51,-61,-60,-49,-46,-47,-27,-23,8,-45,-57,-53,-58,-44,8,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,8,-56,-54,-59,-62,8,-70,-69,-95,-24,8,-85,-84,8,-38,-39,8,-97,8,-29,-40,-71,-98,-100,-99,8,8,-86,-30,8,-73,-72,8,8,-88,-87,-127,-128,-129,]),'SIZEOF':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[88,-38,-39,-198,88,-196,-195,88,88,-194,88,88,-193,-197,88,88,-40,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,-32,-35,-31,88,88,-33,88,88,-130,-36,88,-34,88,-155,-158,-156,-152,-153,-157,-159,88,-161,-162,-154,-160,88,88,88,-127,-146,88,-128,-145,88,-143,-131,88,88,88,-142,88,88,88,88,88,-141,-129,-144,-134,88,-132,88,88,-133,88,88,88,-138,-137,-135,88,-139,88,-136,88,-140,]),'UNSIGNED':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[15,15,-51,-61,-60,-49,-46,-47,-27,-23,15,-45,-57,-53,-58,-44,15,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,15,-56,-54,-59,-62,15,-70,15,-69,-95,-24,15,-85,-84,15,-38,-39,15,-97,15,15,15,-76,15,15,15,15,-29,-40,15,-77,-71,15,15,-98,-100,-99,15,15,-86,-30,15,-73,-78,-72,15,15,-88,-87,-127,-128,-129,]),'UNION':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[18,18,-51,-61,-60,-49,-46,-47,-27,-23,18,-45,-57,-53,-58,-44,18,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,18,-56,-54,-59,-62,18,-70,18,-69,-95,-24,18,-85,-84,18,-38,-39,18,-97,18,18,18,-76,18,18,18,18,-29,-40,18,-77,-71,18,18,-98,-100,-99,18,18,-86,-30,18,-73,-78,-72,18,18,-88,-87,-127,-128,-129,]),'COLON':([2,3,5,6,15,16,17,22,23,28,31,33,34,37,38,39,40,46,48,50,54,56,73,76,78,79,82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,109,111,136,137,138,139,140,142,147,149,153,154,156,157,159,161,163,183,184,185,197,219,229,231,234,236,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,292,295,311,319,320,321,323,324,325,346,],[-51,-61,-60,-49,-57,-53,-58,-149,-94,-96,-50,-52,-55,-56,-54,-59,-62,-70,-69,-95,-85,-84,-97,-221,-221,143,-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,-164,-216,-215,-163,-207,-71,-17,-68,-18,-67,232,-191,-205,-206,-190,-188,-185,-147,-150,-189,-98,-100,-99,-86,309,315,-73,143,-72,-204,-203,-202,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,326,-181,-169,-88,-87,340,-192,-200,-201,-151,-186,-148,-165,]),'$end':([10,11,20,27,30,32,51,66,132,205,299,303,335,],[-27,-23,0,-26,-25,-28,-24,-38,-29,-30,-127,-128,-129,]),'WSTRING_LITERAL':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[84,-38,-39,-198,84,-196,-195,84,84,-194,84,84,-193,-197,84,84,-40,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,-32,-35,-31,84,84,-33,84,84,-130,-36,84,-34,84,-155,-158,-156,-152,-153,-157,-159,84,-161,-162,-154,-160,84,84,84,-127,-146,84,-128,-145,84,-143,-131,84,84,84,-142,84,84,84,84,84,-141,-129,-144,-134,84,-132,84,84,-133,84,84,84,-138,-137,-135,84,-139,84,-136,84,-140,]),'DIVIDE':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,174,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,174,174,174,174,174,174,174,174,174,174,-168,-167,174,174,174,174,174,-169,-192,-200,-201,-186,]),'FOR':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,215,-40,-32,-35,-31,215,-33,215,-130,-36,215,-34,-127,-146,215,-128,-145,-143,-131,215,-142,215,-141,-129,-144,-134,215,-132,215,-133,215,215,-138,-137,-135,-139,215,-136,215,-140,]),'PLUSPLUS':([52,66,70,81,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,100,101,102,103,106,107,110,111,128,131,133,143,146,149,151,152,153,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,229,232,238,239,242,244,245,246,247,248,249,250,251,252,253,254,255,256,260,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,320,321,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[94,-38,-39,-198,-219,-210,-220,-218,-208,94,-196,149,-195,94,94,-217,-194,-199,94,94,-214,-209,-193,-216,-215,-197,-207,94,94,-40,94,94,-205,94,94,-206,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,-32,-35,-31,94,94,-33,94,94,-130,-36,94,-34,-214,94,-204,-203,-202,-155,-158,-156,-152,-153,-157,-159,94,-161,-162,-154,-160,94,-211,94,94,-127,-146,94,-128,-145,94,-143,-131,94,94,94,-142,94,94,-200,-201,94,94,94,-141,-129,-144,-134,94,-132,94,94,-133,94,94,94,-138,-137,-135,94,-139,94,-136,94,-140,]),'EQUALS':([23,28,50,64,73,82,83,84,85,86,87,90,95,97,101,102,106,107,111,123,130,147,149,153,154,156,157,163,183,184,185,229,238,239,242,260,319,320,321,324,],[-94,-96,-95,128,-97,-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,199,128,-191,-205,-206,-190,-188,247,-189,-98,-100,-99,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'ELSE':([206,207,211,214,224,228,299,300,303,304,307,313,334,335,336,338,343,353,358,359,361,363,366,368,],[-32,-35,-31,-33,-36,-34,-127,-146,-128,-145,-143,-142,-141,-129,-144,-134,-132,-133,-138,-137,364,-139,-136,-140,]),'EQ':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,178,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,178,-173,-171,-175,-179,-174,-170,-177,178,-168,-167,-176,178,-178,178,178,-169,-192,-200,-201,-186,]),'AND':([52,66,70,81,82,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,99,100,101,102,103,104,106,107,110,111,128,131,133,143,146,147,149,151,152,153,154,155,156,157,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,229,232,238,239,242,244,245,246,247,248,249,250,251,252,253,254,255,256,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,319,320,321,322,324,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[103,-38,-39,-198,-185,-219,-210,-220,-218,-208,103,-196,-187,-195,103,103,-217,-194,-199,103,-166,103,-214,-209,-193,179,-216,-215,-197,-207,103,103,-40,103,103,-191,-205,103,103,-206,-190,103,-188,-185,-189,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,-32,-35,-31,103,103,-33,103,103,-130,-36,103,-34,-214,103,-204,-203,-202,-155,-158,-156,-152,-153,-157,-159,103,-161,-162,-154,-160,103,-211,103,-172,179,-173,-171,-175,-179,-174,-170,-177,179,-168,-167,-176,179,-178,-180,179,-169,103,-127,-146,103,-128,-145,103,-143,-131,103,103,103,-142,103,103,-192,-200,-201,103,-186,103,103,-141,-129,-144,-134,103,-132,103,103,-133,103,103,103,-138,-137,-135,103,-139,103,-136,103,-140,]),'TYPEID':([0,1,2,3,5,6,7,8,9,10,11,12,14,15,16,17,18,19,20,21,22,23,24,27,28,29,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[22,22,-51,-61,-60,-49,46,-46,-47,-27,-23,22,-45,-57,-53,-58,-75,-44,22,-48,-149,-94,54,-26,-96,-74,-25,-50,-28,-52,-55,22,-56,-54,-59,-62,22,-70,22,-69,-95,-24,22,-85,-84,22,-38,-39,22,-97,22,22,22,-76,22,22,22,22,-29,-40,22,-77,-71,22,22,-98,-100,-99,22,22,-86,-30,22,-73,-78,-72,22,22,-88,-87,-127,-128,-129,]),'LBRACE':([7,18,23,24,28,29,41,46,48,50,54,56,64,66,68,70,71,72,73,128,129,131,133,183,184,185,204,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,332,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[47,-75,-94,55,-96,-74,-221,74,80,-95,120,124,-221,-38,-5,-39,131,-6,-97,204,131,131,-40,-98,-100,-99,204,-32,-35,-31,131,-33,131,-130,-36,131,-34,-127,-146,131,-128,-145,-143,-131,131,-142,131,204,-141,-129,-144,-134,131,-132,131,-133,131,131,-138,-137,-135,-139,131,-136,131,-140,]),'PPHASH':([0,10,11,20,27,30,32,51,66,132,205,299,303,335,],[32,-27,-23,32,-26,-25,-28,-24,-38,-29,-30,-127,-128,-129,]),'INT':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[33,33,-51,-61,-60,-49,-46,-47,-27,-23,33,-45,-57,-53,-58,-44,33,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,33,-56,-54,-59,-62,33,-70,33,-69,-95,-24,33,-85,-84,33,-38,-39,33,-97,33,33,33,-76,33,33,33,33,-29,-40,33,-77,-71,33,33,-98,-100,-99,33,33,-86,-30,33,-73,-78,-72,33,33,-88,-87,-127,-128,-129,]),'SIGNED':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[37,37,-51,-61,-60,-49,-46,-47,-27,-23,37,-45,-57,-53,-58,-44,37,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,37,-56,-54,-59,-62,37,-70,37,-69,-95,-24,37,-85,-84,37,-38,-39,37,-97,37,37,37,-76,37,37,37,37,-29,-40,37,-77,-71,37,37,-98,-100,-99,37,37,-86,-30,37,-73,-78,-72,37,37,-88,-87,-127,-128,-129,]),'CONTINUE':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,216,-40,-32,-35,-31,216,-33,216,-130,-36,216,-34,-127,-146,216,-128,-145,-143,-131,216,-142,216,-141,-129,-144,-134,216,-132,216,-133,216,216,-138,-137,-135,-139,216,-136,216,-140,]),'NOT':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[110,-38,-39,-198,110,-196,-195,110,110,-194,110,110,-193,-197,110,110,-40,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,-32,-35,-31,110,110,-33,110,110,-130,-36,110,-34,110,-155,-158,-156,-152,-153,-157,-159,110,-161,-162,-154,-160,110,110,110,-127,-146,110,-128,-145,110,-143,-131,110,110,110,-142,110,110,110,110,110,-141,-129,-144,-134,110,-132,110,110,-133,110,110,110,-138,-137,-135,110,-139,110,-136,110,-140,]),'OREQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,253,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'MOD':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,182,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,182,182,182,182,182,182,182,182,182,182,-168,-167,182,182,182,182,182,-169,-192,-200,-201,-186,]),'RSHIFT':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,164,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,164,-173,-171,164,164,164,-170,164,164,-168,-167,164,164,164,164,164,-169,-192,-200,-201,-186,]),'DEFAULT':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,219,-40,-32,-35,-31,219,-33,219,-130,-36,219,-34,-127,-146,219,-128,-145,-143,-131,219,-142,219,-141,-129,-144,-134,219,-132,219,-133,219,219,-138,-137,-135,-139,219,-136,219,-140,]),'CHAR':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[31,31,-51,-61,-60,-49,-46,-47,-27,-23,31,-45,-57,-53,-58,-44,31,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,31,-56,-54,-59,-62,31,-70,31,-69,-95,-24,31,-85,-84,31,-38,-39,31,-97,31,31,31,-76,31,31,31,31,-29,-40,31,-77,-71,31,31,-98,-100,-99,31,31,-86,-30,31,-73,-78,-72,31,31,-88,-87,-127,-128,-129,]),'WHILE':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,314,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,220,-40,-32,-35,-31,220,-33,220,-130,-36,220,-34,-127,-146,220,-128,-145,-143,-131,220,-142,342,220,-141,-129,-144,-134,220,-132,220,-133,220,220,-138,-137,-135,-139,220,-136,220,-140,]),'DIVEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,244,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'EXTERN':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,48,50,51,53,54,56,64,66,70,72,73,131,132,133,136,183,184,185,186,195,197,205,212,231,236,258,287,292,295,299,303,335,],[9,9,-51,-61,-60,-49,-46,-47,-27,-23,9,-45,-57,-53,-58,-44,9,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,9,-56,-54,-59,-62,9,-70,-69,-95,-24,9,-85,-84,9,-38,-39,9,-97,9,-29,-40,-71,-98,-100,-99,9,9,-86,-30,9,-73,-72,9,9,-88,-87,-127,-128,-129,]),'CASE':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,221,-40,-32,-35,-31,221,-33,221,-130,-36,221,-34,-127,-146,221,-128,-145,-143,-131,221,-142,221,-141,-129,-144,-134,221,-132,221,-133,221,221,-138,-137,-135,-139,221,-136,221,-140,]),'LAND':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,177,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,177,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,-181,-169,-192,-200,-201,-186,]),'REGISTER':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,48,50,51,53,54,56,64,66,70,72,73,131,132,133,136,183,184,185,186,195,197,205,212,231,236,258,287,292,295,299,303,335,],[14,14,-51,-61,-60,-49,-46,-47,-27,-23,14,-45,-57,-53,-58,-44,14,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,14,-56,-54,-59,-62,14,-70,-69,-95,-24,14,-85,-84,14,-38,-39,14,-97,14,-29,-40,-71,-98,-100,-99,14,14,-86,-30,14,-73,-72,14,14,-88,-87,-127,-128,-129,]),'MODEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,246,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'NE':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,169,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,169,-173,-171,-175,-179,-174,-170,-177,169,-168,-167,-176,169,-178,169,169,-169,-192,-200,-201,-186,]),'SWITCH':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,222,-40,-32,-35,-31,222,-33,222,-130,-36,222,-34,-127,-146,222,-128,-145,-143,-131,222,-142,222,-141,-129,-144,-134,222,-132,222,-133,222,222,-138,-137,-135,-139,222,-136,222,-140,]),'INT_CONST_HEX':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[95,-38,-39,-198,95,-196,-195,95,95,-194,95,95,-193,-197,95,95,-40,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,-32,-35,-31,95,95,-33,95,95,-130,-36,95,-34,95,-155,-158,-156,-152,-153,-157,-159,95,-161,-162,-154,-160,95,95,95,-127,-146,95,-128,-145,95,-143,-131,95,95,95,-142,95,95,95,95,95,-141,-129,-144,-134,95,-132,95,95,-133,95,95,95,-138,-137,-135,95,-139,95,-136,95,-140,]),'PLUSEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,249,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'STRUCT':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[29,29,-51,-61,-60,-49,-46,-47,-27,-23,29,-45,-57,-53,-58,-44,29,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,29,-56,-54,-59,-62,29,-70,29,-69,-95,-24,29,-85,-84,29,-38,-39,29,-97,29,29,29,-76,29,29,29,29,-29,-40,29,-77,-71,29,29,-98,-100,-99,29,29,-86,-30,29,-73,-78,-72,29,29,-88,-87,-127,-128,-129,]),'CONDOP':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,180,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,-181,-169,-192,-200,-201,-186,]),'BREAK':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,225,-40,-32,-35,-31,225,-33,225,-130,-36,225,-34,-127,-146,225,-128,-145,-143,-131,225,-142,225,-141,-129,-144,-134,225,-132,225,-133,225,225,-138,-137,-135,-139,225,-136,225,-140,]),'VOLATILE':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,25,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,57,59,64,66,70,72,73,74,75,76,77,78,80,98,125,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[40,40,-51,-61,-60,-49,-46,-47,-27,-23,40,-45,-57,-53,-58,-44,40,-48,-149,-94,40,-26,-96,-25,-50,-28,-52,-55,40,-56,-54,-59,-62,40,-70,40,-69,-95,-24,40,-85,-84,40,-103,40,-38,-39,40,-97,40,40,40,-76,40,40,40,-104,40,-29,-40,40,-77,-71,40,40,-98,-100,-99,40,40,-86,-30,40,-73,-78,-72,40,40,-88,-87,-127,-128,-129,]),'ANDEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,252,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'DO':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,227,-40,-32,-35,-31,227,-33,227,-130,-36,227,-34,-127,-146,227,-128,-145,-143,-131,227,-142,227,-141,-129,-144,-134,227,-132,227,-133,227,227,-138,-137,-135,-139,227,-136,227,-140,]),'LNOT':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[81,-38,-39,-198,81,-196,-195,81,81,-194,81,81,-193,-197,81,81,-40,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,-32,-35,-31,81,81,-33,81,81,-130,-36,81,-34,81,-155,-158,-156,-152,-153,-157,-159,81,-161,-162,-154,-160,81,81,81,-127,-146,81,-128,-145,81,-143,-131,81,81,81,-142,81,81,81,81,81,-141,-129,-144,-134,81,-132,81,81,-133,81,81,81,-138,-137,-135,81,-139,81,-136,81,-140,]),'CONST':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,25,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,57,59,64,66,70,72,73,74,75,76,77,78,80,98,125,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[3,3,-51,-61,-60,-49,-46,-47,-27,-23,3,-45,-57,-53,-58,-44,3,-48,-149,-94,3,-26,-96,-25,-50,-28,-52,-55,3,-56,-54,-59,-62,3,-70,3,-69,-95,-24,3,-85,-84,3,-103,3,-38,-39,3,-97,3,3,3,-76,3,3,3,-104,3,-29,-40,3,-77,-71,3,3,-98,-100,-99,3,3,-86,-30,3,-73,-78,-72,3,3,-88,-87,-127,-128,-129,]),'LOR':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,165,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,-181,-169,-192,-200,-201,-186,]),'CHAR_CONST':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[83,-38,-39,-198,83,-196,-195,83,83,-194,83,83,-193,-197,83,83,-40,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,-32,-35,-31,83,83,-33,83,83,-130,-36,83,-34,83,-155,-158,-156,-152,-153,-157,-159,83,-161,-162,-154,-160,83,83,83,-127,-146,83,-128,-145,83,-143,-131,83,83,83,-142,83,83,83,83,83,-141,-129,-144,-134,83,-132,83,83,-133,83,83,83,-138,-137,-135,83,-139,83,-136,83,-140,]),'LSHIFT':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,166,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,166,-173,-171,166,166,166,-170,166,166,-168,-167,166,166,166,166,166,-169,-192,-200,-201,-186,]),'GOTO':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,208,-40,-32,-35,-31,208,-33,208,-130,-36,208,-34,-127,-146,208,-128,-145,-143,-131,208,-142,208,-141,-129,-144,-134,208,-132,208,-133,208,208,-138,-137,-135,-139,208,-136,208,-140,]),'LE':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,168,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,168,-173,-171,-175,168,-174,-170,-177,168,-168,-167,-176,168,168,168,168,-169,-192,-200,-201,-186,]),'SEMI':([1,2,3,5,6,8,9,12,14,15,16,17,19,21,22,23,26,28,31,33,34,35,36,37,38,39,40,42,43,44,46,48,49,50,54,56,61,62,63,64,65,66,67,69,70,73,82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,109,111,130,131,133,136,141,142,144,147,149,153,154,156,157,159,161,163,183,184,185,197,201,202,203,206,207,210,211,212,213,214,216,217,218,223,224,225,226,227,228,229,231,233,236,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,292,295,298,299,300,301,302,303,304,305,306,307,308,309,313,315,317,318,319,320,321,323,324,325,333,334,335,336,337,338,340,343,346,350,351,352,353,354,356,357,358,359,361,363,364,366,367,368,],[-221,-51,-61,-60,-49,-46,-47,-221,-45,-57,-53,-58,-44,-48,-149,-94,-221,-96,-50,-52,-55,66,-221,-56,-54,-59,-62,-43,-7,-8,-70,-69,-42,-95,-85,-84,-14,-37,-13,-65,-63,-38,-41,-221,-39,-97,-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,-164,-216,-215,-163,-207,-65,-221,-40,-71,-79,-81,235,-191,-205,-206,-190,-188,-185,-147,-150,-189,-98,-100,-99,-86,-64,-66,-113,-32,-35,300,-31,-221,304,-33,307,-221,-10,-130,-36,313,-9,-221,-34,-214,-73,-83,-72,-204,-203,-202,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,-181,-169,-88,-87,334,-127,-146,-221,-9,-128,-145,336,-221,-143,-131,-221,-142,-221,-82,-80,-192,-200,-201,-151,-186,-148,-114,-141,-129,-144,351,-134,-221,-132,-165,-115,-221,-221,-133,-221,-221,362,-138,-137,-135,-139,-221,-136,-221,-140,]),'LT':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,170,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,170,-173,-171,-175,170,-174,-170,-177,170,-168,-167,-176,170,170,170,170,-169,-192,-200,-201,-186,]),'COMMA':([1,2,3,5,6,8,9,12,14,15,16,17,19,21,22,23,25,28,31,33,34,36,37,38,39,40,42,43,44,46,48,49,50,54,56,57,58,59,60,61,64,65,67,73,82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,109,111,115,116,117,118,119,121,122,123,125,126,130,136,141,142,144,147,149,153,154,156,157,159,161,162,163,183,184,185,187,188,190,191,192,193,196,197,198,200,201,202,203,218,229,231,233,236,238,239,240,241,242,243,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,288,289,291,292,293,294,295,296,297,305,317,318,319,320,321,323,324,325,327,328,329,333,339,341,344,345,346,347,348,349,350,360,],[-221,-51,-61,-60,-49,-46,-47,-221,-45,-57,-53,-58,-44,-48,-149,-94,-221,-96,-50,-52,-55,-221,-56,-54,-59,-62,-43,-7,-8,-70,-69,-42,-95,-85,-84,-22,-101,-103,-21,127,-65,-63,-41,-97,-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,-164,-216,-215,-163,-207,-107,-221,194,195,-111,-89,198,-92,-104,-102,-65,-71,-79,-81,234,-191,-205,-206,-190,-188,-185,-147,-150,261,-189,-98,-100,-99,-110,-2,-109,-121,-119,-1,198,-86,-90,198,-64,-66,-113,261,-214,-73,-83,-72,-204,-203,261,-212,-202,322,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,261,-181,-169,-120,-112,-108,-88,-91,-93,-87,-116,332,261,-82,-80,-192,-200,-201,-151,-186,-148,-126,-122,-124,-114,261,261,261,-213,-165,-123,-125,-117,-115,261,]),'TYPEDEF':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,48,50,51,53,54,56,64,66,70,72,73,131,132,133,136,183,184,185,186,195,197,205,212,231,236,258,287,292,295,299,303,335,],[21,21,-51,-61,-60,-49,-46,-47,-27,-23,21,-45,-57,-53,-58,-44,21,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,21,-56,-54,-59,-62,21,-70,-69,-95,-24,21,-85,-84,21,-38,-39,21,-97,21,-29,-40,-71,-98,-100,-99,21,21,-86,-30,21,-73,-72,21,21,-88,-87,-127,-128,-129,]),'XOR':([82,83,84,85,86,87,90,95,97,99,101,102,104,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-166,-214,-209,173,-216,-215,-207,-191,-205,-206,-190,-188,-185,-189,-214,-204,-203,-202,-211,-172,173,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,173,-178,-180,173,-169,-192,-200,-201,-186,]),'AUTO':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,48,50,51,53,54,56,64,66,70,72,73,131,132,133,136,183,184,185,186,195,197,205,212,231,236,258,287,292,295,299,303,335,],[19,19,-51,-61,-60,-49,-46,-47,-27,-23,19,-45,-57,-53,-58,-44,19,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,19,-56,-54,-59,-62,19,-70,-69,-95,-24,19,-85,-84,19,-38,-39,19,-97,19,-29,-40,-71,-98,-100,-99,19,19,-86,-30,19,-73,-72,19,19,-88,-87,-127,-128,-129,]),'TIMES':([0,1,2,3,4,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,25,26,27,30,31,32,33,34,36,37,38,39,40,42,43,44,46,48,49,51,52,54,56,57,58,59,60,66,67,69,70,76,78,79,81,82,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,99,100,101,102,103,104,106,107,110,111,116,125,127,128,131,132,133,136,137,138,139,140,143,146,147,149,151,152,153,154,155,156,157,160,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,186,189,197,199,204,205,206,207,211,212,213,214,217,221,223,224,227,228,229,231,232,234,236,238,239,242,244,245,246,247,248,249,250,251,252,253,254,255,256,258,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,286,292,295,299,300,301,303,304,306,307,308,309,310,312,313,315,316,319,320,321,322,324,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[25,-221,-51,-61,25,-60,-49,-46,-47,-27,-23,-221,-45,-57,-53,-58,-44,25,-48,-149,-221,25,-26,-25,-50,-28,-52,-55,-221,-56,-54,-59,-62,-43,-7,-8,-70,-69,-42,-24,96,-85,-84,-22,25,-103,-21,-38,-41,25,-39,-221,-221,25,-198,-185,-219,-210,-220,-218,-208,96,-196,-187,-195,96,96,-217,-194,-199,96,-166,96,-214,-209,-193,175,-216,-215,-197,-207,25,-104,25,96,96,-29,-40,-71,-17,-68,-18,-67,96,96,-191,-205,96,96,-206,-190,96,-188,-185,25,-189,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,25,96,-86,96,96,-30,-32,-35,-31,96,96,-33,96,96,-130,-36,96,-34,-214,-73,96,25,-72,-204,-203,-202,-155,-158,-156,-152,-153,-157,-159,96,-161,-162,-154,-160,96,25,-211,96,175,175,175,175,175,175,175,175,175,175,-168,-167,175,175,175,175,175,-169,96,-88,-87,-127,-146,96,-128,-145,96,-143,-131,96,96,96,-142,96,96,-192,-200,-201,96,-186,96,96,-141,-129,-144,-134,96,-132,96,96,-133,96,96,96,-138,-137,-135,96,-139,96,-136,96,-140,]),'LPAREN':([0,1,2,3,4,5,6,8,9,10,11,12,13,14,15,16,17,19,20,21,22,23,25,26,27,28,30,31,32,33,34,36,37,38,39,40,42,43,44,46,48,49,50,51,52,54,56,57,58,59,60,66,67,69,70,73,76,78,79,81,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,100,101,102,103,106,107,110,111,116,125,126,127,128,131,132,133,136,137,138,139,140,143,146,149,151,152,153,155,160,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,189,191,192,197,199,204,205,206,207,211,212,213,214,215,217,220,221,222,223,224,227,228,229,230,231,232,234,236,238,239,242,244,245,246,247,248,249,250,251,252,253,254,255,256,258,259,260,261,286,288,292,295,299,300,301,303,304,306,307,308,309,310,312,313,315,316,320,321,322,326,327,328,329,332,334,335,336,338,340,342,343,347,348,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[4,-221,-51,-61,4,-60,-49,-46,-47,-27,-23,-221,4,-45,-57,-53,-58,-44,4,-48,-149,53,-221,4,-26,-96,-25,-50,-28,-52,-55,-221,-56,-54,-59,-62,-43,-7,-8,-70,-69,-42,53,-24,98,-85,-84,-22,-101,-103,-21,-38,-41,4,-39,-97,-221,-221,4,-198,-219,-210,-220,-218,-208,146,-196,152,-195,98,155,-217,-194,-199,98,155,-214,-209,-193,-216,-215,-197,-207,186,-104,-102,4,98,98,-29,-40,-71,-17,-68,-18,-67,98,98,-205,98,98,-206,98,258,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,-98,-100,-99,186,98,287,186,-86,98,98,-30,-32,-35,-31,98,98,-33,306,98,310,98,312,-130,-36,98,-34,-214,316,-73,98,4,-72,-204,-203,-202,-155,-158,-156,-152,-153,-157,-159,98,-161,-162,-154,-160,98,258,258,-211,98,98,287,-88,-87,-127,-146,98,-128,-145,98,-143,-131,98,98,98,-142,98,98,-200,-201,98,98,-126,-122,-124,98,-141,-129,-144,-134,98,355,-132,-123,-125,98,98,-133,98,98,98,-138,-137,-135,98,-139,98,-136,98,-140,]),'MINUSMINUS':([52,66,70,81,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,100,101,102,103,106,107,110,111,128,131,133,143,146,149,151,152,153,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,229,232,238,239,242,244,245,246,247,248,249,250,251,252,253,254,255,256,260,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,320,321,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[100,-38,-39,-198,-219,-210,-220,-218,-208,100,-196,153,-195,100,100,-217,-194,-199,100,100,-214,-209,-193,-216,-215,-197,-207,100,100,-40,100,100,-205,100,100,-206,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,-32,-35,-31,100,100,-33,100,100,-130,-36,100,-34,-214,100,-204,-203,-202,-155,-158,-156,-152,-153,-157,-159,100,-161,-162,-154,-160,100,-211,100,100,-127,-146,100,-128,-145,100,-143,-131,100,100,100,-142,100,100,-200,-201,100,100,100,-141,-129,-144,-134,100,-132,100,100,-133,100,100,100,-138,-137,-135,100,-139,100,-136,100,-140,]),'ID':([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,24,25,26,27,29,30,31,32,33,34,36,37,38,39,40,42,43,44,46,48,49,51,52,53,54,55,56,57,58,59,60,66,67,69,70,76,78,79,81,88,89,91,92,94,96,98,100,103,110,116,120,124,125,126,127,128,131,132,133,136,137,138,139,140,143,146,148,150,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,186,189,192,194,197,198,199,204,205,206,207,208,211,212,213,214,217,221,223,224,227,228,231,232,234,236,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,292,295,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[28,-221,-51,-61,28,-60,-49,48,-46,-47,-27,-23,-221,28,-45,-57,-53,-58,-75,-44,28,-48,-149,56,-221,28,-26,-74,-25,-50,-28,-52,-55,-221,-56,-54,-59,-62,-43,-7,-8,-70,-69,-42,-24,101,101,-85,123,-84,-22,-101,-103,-21,-38,-41,28,-39,-221,-221,28,-198,101,-196,-195,101,101,-194,101,101,-193,-197,28,123,123,-104,-102,28,101,229,-29,-40,-71,-17,-68,-18,-67,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,28,101,28,101,-86,123,101,101,-30,-32,-35,298,-31,229,101,-33,229,101,-130,-36,229,-34,-73,101,28,-72,-155,-158,-156,-152,-153,-157,-159,101,-161,-162,-154,-160,101,101,101,-88,-87,-127,-146,229,-128,-145,101,-143,-131,229,101,101,-142,229,101,101,101,101,-141,-129,-144,-134,229,-132,101,229,-133,229,101,229,-138,-137,-135,101,-139,229,-136,229,-140,]),'IF':([66,70,131,133,206,207,211,212,214,217,223,224,227,228,299,300,301,303,304,307,308,309,313,315,334,335,336,338,340,343,352,353,354,356,358,359,361,363,364,366,367,368,],[-38,-39,230,-40,-32,-35,-31,230,-33,230,-130,-36,230,-34,-127,-146,230,-128,-145,-143,-131,230,-142,230,-141,-129,-144,-134,230,-132,230,-133,230,230,-138,-137,-135,-139,230,-136,230,-140,]),'STRING_LITERAL':([52,66,70,81,88,89,91,92,94,96,98,100,103,110,128,131,133,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,206,207,211,212,213,214,217,221,223,224,227,228,232,244,245,246,247,248,249,250,251,252,253,254,255,256,261,286,299,300,301,303,304,306,307,308,309,310,312,313,315,316,322,326,332,334,335,336,338,340,343,351,352,353,354,355,356,358,359,361,362,363,364,366,367,368,],[102,-38,-39,-198,102,-196,-195,102,102,-194,102,102,-193,-197,102,102,-40,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,-32,-35,-31,102,102,-33,102,102,-130,-36,102,-34,102,-155,-158,-156,-152,-153,-157,-159,102,-161,-162,-154,-160,102,102,102,-127,-146,102,-128,-145,102,-143,-131,102,102,102,-142,102,102,102,102,102,-141,-129,-144,-134,102,-132,102,102,-133,102,102,102,-138,-137,-135,102,-139,102,-136,102,-140,]),'FLOAT':([0,1,2,3,5,6,8,9,10,11,12,14,15,16,17,19,20,21,22,23,27,28,30,31,32,33,34,36,37,38,39,40,41,46,47,48,50,51,53,54,56,64,66,70,72,73,74,75,76,77,78,80,98,131,132,133,134,135,136,145,146,183,184,185,186,195,197,205,212,231,235,236,258,287,292,295,299,303,335,],[38,38,-51,-61,-60,-49,-46,-47,-27,-23,38,-45,-57,-53,-58,-44,38,-48,-149,-94,-26,-96,-25,-50,-28,-52,-55,38,-56,-54,-59,-62,38,-70,38,-69,-95,-24,38,-85,-84,38,-38,-39,38,-97,38,38,38,-76,38,38,38,38,-29,-40,38,-77,-71,38,38,-98,-100,-99,38,38,-86,-30,38,-73,-78,-72,38,38,-88,-87,-127,-128,-129,]),'XOREQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,248,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'LSHIFTEQUAL':([82,83,84,85,86,87,90,95,97,101,102,106,107,111,147,149,153,154,156,157,163,229,238,239,242,260,319,320,321,324,],[-185,-219,-210,-220,-218,-208,-187,-217,-199,-214,-209,-216,-215,-207,-191,-205,-206,-190,-188,250,-189,-214,-204,-203,-202,-211,-192,-200,-201,-186,]),'RBRACKET':([52,82,83,84,85,86,87,90,93,95,97,99,101,102,104,105,106,107,108,109,111,147,149,153,154,156,157,159,161,163,189,238,239,240,242,260,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,279,280,285,286,319,320,321,323,324,325,330,346,],[-221,-185,-219,-210,-220,-218,-208,-187,-3,-217,-199,-166,-214,-209,-164,183,-216,-215,-4,-163,-207,-191,-205,-206,-190,-188,-185,-147,-150,-189,-221,-204,-203,320,-202,-211,-172,-184,-173,-171,-175,-179,-174,-170,-177,-182,-168,-167,-176,-183,-178,-180,-181,-169,329,-221,-192,-200,-201,-151,-186,-148,347,-165,]),} + +_lr_action = { } +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = { } + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'storage_class_specifier':([0,1,12,20,36,41,53,64,72,131,186,195,212,258,287,],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,]),'identifier_list_opt':([53,],[112,]),'selection_statement':([131,212,217,227,301,309,315,340,352,354,356,364,367,],[228,228,228,228,228,228,228,228,228,228,228,228,228,]),'constant':([52,88,92,94,98,100,128,131,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,212,213,217,221,227,232,251,256,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,]),'unary_expression':([52,88,92,94,98,100,128,131,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,212,213,217,221,227,232,251,256,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[82,147,82,156,157,163,157,157,82,157,157,157,157,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,157,82,82,82,82,157,157,157,157,82,157,82,157,82,157,82,157,157,157,157,157,157,157,157,82,157,157,157,157,157,157,157,157,157,157,]),'conditional_expression':([52,98,128,131,143,146,151,152,155,180,189,199,204,212,213,217,221,227,232,251,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[109,161,161,161,109,161,161,161,161,161,109,109,161,161,161,161,109,161,109,161,161,109,161,161,161,161,161,161,161,161,346,161,161,161,161,161,161,161,161,161,161,]),'struct_or_union_specifier':([0,1,12,20,36,41,47,53,64,72,74,75,76,78,80,98,131,134,145,146,186,195,212,258,287,],[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,]),'initializer':([128,204,332,],[202,296,349,]),'abstract_declarator_opt':([116,160,],[187,257,]),'iteration_statement':([131,212,217,227,301,309,315,340,352,354,356,364,367,],[207,207,207,207,207,207,207,207,207,207,207,207,207,]),'init_declarator_list':([26,69,],[61,61,]),'init_declarator_list_opt':([26,69,],[62,62,]),'struct_declaration_list':([47,74,80,],[75,134,145,]),'enumerator':([55,120,124,198,],[121,121,121,293,]),'pp_directive':([0,20,],[10,10,]),'abstract_declarator':([116,160,186,258,],[188,188,284,284,]),'declaration_specifiers_opt':([1,12,36,],[42,49,67,]),'external_declaration':([0,20,],[11,51,]),'type_specifier':([0,1,12,20,36,41,47,53,64,72,74,75,76,78,80,98,131,134,145,146,186,195,212,258,287,],[12,12,12,12,12,12,76,12,12,12,76,76,76,76,76,76,12,76,76,76,12,12,12,12,12,]),'compound_statement':([71,129,131,212,217,227,301,309,315,340,352,354,356,364,367,],[132,205,214,214,214,214,214,214,214,214,214,214,214,214,214,]),'pointer':([0,4,20,26,58,69,79,116,127,160,186,234,258,],[13,13,13,13,126,13,13,192,13,259,192,13,259,]),'type_name':([98,146,],[158,237,]),'postfix_expression':([52,88,92,94,98,100,128,131,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,212,213,217,221,227,232,251,256,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,]),'parameter_type_list_opt':([186,258,287,],[283,283,331,]),'expression_statement':([131,212,217,227,301,309,315,340,352,354,356,364,367,],[206,206,206,206,206,206,206,206,206,206,206,206,206,]),'unary_operator':([52,88,92,94,98,100,128,131,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,212,213,217,221,227,232,251,256,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,]),'cast_expression':([52,92,98,128,131,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,212,213,217,221,227,232,251,256,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[99,154,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,324,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,]),'initializer_list':([204,],[297,]),'struct_declarator_list':([79,],[144,]),'empty':([1,12,25,26,36,41,52,53,64,69,76,78,116,131,160,186,189,212,217,227,258,286,287,301,306,309,315,340,351,352,354,356,362,364,367,],[43,43,60,63,43,68,93,113,68,63,137,137,193,226,193,281,93,302,302,302,281,93,281,302,302,302,302,302,302,302,302,302,302,302,302,]),'assignment_operator':([157,],[251,]),'struct_or_union':([0,1,12,20,36,41,47,53,64,72,74,75,76,78,80,98,131,134,145,146,186,195,212,258,287,],[7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,]),'struct_declaration':([47,74,75,80,134,145,],[77,77,135,77,135,135,]),'assignment_expression':([98,128,131,146,151,152,155,180,204,212,213,217,227,251,261,301,306,309,310,312,315,316,322,332,340,351,352,354,355,356,362,364,367,],[159,203,159,159,159,241,159,159,203,159,159,159,159,323,325,159,159,159,159,159,159,159,345,203,159,159,159,159,159,159,159,159,159,]),'parameter_type_list':([53,186,258,287,],[114,282,282,282,]),'type_qualifier_list_opt':([25,],[58,]),'direct_declarator':([0,4,13,20,26,69,79,116,127,186,192,234,],[23,23,50,23,23,23,23,23,23,23,50,23,]),'type_qualifier_list':([25,],[57,]),'argument_expression_list':([152,],[243,]),'statement_list_opt':([131,],[209,]),'direct_abstract_declarator':([116,160,186,192,258,259,],[191,191,191,288,191,288,]),'specifier_qualifier_list_opt':([76,78,],[138,140,]),'constant_expression':([52,143,189,199,221,232,286,],[108,233,108,294,311,317,108,]),'expression_opt':([131,212,217,227,301,306,309,315,340,351,352,354,356,362,364,367,],[210,210,210,210,210,337,210,210,210,357,210,210,210,365,210,210,]),'primary_expression':([52,88,92,94,98,100,128,131,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,212,213,217,221,227,232,251,256,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,]),'declaration_specifiers':([0,1,12,20,36,41,53,64,72,131,186,195,212,258,287,],[26,44,44,26,44,69,116,69,69,69,116,116,69,116,116,]),'declaration':([0,20,41,64,72,131,212,],[27,27,70,70,133,70,133,]),'declarator':([0,4,20,26,69,79,116,127,186,234,],[41,45,41,64,130,142,190,130,45,142,]),'typedef_name':([0,1,12,20,36,41,47,53,64,72,74,75,76,78,80,98,131,134,145,146,186,195,212,258,287,],[17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,]),'identifier_list':([53,],[117,]),'jump_statement':([131,212,217,227,301,309,315,340,352,354,356,364,367,],[224,224,224,224,224,224,224,224,224,224,224,224,224,]),'declaration_list_opt':([41,64,],[71,129,]),'struct_declarator':([79,234,],[141,318,]),'function_definition':([0,20,],[30,30,]),'binary_expression':([52,98,128,131,143,146,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,199,204,212,213,217,221,227,232,251,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[104,104,104,104,104,104,104,104,104,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,104,279,280,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,]),'parameter_list':([53,186,258,287,],[118,118,118,118,]),'enum_specifier':([0,1,12,20,36,41,47,53,64,72,74,75,76,78,80,98,131,134,145,146,186,195,212,258,287,],[39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,]),'decl_body':([0,20,41,64,72,131,212,],[35,35,35,35,35,35,35,]),'type_qualifier':([0,1,12,20,25,36,41,47,53,57,64,72,74,75,76,78,80,98,131,134,145,146,186,195,212,258,287,],[36,36,36,36,59,36,36,78,36,125,36,36,78,78,78,78,78,78,36,78,78,78,36,36,36,36,36,]),'constant_expression_opt':([52,189,286,],[105,285,330,]),'enumerator_list':([55,120,124,],[122,196,200,]),'labeled_statement':([131,212,217,227,301,309,315,340,352,354,356,364,367,],[211,211,211,211,211,211,211,211,211,211,211,211,211,]),'declaration_list':([41,64,131,],[72,72,212,]),'specifier_qualifier_list':([47,74,75,76,78,80,98,134,145,146,],[79,79,79,139,139,79,160,79,79,160,]),'statement':([131,212,217,227,301,309,315,340,352,354,356,364,367,],[223,223,308,314,308,338,343,353,358,359,361,366,368,]),'translation_unit':([0,],[20,]),'init_declarator':([26,69,127,],[65,65,201,]),'parameter_declaration':([53,186,195,258,287,],[115,115,291,115,115,]),'identifier':([52,53,88,92,94,98,100,128,131,143,146,148,150,151,152,155,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,189,194,199,204,212,213,217,221,227,232,251,256,261,286,301,306,309,310,312,315,316,322,326,332,340,351,352,354,355,356,362,364,367,],[111,119,111,111,111,111,111,111,111,111,111,238,239,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,289,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,]),'expression':([98,131,146,151,155,180,212,213,217,227,301,306,309,310,312,315,316,340,351,352,354,355,356,362,364,367,],[162,218,162,240,162,278,218,305,218,218,218,218,218,339,341,218,344,218,218,218,218,360,218,218,218,218,]),'statement_list':([131,212,],[217,301,]),} + +_lr_goto = { } +for _k, _v in _lr_goto_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_goto: _lr_goto[_x] = { } + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> translation_unit","S'",1,None,None,None), + ('abstract_declarator_opt -> empty','abstract_declarator_opt',1,'p_abstract_declarator_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('abstract_declarator_opt -> abstract_declarator','abstract_declarator_opt',1,'p_abstract_declarator_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('constant_expression_opt -> empty','constant_expression_opt',1,'p_constant_expression_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('constant_expression_opt -> constant_expression','constant_expression_opt',1,'p_constant_expression_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('declaration_list_opt -> empty','declaration_list_opt',1,'p_declaration_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('declaration_list_opt -> declaration_list','declaration_list_opt',1,'p_declaration_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('declaration_specifiers_opt -> empty','declaration_specifiers_opt',1,'p_declaration_specifiers_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('declaration_specifiers_opt -> declaration_specifiers','declaration_specifiers_opt',1,'p_declaration_specifiers_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('expression_opt -> empty','expression_opt',1,'p_expression_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('expression_opt -> expression','expression_opt',1,'p_expression_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('identifier_list_opt -> empty','identifier_list_opt',1,'p_identifier_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('identifier_list_opt -> identifier_list','identifier_list_opt',1,'p_identifier_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('init_declarator_list_opt -> empty','init_declarator_list_opt',1,'p_init_declarator_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('init_declarator_list_opt -> init_declarator_list','init_declarator_list_opt',1,'p_init_declarator_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('parameter_type_list_opt -> empty','parameter_type_list_opt',1,'p_parameter_type_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('parameter_type_list_opt -> parameter_type_list','parameter_type_list_opt',1,'p_parameter_type_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('specifier_qualifier_list_opt -> empty','specifier_qualifier_list_opt',1,'p_specifier_qualifier_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('specifier_qualifier_list_opt -> specifier_qualifier_list','specifier_qualifier_list_opt',1,'p_specifier_qualifier_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('statement_list_opt -> empty','statement_list_opt',1,'p_statement_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('statement_list_opt -> statement_list','statement_list_opt',1,'p_statement_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('type_qualifier_list_opt -> empty','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',41), + ('type_qualifier_list_opt -> type_qualifier_list','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','/home/skybot/ext/parser/pycparser/plyparser.py',42), + ('translation_unit -> external_declaration','translation_unit',1,'p_translation_unit_1','/home/skybot/ext/parser/pycparser/c_parser.py',333), + ('translation_unit -> translation_unit external_declaration','translation_unit',2,'p_translation_unit_2','/home/skybot/ext/parser/pycparser/c_parser.py',340), + ('external_declaration -> function_definition','external_declaration',1,'p_external_declaration_1','/home/skybot/ext/parser/pycparser/c_parser.py',351), + ('external_declaration -> declaration','external_declaration',1,'p_external_declaration_2','/home/skybot/ext/parser/pycparser/c_parser.py',356), + ('external_declaration -> pp_directive','external_declaration',1,'p_external_declaration_3','/home/skybot/ext/parser/pycparser/c_parser.py',361), + ('pp_directive -> PPHASH','pp_directive',1,'p_pp_directive','/home/skybot/ext/parser/pycparser/c_parser.py',366), + ('function_definition -> declarator declaration_list_opt compound_statement','function_definition',3,'p_function_definition_1','/home/skybot/ext/parser/pycparser/c_parser.py',375), + ('function_definition -> declaration_specifiers declarator declaration_list_opt compound_statement','function_definition',4,'p_function_definition_2','/home/skybot/ext/parser/pycparser/c_parser.py',387), + ('statement -> labeled_statement','statement',1,'p_statement','/home/skybot/ext/parser/pycparser/c_parser.py',398), + ('statement -> expression_statement','statement',1,'p_statement','/home/skybot/ext/parser/pycparser/c_parser.py',399), + ('statement -> compound_statement','statement',1,'p_statement','/home/skybot/ext/parser/pycparser/c_parser.py',400), + ('statement -> selection_statement','statement',1,'p_statement','/home/skybot/ext/parser/pycparser/c_parser.py',401), + ('statement -> iteration_statement','statement',1,'p_statement','/home/skybot/ext/parser/pycparser/c_parser.py',402), + ('statement -> jump_statement','statement',1,'p_statement','/home/skybot/ext/parser/pycparser/c_parser.py',403), + ('decl_body -> declaration_specifiers init_declarator_list_opt','decl_body',2,'p_decl_body','/home/skybot/ext/parser/pycparser/c_parser.py',417), + ('declaration -> decl_body SEMI','declaration',2,'p_declaration','/home/skybot/ext/parser/pycparser/c_parser.py',497), + ('declaration_list -> declaration','declaration_list',1,'p_declaration_list','/home/skybot/ext/parser/pycparser/c_parser.py',506), + ('declaration_list -> declaration_list declaration','declaration_list',2,'p_declaration_list','/home/skybot/ext/parser/pycparser/c_parser.py',507), + ('declaration_specifiers -> type_qualifier declaration_specifiers_opt','declaration_specifiers',2,'p_declaration_specifiers_1','/home/skybot/ext/parser/pycparser/c_parser.py',512), + ('declaration_specifiers -> type_specifier declaration_specifiers_opt','declaration_specifiers',2,'p_declaration_specifiers_2','/home/skybot/ext/parser/pycparser/c_parser.py',517), + ('declaration_specifiers -> storage_class_specifier declaration_specifiers_opt','declaration_specifiers',2,'p_declaration_specifiers_3','/home/skybot/ext/parser/pycparser/c_parser.py',522), + ('storage_class_specifier -> AUTO','storage_class_specifier',1,'p_storage_class_specifier','/home/skybot/ext/parser/pycparser/c_parser.py',527), + ('storage_class_specifier -> REGISTER','storage_class_specifier',1,'p_storage_class_specifier','/home/skybot/ext/parser/pycparser/c_parser.py',528), + ('storage_class_specifier -> STATIC','storage_class_specifier',1,'p_storage_class_specifier','/home/skybot/ext/parser/pycparser/c_parser.py',529), + ('storage_class_specifier -> EXTERN','storage_class_specifier',1,'p_storage_class_specifier','/home/skybot/ext/parser/pycparser/c_parser.py',530), + ('storage_class_specifier -> TYPEDEF','storage_class_specifier',1,'p_storage_class_specifier','/home/skybot/ext/parser/pycparser/c_parser.py',531), + ('type_specifier -> VOID','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',536), + ('type_specifier -> CHAR','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',537), + ('type_specifier -> SHORT','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',538), + ('type_specifier -> INT','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',539), + ('type_specifier -> LONG','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',540), + ('type_specifier -> FLOAT','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',541), + ('type_specifier -> DOUBLE','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',542), + ('type_specifier -> SIGNED','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',543), + ('type_specifier -> UNSIGNED','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',544), + ('type_specifier -> typedef_name','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',545), + ('type_specifier -> enum_specifier','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',546), + ('type_specifier -> struct_or_union_specifier','type_specifier',1,'p_type_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',547), + ('type_qualifier -> CONST','type_qualifier',1,'p_type_qualifier','/home/skybot/ext/parser/pycparser/c_parser.py',552), + ('type_qualifier -> VOLATILE','type_qualifier',1,'p_type_qualifier','/home/skybot/ext/parser/pycparser/c_parser.py',553), + ('init_declarator_list -> init_declarator','init_declarator_list',1,'p_init_declarator_list','/home/skybot/ext/parser/pycparser/c_parser.py',558), + ('init_declarator_list -> init_declarator_list COMMA init_declarator','init_declarator_list',3,'p_init_declarator_list','/home/skybot/ext/parser/pycparser/c_parser.py',559), + ('init_declarator -> declarator','init_declarator',1,'p_init_declarator','/home/skybot/ext/parser/pycparser/c_parser.py',567), + ('init_declarator -> declarator EQUALS initializer','init_declarator',3,'p_init_declarator','/home/skybot/ext/parser/pycparser/c_parser.py',568), + ('specifier_qualifier_list -> type_qualifier specifier_qualifier_list_opt','specifier_qualifier_list',2,'p_specifier_qualifier_list_1','/home/skybot/ext/parser/pycparser/c_parser.py',573), + ('specifier_qualifier_list -> type_specifier specifier_qualifier_list_opt','specifier_qualifier_list',2,'p_specifier_qualifier_list_2','/home/skybot/ext/parser/pycparser/c_parser.py',578), + ('struct_or_union_specifier -> struct_or_union ID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',586), + ('struct_or_union_specifier -> struct_or_union TYPEID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',587), + ('struct_or_union_specifier -> struct_or_union LBRACE struct_declaration_list RBRACE','struct_or_union_specifier',4,'p_struct_or_union_specifier_2','/home/skybot/ext/parser/pycparser/c_parser.py',596), + ('struct_or_union_specifier -> struct_or_union ID LBRACE struct_declaration_list RBRACE','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','/home/skybot/ext/parser/pycparser/c_parser.py',605), + ('struct_or_union_specifier -> struct_or_union TYPEID LBRACE struct_declaration_list RBRACE','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','/home/skybot/ext/parser/pycparser/c_parser.py',606), + ('struct_or_union -> STRUCT','struct_or_union',1,'p_struct_or_union','/home/skybot/ext/parser/pycparser/c_parser.py',615), + ('struct_or_union -> UNION','struct_or_union',1,'p_struct_or_union','/home/skybot/ext/parser/pycparser/c_parser.py',616), + ('struct_declaration_list -> struct_declaration','struct_declaration_list',1,'p_struct_declaration_list','/home/skybot/ext/parser/pycparser/c_parser.py',623), + ('struct_declaration_list -> struct_declaration_list struct_declaration','struct_declaration_list',2,'p_struct_declaration_list','/home/skybot/ext/parser/pycparser/c_parser.py',624), + ('struct_declaration -> specifier_qualifier_list struct_declarator_list SEMI','struct_declaration',3,'p_struct_declaration_1','/home/skybot/ext/parser/pycparser/c_parser.py',629), + ('struct_declarator_list -> struct_declarator','struct_declarator_list',1,'p_struct_declarator_list','/home/skybot/ext/parser/pycparser/c_parser.py',650), + ('struct_declarator_list -> struct_declarator_list COMMA struct_declarator','struct_declarator_list',3,'p_struct_declarator_list','/home/skybot/ext/parser/pycparser/c_parser.py',651), + ('struct_declarator -> declarator','struct_declarator',1,'p_struct_declarator_1','/home/skybot/ext/parser/pycparser/c_parser.py',659), + ('struct_declarator -> declarator COLON constant_expression','struct_declarator',3,'p_struct_declarator_2','/home/skybot/ext/parser/pycparser/c_parser.py',664), + ('struct_declarator -> COLON constant_expression','struct_declarator',2,'p_struct_declarator_2','/home/skybot/ext/parser/pycparser/c_parser.py',665), + ('enum_specifier -> ENUM ID','enum_specifier',2,'p_enum_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',673), + ('enum_specifier -> ENUM TYPEID','enum_specifier',2,'p_enum_specifier_1','/home/skybot/ext/parser/pycparser/c_parser.py',674), + ('enum_specifier -> ENUM LBRACE enumerator_list RBRACE','enum_specifier',4,'p_enum_specifier_2','/home/skybot/ext/parser/pycparser/c_parser.py',679), + ('enum_specifier -> ENUM ID LBRACE enumerator_list RBRACE','enum_specifier',5,'p_enum_specifier_3','/home/skybot/ext/parser/pycparser/c_parser.py',684), + ('enum_specifier -> ENUM TYPEID LBRACE enumerator_list RBRACE','enum_specifier',5,'p_enum_specifier_3','/home/skybot/ext/parser/pycparser/c_parser.py',685), + ('enumerator_list -> enumerator','enumerator_list',1,'p_enumerator_list','/home/skybot/ext/parser/pycparser/c_parser.py',690), + ('enumerator_list -> enumerator_list COMMA','enumerator_list',2,'p_enumerator_list','/home/skybot/ext/parser/pycparser/c_parser.py',691), + ('enumerator_list -> enumerator_list COMMA enumerator','enumerator_list',3,'p_enumerator_list','/home/skybot/ext/parser/pycparser/c_parser.py',692), + ('enumerator -> ID','enumerator',1,'p_enumerator','/home/skybot/ext/parser/pycparser/c_parser.py',703), + ('enumerator -> ID EQUALS constant_expression','enumerator',3,'p_enumerator','/home/skybot/ext/parser/pycparser/c_parser.py',704), + ('declarator -> direct_declarator','declarator',1,'p_declarator_1','/home/skybot/ext/parser/pycparser/c_parser.py',716), + ('declarator -> pointer direct_declarator','declarator',2,'p_declarator_2','/home/skybot/ext/parser/pycparser/c_parser.py',721), + ('direct_declarator -> ID','direct_declarator',1,'p_direct_declarator_1','/home/skybot/ext/parser/pycparser/c_parser.py',726), + ('direct_declarator -> LPAREN declarator RPAREN','direct_declarator',3,'p_direct_declarator_2','/home/skybot/ext/parser/pycparser/c_parser.py',735), + ('direct_declarator -> direct_declarator LBRACKET constant_expression_opt RBRACKET','direct_declarator',4,'p_direct_declarator_3','/home/skybot/ext/parser/pycparser/c_parser.py',740), + ('direct_declarator -> direct_declarator LPAREN parameter_type_list RPAREN','direct_declarator',4,'p_direct_declarator_4','/home/skybot/ext/parser/pycparser/c_parser.py',750), + ('direct_declarator -> direct_declarator LPAREN identifier_list_opt RPAREN','direct_declarator',4,'p_direct_declarator_4','/home/skybot/ext/parser/pycparser/c_parser.py',751), + ('pointer -> TIMES type_qualifier_list_opt','pointer',2,'p_pointer','/home/skybot/ext/parser/pycparser/c_parser.py',761), + ('pointer -> TIMES type_qualifier_list_opt pointer','pointer',3,'p_pointer','/home/skybot/ext/parser/pycparser/c_parser.py',762), + ('type_qualifier_list -> type_qualifier','type_qualifier_list',1,'p_type_qualifier_list','/home/skybot/ext/parser/pycparser/c_parser.py',772), + ('type_qualifier_list -> type_qualifier_list type_qualifier','type_qualifier_list',2,'p_type_qualifier_list','/home/skybot/ext/parser/pycparser/c_parser.py',773), + ('parameter_type_list -> parameter_list','parameter_type_list',1,'p_parameter_type_list','/home/skybot/ext/parser/pycparser/c_parser.py',778), + ('parameter_type_list -> parameter_list COMMA ELLIPSIS','parameter_type_list',3,'p_parameter_type_list','/home/skybot/ext/parser/pycparser/c_parser.py',779), + ('parameter_list -> parameter_declaration','parameter_list',1,'p_parameter_list','/home/skybot/ext/parser/pycparser/c_parser.py',787), + ('parameter_list -> parameter_list COMMA parameter_declaration','parameter_list',3,'p_parameter_list','/home/skybot/ext/parser/pycparser/c_parser.py',788), + ('parameter_declaration -> declaration_specifiers declarator','parameter_declaration',2,'p_parameter_declaration_1','/home/skybot/ext/parser/pycparser/c_parser.py',797), + ('parameter_declaration -> declaration_specifiers abstract_declarator_opt','parameter_declaration',2,'p_parameter_declaration_2','/home/skybot/ext/parser/pycparser/c_parser.py',815), + ('identifier_list -> identifier','identifier_list',1,'p_identifier_list','/home/skybot/ext/parser/pycparser/c_parser.py',827), + ('identifier_list -> identifier_list COMMA identifier','identifier_list',3,'p_identifier_list','/home/skybot/ext/parser/pycparser/c_parser.py',828), + ('initializer -> assignment_expression','initializer',1,'p_initializer_1','/home/skybot/ext/parser/pycparser/c_parser.py',837), + ('initializer -> LBRACE initializer_list RBRACE','initializer',3,'p_initializer_2','/home/skybot/ext/parser/pycparser/c_parser.py',842), + ('initializer -> LBRACE initializer_list COMMA RBRACE','initializer',4,'p_initializer_2','/home/skybot/ext/parser/pycparser/c_parser.py',843), + ('initializer_list -> initializer','initializer_list',1,'p_initializer_list','/home/skybot/ext/parser/pycparser/c_parser.py',848), + ('initializer_list -> initializer_list COMMA initializer','initializer_list',3,'p_initializer_list','/home/skybot/ext/parser/pycparser/c_parser.py',849), + ('type_name -> specifier_qualifier_list abstract_declarator_opt','type_name',2,'p_type_name','/home/skybot/ext/parser/pycparser/c_parser.py',858), + ('abstract_declarator -> pointer','abstract_declarator',1,'p_abstract_declarator_1','/home/skybot/ext/parser/pycparser/c_parser.py',873), + ('abstract_declarator -> pointer direct_abstract_declarator','abstract_declarator',2,'p_abstract_declarator_2','/home/skybot/ext/parser/pycparser/c_parser.py',881), + ('abstract_declarator -> direct_abstract_declarator','abstract_declarator',1,'p_abstract_declarator_3','/home/skybot/ext/parser/pycparser/c_parser.py',886), + ('direct_abstract_declarator -> LPAREN abstract_declarator RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_1','/home/skybot/ext/parser/pycparser/c_parser.py',896), + ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET constant_expression_opt RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_2','/home/skybot/ext/parser/pycparser/c_parser.py',900), + ('direct_abstract_declarator -> LBRACKET constant_expression_opt RBRACKET','direct_abstract_declarator',3,'p_direct_abstract_declarator_3','/home/skybot/ext/parser/pycparser/c_parser.py',910), + ('direct_abstract_declarator -> direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',4,'p_direct_abstract_declarator_4','/home/skybot/ext/parser/pycparser/c_parser.py',918), + ('direct_abstract_declarator -> LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_5','/home/skybot/ext/parser/pycparser/c_parser.py',928), + ('compound_statement -> LBRACE statement_list_opt RBRACE','compound_statement',3,'p_compound_statement_1','/home/skybot/ext/parser/pycparser/c_parser.py',936), + ('compound_statement -> LBRACE declaration_list RBRACE','compound_statement',3,'p_compound_statement_2','/home/skybot/ext/parser/pycparser/c_parser.py',943), + ('compound_statement -> LBRACE declaration_list statement_list RBRACE','compound_statement',4,'p_compound_statement_3','/home/skybot/ext/parser/pycparser/c_parser.py',950), + ('statement_list -> statement','statement_list',1,'p_statement_list','/home/skybot/ext/parser/pycparser/c_parser.py',964), + ('statement_list -> statement_list statement','statement_list',2,'p_statement_list','/home/skybot/ext/parser/pycparser/c_parser.py',965), + ('labeled_statement -> ID COLON statement','labeled_statement',3,'p_labeled_statement_1','/home/skybot/ext/parser/pycparser/c_parser.py',973), + ('labeled_statement -> CASE constant_expression COLON statement','labeled_statement',4,'p_labeled_statement_2','/home/skybot/ext/parser/pycparser/c_parser.py',977), + ('labeled_statement -> DEFAULT COLON statement','labeled_statement',3,'p_labeled_statement_3','/home/skybot/ext/parser/pycparser/c_parser.py',981), + ('selection_statement -> IF LPAREN expression RPAREN statement','selection_statement',5,'p_selection_statement_1','/home/skybot/ext/parser/pycparser/c_parser.py',985), + ('selection_statement -> IF LPAREN expression RPAREN statement ELSE statement','selection_statement',7,'p_selection_statement_2','/home/skybot/ext/parser/pycparser/c_parser.py',989), + ('selection_statement -> SWITCH LPAREN expression RPAREN statement','selection_statement',5,'p_selection_statement_3','/home/skybot/ext/parser/pycparser/c_parser.py',993), + ('iteration_statement -> WHILE LPAREN expression RPAREN statement','iteration_statement',5,'p_iteration_statement_1','/home/skybot/ext/parser/pycparser/c_parser.py',997), + ('iteration_statement -> DO statement WHILE LPAREN expression RPAREN','iteration_statement',6,'p_iteration_statement_2','/home/skybot/ext/parser/pycparser/c_parser.py',1001), + ('iteration_statement -> FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN statement','iteration_statement',9,'p_iteration_statement_3','/home/skybot/ext/parser/pycparser/c_parser.py',1005), + ('jump_statement -> GOTO ID SEMI','jump_statement',3,'p_jump_statement_1','/home/skybot/ext/parser/pycparser/c_parser.py',1009), + ('jump_statement -> BREAK SEMI','jump_statement',2,'p_jump_statement_2','/home/skybot/ext/parser/pycparser/c_parser.py',1013), + ('jump_statement -> CONTINUE SEMI','jump_statement',2,'p_jump_statement_3','/home/skybot/ext/parser/pycparser/c_parser.py',1017), + ('jump_statement -> RETURN expression SEMI','jump_statement',3,'p_jump_statement_4','/home/skybot/ext/parser/pycparser/c_parser.py',1021), + ('jump_statement -> RETURN SEMI','jump_statement',2,'p_jump_statement_4','/home/skybot/ext/parser/pycparser/c_parser.py',1022), + ('expression_statement -> expression_opt SEMI','expression_statement',2,'p_expression_statement','/home/skybot/ext/parser/pycparser/c_parser.py',1027), + ('expression -> assignment_expression','expression',1,'p_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1031), + ('expression -> expression COMMA assignment_expression','expression',3,'p_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1032), + ('typedef_name -> TYPEID','typedef_name',1,'p_typedef_name','/home/skybot/ext/parser/pycparser/c_parser.py',1044), + ('assignment_expression -> conditional_expression','assignment_expression',1,'p_assignment_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1048), + ('assignment_expression -> unary_expression assignment_operator assignment_expression','assignment_expression',3,'p_assignment_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1049), + ('assignment_operator -> EQUALS','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1062), + ('assignment_operator -> XOREQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1063), + ('assignment_operator -> TIMESEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1064), + ('assignment_operator -> DIVEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1065), + ('assignment_operator -> MODEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1066), + ('assignment_operator -> PLUSEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1067), + ('assignment_operator -> MINUSEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1068), + ('assignment_operator -> LSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1069), + ('assignment_operator -> RSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1070), + ('assignment_operator -> ANDEQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1071), + ('assignment_operator -> OREQUAL','assignment_operator',1,'p_assignment_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1072), + ('constant_expression -> conditional_expression','constant_expression',1,'p_constant_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1077), + ('conditional_expression -> binary_expression','conditional_expression',1,'p_conditional_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1081), + ('conditional_expression -> binary_expression CONDOP expression COLON conditional_expression','conditional_expression',5,'p_conditional_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1082), + ('binary_expression -> cast_expression','binary_expression',1,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1090), + ('binary_expression -> binary_expression TIMES binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1091), + ('binary_expression -> binary_expression DIVIDE binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1092), + ('binary_expression -> binary_expression MOD binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1093), + ('binary_expression -> binary_expression PLUS binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1094), + ('binary_expression -> binary_expression MINUS binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1095), + ('binary_expression -> binary_expression RSHIFT binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1096), + ('binary_expression -> binary_expression LSHIFT binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1097), + ('binary_expression -> binary_expression LT binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1098), + ('binary_expression -> binary_expression LE binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1099), + ('binary_expression -> binary_expression GE binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1100), + ('binary_expression -> binary_expression GT binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1101), + ('binary_expression -> binary_expression EQ binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1102), + ('binary_expression -> binary_expression NE binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1103), + ('binary_expression -> binary_expression AND binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1104), + ('binary_expression -> binary_expression OR binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1105), + ('binary_expression -> binary_expression XOR binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1106), + ('binary_expression -> binary_expression LAND binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1107), + ('binary_expression -> binary_expression LOR binary_expression','binary_expression',3,'p_binary_expression','/home/skybot/ext/parser/pycparser/c_parser.py',1108), + ('cast_expression -> unary_expression','cast_expression',1,'p_cast_expression_1','/home/skybot/ext/parser/pycparser/c_parser.py',1116), + ('cast_expression -> LPAREN type_name RPAREN cast_expression','cast_expression',4,'p_cast_expression_2','/home/skybot/ext/parser/pycparser/c_parser.py',1120), + ('unary_expression -> postfix_expression','unary_expression',1,'p_unary_expression_1','/home/skybot/ext/parser/pycparser/c_parser.py',1124), + ('unary_expression -> PLUSPLUS unary_expression','unary_expression',2,'p_unary_expression_2','/home/skybot/ext/parser/pycparser/c_parser.py',1128), + ('unary_expression -> MINUSMINUS unary_expression','unary_expression',2,'p_unary_expression_2','/home/skybot/ext/parser/pycparser/c_parser.py',1129), + ('unary_expression -> unary_operator cast_expression','unary_expression',2,'p_unary_expression_2','/home/skybot/ext/parser/pycparser/c_parser.py',1130), + ('unary_expression -> SIZEOF unary_expression','unary_expression',2,'p_unary_expression_3','/home/skybot/ext/parser/pycparser/c_parser.py',1135), + ('unary_expression -> SIZEOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression_3','/home/skybot/ext/parser/pycparser/c_parser.py',1136), + ('unary_operator -> AND','unary_operator',1,'p_unary_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1144), + ('unary_operator -> TIMES','unary_operator',1,'p_unary_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1145), + ('unary_operator -> PLUS','unary_operator',1,'p_unary_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1146), + ('unary_operator -> MINUS','unary_operator',1,'p_unary_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1147), + ('unary_operator -> NOT','unary_operator',1,'p_unary_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1148), + ('unary_operator -> LNOT','unary_operator',1,'p_unary_operator','/home/skybot/ext/parser/pycparser/c_parser.py',1149), + ('postfix_expression -> primary_expression','postfix_expression',1,'p_postfix_exptession_1','/home/skybot/ext/parser/pycparser/c_parser.py',1154), + ('postfix_expression -> postfix_expression LBRACKET expression RBRACKET','postfix_expression',4,'p_postfix_exptession_2','/home/skybot/ext/parser/pycparser/c_parser.py',1158), + ('postfix_expression -> postfix_expression LPAREN argument_expression_list RPAREN','postfix_expression',4,'p_postfix_exptession_3','/home/skybot/ext/parser/pycparser/c_parser.py',1162), + ('postfix_expression -> postfix_expression LPAREN RPAREN','postfix_expression',3,'p_postfix_exptession_3','/home/skybot/ext/parser/pycparser/c_parser.py',1163), + ('postfix_expression -> postfix_expression PERIOD identifier','postfix_expression',3,'p_postfix_expression_4','/home/skybot/ext/parser/pycparser/c_parser.py',1168), + ('postfix_expression -> postfix_expression ARROW identifier','postfix_expression',3,'p_postfix_expression_4','/home/skybot/ext/parser/pycparser/c_parser.py',1169), + ('postfix_expression -> postfix_expression PLUSPLUS','postfix_expression',2,'p_postfix_expression_5','/home/skybot/ext/parser/pycparser/c_parser.py',1174), + ('postfix_expression -> postfix_expression MINUSMINUS','postfix_expression',2,'p_postfix_expression_5','/home/skybot/ext/parser/pycparser/c_parser.py',1175), + ('primary_expression -> identifier','primary_expression',1,'p_primary_expression_1','/home/skybot/ext/parser/pycparser/c_parser.py',1180), + ('primary_expression -> constant','primary_expression',1,'p_primary_expression_2','/home/skybot/ext/parser/pycparser/c_parser.py',1184), + ('primary_expression -> STRING_LITERAL','primary_expression',1,'p_primary_expression_3','/home/skybot/ext/parser/pycparser/c_parser.py',1188), + ('primary_expression -> WSTRING_LITERAL','primary_expression',1,'p_primary_expression_3','/home/skybot/ext/parser/pycparser/c_parser.py',1189), + ('primary_expression -> LPAREN expression RPAREN','primary_expression',3,'p_primary_expression_4','/home/skybot/ext/parser/pycparser/c_parser.py',1195), + ('argument_expression_list -> assignment_expression','argument_expression_list',1,'p_argument_expression_list','/home/skybot/ext/parser/pycparser/c_parser.py',1199), + ('argument_expression_list -> argument_expression_list COMMA assignment_expression','argument_expression_list',3,'p_argument_expression_list','/home/skybot/ext/parser/pycparser/c_parser.py',1200), + ('identifier -> ID','identifier',1,'p_identifier','/home/skybot/ext/parser/pycparser/c_parser.py',1209), + ('constant -> INT_CONST_DEC','constant',1,'p_constant_1','/home/skybot/ext/parser/pycparser/c_parser.py',1213), + ('constant -> INT_CONST_OCT','constant',1,'p_constant_1','/home/skybot/ext/parser/pycparser/c_parser.py',1214), + ('constant -> INT_CONST_HEX','constant',1,'p_constant_1','/home/skybot/ext/parser/pycparser/c_parser.py',1215), + ('constant -> FLOAT_CONST','constant',1,'p_constant_2','/home/skybot/ext/parser/pycparser/c_parser.py',1221), + ('constant -> CHAR_CONST','constant',1,'p_constant_3','/home/skybot/ext/parser/pycparser/c_parser.py',1226), + ('constant -> WCHAR_CONST','constant',1,'p_constant_3','/home/skybot/ext/parser/pycparser/c_parser.py',1227), + ('empty -> ','empty',0,'p_empty','/home/skybot/ext/parser/pycparser/c_parser.py',1233), +] diff --git a/plugins_available/pyexec.py b/plugins_available/pyexec.py new file mode 100644 index 0000000..f6e3e78 --- /dev/null +++ b/plugins_available/pyexec.py @@ -0,0 +1,25 @@ +import urllib +import re + +from util import hook + + +re_lineends = re.compile(r'[\r\n]*') + + +@hook.command +def py(inp): + ".py -- executes python code " + + if not inp: + return py.__doc__ + + res = urllib.urlopen("http://eval.appspot.com/eval?statement=%s" % + urllib.quote(inp.strip(), safe='')).readlines() + if len(res) == 0: + return + res[0] = re_lineends.split(res[0])[0] + if not res[0] == 'Traceback (most recent call last):': + return res[0] + else: + return res[-1] diff --git a/plugins_available/remember.py b/plugins_available/remember.py new file mode 100644 index 0000000..4d2904e --- /dev/null +++ b/plugins_available/remember.py @@ -0,0 +1,89 @@ +""" +remember.py: written by Scaevolus 2009 +""" + +import os +import thread +import codecs + +from util import hook + + +lock = thread.allocate_lock() +memory = {} + + +def load_memory(filename, mtimes={}): + if not os.path.exists(filename): + return {} + mtime = os.stat(filename).st_mtime + if mtimes.get(filename, 0) != mtime: + mtimes[filename] = mtime + return dict((x.split(None, 1)[0].lower(), x.strip()) for x in + codecs.open(filename, 'r', 'utf-8')) + + +def save_memory(filename, memory): + out = codecs.open(filename, 'w', 'utf-8') + out.write('\n'.join(sorted(memory.itervalues()))) + out.flush() + out.close() + + +def make_filename(dir, chan): + return os.path.join(dir, 'memory') + + +@hook.command +def remember(bot, input): + ".remember -- maps word to data in the memory" + with lock: + filename = make_filename(bot.persist_dir, input.chan) + memory.setdefault(filename, load_memory(filename)) + + try: + head, tail = input.inp.split(None, 1) + except ValueError: + return remember.__doc__ + + tail = tail.strip() + low = head.lower() + if low not in memory[filename]: + input.reply("done.") + else: + input.reply('forgetting that "%s", remembering this instead.' % + memory[filename][low]) + memory[filename][low] = input.inp.strip() + save_memory(filename, memory[filename]) + + +@hook.command +def forget(bot, input): + ".forget -- forgets the mapping that word had" + with lock: + filename = make_filename(bot.persist_dir, input.chan) + memory.setdefault(filename, load_memory(filename)) + + if not input.inp.strip(): + return forget.__doc__ + + low = input.inp.strip().lower() + if low not in memory[filename]: + return "I don't know about that." + if not hasattr(input, 'chan'): + return "I won't forget anything in private." + input.say("Forgot that %s" % memory[filename][low]) + del memory[filename][low] + save_memory(filename, memory[filename]) + + +@hook.command(hook='\?(.+)', prefix=False) +def question(bot, input): + "? -- shows what data is associated with word" + with lock: + filename = make_filename(bot.persist_dir, input.chan) + memory.setdefault(filename, load_memory(filename)) + + word = input.inp.split()[0].lower() + if word in memory[filename]: + input.say("%s" % memory[filename][word]) diff --git a/plugins_available/seen.py b/plugins_available/seen.py new file mode 100644 index 0000000..64308f5 --- /dev/null +++ b/plugins_available/seen.py @@ -0,0 +1,77 @@ +" seen.py: written by sklnd in about two beers July 2009" + +import os +import time +from datetime import datetime +import sqlite3 + +from util import hook, timesince + + +dbname = "skybot.db" + + +def adapt_datetime(ts): + return time.mktime(ts.timetuple()) + +sqlite3.register_adapter(datetime, adapt_datetime) + + +@hook.tee +def seeninput(bot, input): + if input.command != 'PRIVMSG': + return + + dbpath = os.path.join(bot.persist_dir, dbname) + + conn = dbconnect(dbpath) + cursor = conn.cursor() + cursor.execute("INSERT OR REPLACE INTO seen(name, date, quote, chan)" + "values(?,?,?,?)", (input.nick.lower(), datetime.now(), + input.msg, input.chan)) + conn.commit() + conn.close() + + +@hook.command +def seen(bot, input): + ".seen -- Tell when a nickname was last in active in irc" + + if len(input.msg) < 6: + return seen.__doc__ + + query = input.inp.strip() + + if query.lower() == input.nick.lower(): + return "Have you looked in a mirror lately?" + + dbpath = os.path.join(bot.persist_dir, dbname) + conn = dbconnect(dbpath) + cursor = conn.cursor() + + command = "SELECT date, quote FROM seen WHERE name LIKE ? AND chan = ?" \ + "ORDER BY date DESC" + cursor.execute(command, (query, input.chan)) + results = cursor.fetchone() + + conn.close() + + if(results != None): + reltime = timesince.timesince(datetime.fromtimestamp(results[0])) + return '%s was last seen %s ago saying: %s' % \ + (query, reltime, results[1]) + else: + return "I've never seen %s" % query + + +def dbconnect(db): + "check to see that our db has the the seen table and return a connection." + conn = sqlite3.connect(db) + + conn.execute("CREATE TABLE IF NOT EXISTS " + "seen(name varchar(30) not null, date datetime not null, " + "quote varchar(250) not null, chan varchar(32) not null, " + "primary key(name, chan));") + conn.commit() + + return conn diff --git a/plugins_available/sieve.py b/plugins_available/sieve.py new file mode 100644 index 0000000..bb3fde3 --- /dev/null +++ b/plugins_available/sieve.py @@ -0,0 +1,29 @@ +import re + +from util import hook + + +@hook.sieve +def sieve_suite(bot, input, func, args): + events = args.get('events', ['PRIVMSG']) + + if input.command not in events and events != '*': + return None + + if input.nick.lower()[-3:] == 'bot' and args.get('ignorebots', True): + return None + + hook = args.get('hook', r'(.*)') + + if args.get('prefix', True): + # add a prefix, unless it's a private message + hook = (r'^(?:[.!]|' if input.chan != input.nick else r'^(?:[.!]?|') \ + + input.conn.nick + r'[:,]*\s*)' + hook + + input.re = re.match(hook, input.msg, flags=re.I) + if input.re is None: + return None + + input.inp = ' '.join(input.re.groups()) + + return input diff --git a/plugins_available/suggest.py b/plugins_available/suggest.py new file mode 100644 index 0000000..55cbf12 --- /dev/null +++ b/plugins_available/suggest.py @@ -0,0 +1,36 @@ +import random +import urllib +import urllib2 +import re +import json + +from util import hook + +@hook.command +def suggest(inp): + ".suggest [#n] -- gets a random/the nth suggested google search" + if not inp.strip(): + return suggest.__doc__ + + m = re.match('^#(\d+) (.+)$', inp) + if m: + num, inp = m.groups() + num = int(num) + if num > 10: + return 'can only get first ten suggestions' + else: + num = 0 + + url = 'http://google.com/complete/search?q=' + urllib.quote(inp, safe='') + page = urllib2.urlopen(url).read() + page_json = page.split('(', 1)[1][:-1] + suggestions = json.loads(page_json)[1] + if not suggestions: + return 'no suggestions found' + if num: + if len(suggestions) + 1 <= num: + return 'only got %d suggestions' % len(suggestions) + out = suggestions[num - 1] + else: + out = random.choice(suggestions) + return '#%d: %s (%s)' % (int(out[2][0]) + 1, out[0], out[1]) diff --git a/plugins_available/tell.py b/plugins_available/tell.py new file mode 100644 index 0000000..bdfc9ec --- /dev/null +++ b/plugins_available/tell.py @@ -0,0 +1,131 @@ +" tell.py: written by sklnd in July 2009" + +import os +import time +from datetime import datetime +import sqlite3 + +from util import hook, timesince + + +dbname = "skybot.db" + + +def adapt_datetime(ts): + return time.mktime(ts.timetuple()) + +sqlite3.register_adapter(datetime, adapt_datetime) + + +@hook.command(hook=r'(.*)', prefix=False, ignorebots=True) +def tellinput(bot, input): + dbpath = os.path.join(bot.persist_dir, dbname) + conn = dbconnect(dbpath) + + cursor = conn.cursor() + command = "select count(name) from tell where name LIKE ? and chan = ?" + results = cursor.execute(command, (input.nick, input.chan)).fetchone() + + + if results[0] > 0: + command = "select id, user_from, quote, date from tell " \ + "where name LIKE ? and chan = ? limit 1" + tell = cursor.execute(command, (input.nick, input.chan)).fetchall()[0] + more = results[0] - 1 + reltime = timesince.timesince(datetime.fromtimestamp(tell[3])) + + reply = "%(teller)s said %(reltime)s ago: %(quote)s" % \ + {"teller": tell[1], "quote": tell[2], "reltime": reltime} + if more: + reply += " (+%(more)d more, to view say .showtells)" % {"more": more} + + input.reply(reply) + command = "delete from tell where id = ?" + cursor.execute(command, (tell[0], )) + + conn.commit() + conn.close() + +@hook.command +def showtells(bot, input): + ".showtells -- view all pending tell messages (sent in PM)." + + dbpath = os.path.join(bot.persist_dir, dbname) + conn = dbconnect(dbpath) + + cursor = conn.cursor() + command = "SELECT id, user_from, quote, date FROM tell " \ + "WHERE name LIKE ? and chan = ?" + tells = cursor.execute(command, (input.nick, input.chan)).fetchall() + + if(len(tells) > 0): + for tell in tells: + reltime = timesince.timesince(datetime.fromtimestamp(tell[3])) + input.msg(input.nick, '%(teller)s said %(time)s ago: %(quote)s' % + {'teller': tell[1], 'quote': tell[2], 'time': reltime}) + + command = "delete from tell where id = ?" + cursor.execute(command, (tell[0], )) + + conn.commit() + else: + input.pm("You have no pending tells.") + + conn.close() + + +@hook.command +def tell(bot, input): + ".tell -- relay to when is around" + + if len(input.msg) < 6: + return tell.__doc__ + + query = input.msg[6:].strip().partition(" ") + + if query[0] == input.nick: + return "No." + + + if query[2] != "": + dbpath = os.path.join(bot.persist_dir, dbname) + conn = dbconnect(dbpath) + + command = "select count(*) from tell_probation where name=? and chan=?" + if conn.execute(command, (input.nick, input.chan)).fetchone()[0] > 0: + return "No." + + command = "select count(*) from tell where name=? and user_from=?" + if conn.execute(command, (query[0], input.nick)).fetchone()[0] >= 3: + return "You've told that person too many things." + + cursor = conn.cursor() + command = "insert into tell(name, user_from, quote, chan, date) " \ + "values(?,?,?,?,?)" + cursor.execute(command, (query[0], input.nick, query[2], input.chan, + datetime.now())) + + conn.commit() + conn.close() + return "I'll pass that along." + + else: + return tell.__doc__ + + +def dbconnect(db): + "check to see that our db has the tell table and return a connection." + conn = sqlite3.connect(db) + + conn.execute("CREATE TABLE IF NOT EXISTS tell(id integer primary key " + "autoincrement, name text not null, user_from text not null," + "quote text not null, chan text not null, " + "date datetime not null);") + + conn.execute("CREATE TABLE IF NOT EXISTS " + "tell_probation(name text, chan text," + "primary key(name, chan));") + + conn.commit() + + return conn diff --git a/plugins_available/tinyurl.py b/plugins_available/tinyurl.py new file mode 100644 index 0000000..4dd1277 --- /dev/null +++ b/plugins_available/tinyurl.py @@ -0,0 +1,18 @@ +import re +import urllib2 + +from util import hook + + +tinyurl_re = re.compile(r'http://(?:www\.)?tinyurl.com/([A-Za-z0-9\-]+)', + flags=re.IGNORECASE) + + +@hook.command(hook=r'(.*)', prefix=False) +def tinyurl(inp): + tumatch = tinyurl_re.search(inp) + if tumatch: + try: + return urllib2.urlopen(tumatch.group()).url.strip() + except urllib2.URLError: + pass diff --git a/plugins_available/twitter.py b/plugins_available/twitter.py new file mode 100644 index 0000000..1e605c7 --- /dev/null +++ b/plugins_available/twitter.py @@ -0,0 +1,137 @@ +""" +twitter.py: written by Scaevolus 2009 +retrieves most recent tweets +""" + +import re +import random +import urllib2 +from lxml import etree +from time import strptime, strftime + +from util import hook + + +def unescape_xml(string): + # unescape the 5 chars that might be escaped in xml + + # gratuitously functional + # return reduce(lambda x, y: x.replace(*y), (string, + # zip('> < ' "e; &'.split(), '> < \' " &'.split())) + + # boring, normal + return string.replace('>', '>').replace('<', '<').replace(''', + "'").replace('"e;', '"').replace('&', '&') + +history = [] +history_max_size = 250 + +@hook.command +def twitter(inp): + ".twitter / //#/@ -- gets last/th tweet from"\ + "/gets tweet /gets random tweet with #/gets replied tweet from @" + + inp = inp.strip() + if not inp: + return twitter.__doc__ + + def add_reply(reply_name, reply_id): + if len(history) == history_max_size: + history.pop() + history.insert(0, (reply_name, reply_id)) + + def find_reply(reply_name): + for name, id in history: + if name == reply_name: + return id + + if inp[0] == '@': + reply_id = find_reply(inp[1:]) + if reply_id == None: + return 'error: no replies to %s found' % inp + inp = reply_id + + url = 'http://twitter.com' + getting_nth = False + getting_id = False + searching_hashtag = False + + time = 'status/created_at' + text = 'status/text' + reply_name = 'status/in_reply_to_screen_name' + reply_id = 'status/in_reply_to_status_id' + + if re.match(r'^\d+$', inp): + getting_id = True + url += '/statuses/show/%s.xml' % inp + screen_name = 'user/screen_name' + time = 'created_at' + text = 'text' + reply_name = 'in_reply_to_screen_name' + reply_id = 'in_reply_to_status_id' + elif re.match(r'^\w{1,15}$', inp): + url += '/users/show/%s.xml' % inp + screen_name = 'screen_name' + elif re.match(r'^\w{1,15}\s+\d+$', inp): + getting_nth = True + name, num = inp.split() + if int(num) > 3200: + return 'error: only supports up to the 3200th tweet' + url += '/statuses/user_timeline/%s.xml?count=1&page=%s' % (name, num) + screen_name = 'status/user/screen_name' + elif re.match(r'^#\w+$', inp): + url = 'http://search.twitter.com/search.atom?q=%23' + inp[1:] + searching_hashtag = True + else: + return 'error: invalid request' + + try: + xml = urllib2.urlopen(url).read() + except urllib2.HTTPError, e: + errors = {400 : 'bad request (ratelimited?)', + 401: 'tweet is private', + 404: 'invalid user/id', + 500: 'twitter is broken', + 502: 'twitter is down ("getting upgraded")', + 503: 'twitter is overloaded (lol, RoR)'} + if e.code == 404: + return 'error: invalid ' + ['username', 'tweet id'][getting_id] + if e.code in errors: + return 'error: ' + errors[e.code] + return 'error: unknown' + except urllib2.URLerror, e: + return 'error: timeout' + + tweet = etree.fromstring(xml) + + if searching_hashtag: + ns = '{http://www.w3.org/2005/Atom}' + tweets = tweet.findall(ns + 'entry/' + ns + 'id') + if not tweets: + return 'error: hashtag not found' + id = random.choice(tweets).text + id = id[id.rfind(':') + 1:] + print id + return twitter(id) + + if getting_nth: + if tweet.find('status') is None: + return 'error: user does not have that many tweets' + + time = tweet.find(time) + if time is None: + return 'error: user has no tweets' + + reply_name = tweet.find(reply_name).text + reply_id = tweet.find(reply_id).text + if reply_name is not None and reply_id is not None: + add_reply(reply_name, reply_id) + + time = strftime('%Y-%m-%d %H:%M:%S', + strptime(time.text, + '%a %b %d %H:%M:%S +0000 %Y')) + text = unescape_xml(tweet.find(text).text.replace('\n', '')) + screen_name = tweet.find(screen_name).text + + return "%s %s: %s" % (time, screen_name, text) + diff --git a/plugins_available/urbandictionary.py b/plugins_available/urbandictionary.py new file mode 100644 index 0000000..2327df6 --- /dev/null +++ b/plugins_available/urbandictionary.py @@ -0,0 +1,29 @@ +from lxml import html +import urllib + +from util import hook + + +@hook.command('u') +@hook.command +def urban(inp): + '''.u/.urban -- looks up on urbandictionary.com''' + if not inp.strip(): + return urban.__doc__ + + url = 'http://www.urbandictionary.com/define.php?term=' + \ + urllib.quote(inp.strip(), safe='') + page = html.parse(url) + words = page.xpath("//td[@class='word']") + defs = page.xpath("//div[@class='definition']") + + if not defs: + return 'no definitions found' + + out = words[0].text_content().strip() + ': ' + ' '.join( + defs[0].text_content().split()) + + if len(out) > 400: + out = out[:out.rfind(' ', 0, 400)] + '...' + + return out diff --git a/plugins_available/util/__init__.py b/plugins_available/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins_available/util/hook.py b/plugins_available/util/hook.py new file mode 100644 index 0000000..f6e257a --- /dev/null +++ b/plugins_available/util/hook.py @@ -0,0 +1,94 @@ +import Queue +import thread +import traceback + +def _isfunc(x): + if type(x) == type(_isfunc): + return True + return False + + +def _hook_add(func, add): + if not hasattr(func, '_skybot_hook'): + func._skybot_hook = [] + func._skybot_hook.append(add) + + +def _make_sig(f): + return f.func_code.co_filename, f.func_name, f.func_code.co_firstlineno + + +def sieve(func): + if func.func_code.co_argcount != 4: + raise ValueError( + 'sieves must take 4 arguments: (bot, input, func, args)') + _hook_add(func, ['sieve', (_make_sig(func), func)]) + return func + + +def command(func=None, hook=None, **kwargs): + args = {} + + def command_wrapper(func): + if func.func_code.co_argcount not in (1, 2): + raise ValueError( + 'commands must take 1 or 2 arguments: (inp) or (bot, input)') + args.setdefault('name', func.func_name) + args.setdefault('hook', args['name'] + r'(?:\s+|$)(.*)') + _hook_add(func, ['command', (_make_sig(func), func, args)]) + return func + + if hook is not None or kwargs or not _isfunc(func): + if func is not None: + args['name'] = func + if hook is not None: + args['hook'] = hook + args.update(kwargs) + return command_wrapper + else: + return command_wrapper(func) + + +def event(arg=None, **kwargs): + args = kwargs + + def event_wrapper(func): + if func.func_code.co_argcount != 2: + raise ValueError('events must take 2 arguments: (bot, input)') + args['name'] = func.func_name + args['prefix'] = False + args.setdefault('events', '*') + _hook_add(func, ['event', (_make_sig(func), func, args)]) + return func + + if _isfunc(arg): + return event_wrapper(arg, kwargs) + else: + if arg is not None: + args['events'] = arg.split() + return event_wrapper + + +def tee(func, **kwargs): + "passes _all_ input lines to function, in order (skips sieves)" + + if func.func_code.co_argcount != 2: + raise ValueError('tees must take 2 arguments: (bot, input)') + + _hook_add(func, ['tee', (_make_sig(func), func, kwargs)]) + func._iqueue = Queue.Queue() + + def trampoline(func): + input = None + while True: + input = func._iqueue.get() + if input == StopIteration: + return + try: + func(*input) + except Exception: + traceback.print_exc(Exception) + + thread.start_new_thread(trampoline, (func,)) + + return func diff --git a/plugins_available/util/timesince.py b/plugins_available/util/timesince.py new file mode 100644 index 0000000..03e8802 --- /dev/null +++ b/plugins_available/util/timesince.py @@ -0,0 +1,96 @@ +# Copyright (c) Django Software Foundation and individual contributors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of Django nor the names of its contributors may be used +# to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import datetime +import time + +def timesince(d, now=None): + """ + Takes two datetime objects and returns the time between d and now + as a nicely formatted string, e.g. "10 minutes". If d occurs after now, + then "0 minutes" is returned. + + Units used are years, months, weeks, days, hours, and minutes. + Seconds and microseconds are ignored. Up to two adjacent units will be + displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are + possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not. + + Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since + """ + chunks = ( + (60 * 60 * 24 * 365, ('year', 'years')), + (60 * 60 * 24 * 30, ('month', 'months')), + (60 * 60 * 24 * 7, ('week', 'weeks')), + (60 * 60 * 24, ('day', 'days')), + (60 * 60, ('hour', 'hours')), + (60, ('minute', 'minutes')) + ) + # Convert datetime.date to datetime.datetime for comparison. + if not isinstance(d, datetime.datetime): + d = datetime.datetime(d.year, d.month, d.day) + if now and not isinstance(now, datetime.datetime): + now = datetime.datetime(now.year, now.month, now.day) + + if not now: + now = datetime.datetime.now() + + # ignore microsecond part of 'd' since we removed it from 'now' + delta = now - (d - datetime.timedelta(0, 0, d.microsecond)) + since = delta.days * 24 * 60 * 60 + delta.seconds + if since <= 0: + # d is in the future compared to now, stop processing. + return u'0 ' + 'minutes' + for i, (seconds, name) in enumerate(chunks): + count = since // seconds + if count != 0: + break + + if count == 1: + s = '%(number)d %(type)s' % {'number': count, 'type': name[0]} + else: + s = '%(number)d %(type)s' % {'number': count, 'type': name[1]} + + if i + 1 < len(chunks): + # Now get the second item + seconds2, name2 = chunks[i + 1] + count2 = (since - (seconds * count)) // seconds2 + if count2 != 0: + if count2 == 1: + s += ', %(number)d %(type)s' % {'number': count2, 'type': name2[0]} + else: + s += ', %(number)d %(type)s' % {'number': count2, 'type': name2[1]} + return s + +def timeuntil(d, now=None): + """ + Like timesince, but returns a string measuring the time until + the given time. + """ + if not now: + now = datetime.datetime.now() + return timesince(now, d) diff --git a/plugins_available/util/yaml/LICENSE b/plugins_available/util/yaml/LICENSE new file mode 100644 index 0000000..050ced2 --- /dev/null +++ b/plugins_available/util/yaml/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins_available/util/yaml/PKG-INFO b/plugins_available/util/yaml/PKG-INFO new file mode 100644 index 0000000..eb63484 --- /dev/null +++ b/plugins_available/util/yaml/PKG-INFO @@ -0,0 +1,36 @@ +Metadata-Version: 1.0 +Name: PyYAML +Version: 3.09 +Summary: YAML parser and emitter for Python +Home-page: http://pyyaml.org/wiki/PyYAML +Author: Kirill Simonov +Author-email: xi@resolvent.net +License: MIT +Download-URL: http://pyyaml.org/download/pyyaml/PyYAML-3.09.tar.gz +Description: YAML is a data serialization format designed for human readability + and interaction with scripting languages. PyYAML is a YAML parser + and emitter for Python. + + PyYAML features a complete YAML 1.1 parser, Unicode support, pickle + support, capable extension API, and sensible error messages. PyYAML + supports standard YAML tags and provides Python-specific tags that + allow to represent an arbitrary Python object. + + PyYAML is applicable for a broad range of tasks from complex + configuration files to object serialization and persistance. +Platform: Any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.3 +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.0 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup diff --git a/plugins_available/util/yaml/README b/plugins_available/util/yaml/README new file mode 100644 index 0000000..c1edf13 --- /dev/null +++ b/plugins_available/util/yaml/README @@ -0,0 +1,35 @@ +PyYAML - The next generation YAML parser and emitter for Python. + +To install, type 'python setup.py install'. + +By default, the setup.py script checks whether LibYAML is installed +and if so, builds and installs LibYAML bindings. To skip the check +and force installation of LibYAML bindings, use the option '--with-libyaml': +'python setup.py --with-libyaml install'. To disable the check and +skip building and installing LibYAML bindings, use '--without-libyaml': +'python setup.py --without-libyaml install'. + +When LibYAML bindings are installed, you may use fast LibYAML-based +parser and emitter as follows: + + >>> yaml.load(stream, Loader=yaml.CLoader) + >>> yaml.dump(data, Dumper=yaml.CDumper) + +PyYAML includes a comprehensive test suite. To run the tests, +type 'python setup.py test'. + +For more information, check the PyYAML homepage: +'http://pyyaml.org/wiki/PyYAML'. + +For PyYAML tutorial and reference, see: +'http://pyyaml.org/wiki/PyYAMLDocumentation'. + +Post your questions and opinions to the YAML-Core mailing list: +'http://lists.sourceforge.net/lists/listinfo/yaml-core'. + +Submit bug reports and feature requests to the PyYAML bug tracker: +'http://pyyaml.org/newticket?component=pyyaml'. + +PyYAML is written by Kirill Simonov . It is released +under the MIT license. See the file LICENSE for more details. + diff --git a/plugins_available/util/yaml/__init__.py b/plugins_available/util/yaml/__init__.py new file mode 100644 index 0000000..c0fd1f3 --- /dev/null +++ b/plugins_available/util/yaml/__init__.py @@ -0,0 +1,288 @@ + +from error import * + +from tokens import * +from events import * +from nodes import * + +from loader import * +from dumper import * + +__version__ = '3.09' + +try: + from cyaml import * + __with_libyaml__ = True +except ImportError: + __with_libyaml__ = False + +def scan(stream, Loader=Loader): + """ + Scan a YAML stream and produce scanning tokens. + """ + loader = Loader(stream) + while loader.check_token(): + yield loader.get_token() + +def parse(stream, Loader=Loader): + """ + Parse a YAML stream and produce parsing events. + """ + loader = Loader(stream) + while loader.check_event(): + yield loader.get_event() + +def compose(stream, Loader=Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding representation tree. + """ + loader = Loader(stream) + return loader.get_single_node() + +def compose_all(stream, Loader=Loader): + """ + Parse all YAML documents in a stream + and produce corresponding representation trees. + """ + loader = Loader(stream) + while loader.check_node(): + yield loader.get_node() + +def load(stream, Loader=Loader): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + """ + loader = Loader(stream) + return loader.get_single_data() + +def load_all(stream, Loader=Loader): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + """ + loader = Loader(stream) + while loader.check_data(): + yield loader.get_data() + +def safe_load(stream): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + Resolve only basic YAML tags. + """ + return load(stream, SafeLoader) + +def safe_load_all(stream): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + Resolve only basic YAML tags. + """ + return load_all(stream, SafeLoader) + +def emit(events, stream=None, Dumper=Dumper, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None): + """ + Emit YAML parsing events into a stream. + If stream is None, return the produced string instead. + """ + getvalue = None + if stream is None: + from StringIO import StringIO + stream = StringIO() + getvalue = stream.getvalue + dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + for event in events: + dumper.emit(event) + if getvalue: + return getvalue() + +def serialize_all(nodes, stream=None, Dumper=Dumper, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding='utf-8', explicit_start=None, explicit_end=None, + version=None, tags=None): + """ + Serialize a sequence of representation trees into a YAML stream. + If stream is None, return the produced string instead. + """ + getvalue = None + if stream is None: + if encoding is None: + from StringIO import StringIO + else: + from cStringIO import StringIO + stream = StringIO() + getvalue = stream.getvalue + dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + encoding=encoding, version=version, tags=tags, + explicit_start=explicit_start, explicit_end=explicit_end) + dumper.open() + for node in nodes: + dumper.serialize(node) + dumper.close() + if getvalue: + return getvalue() + +def serialize(node, stream=None, Dumper=Dumper, **kwds): + """ + Serialize a representation tree into a YAML stream. + If stream is None, return the produced string instead. + """ + return serialize_all([node], stream, Dumper=Dumper, **kwds) + +def dump_all(documents, stream=None, Dumper=Dumper, + default_style=None, default_flow_style=None, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding='utf-8', explicit_start=None, explicit_end=None, + version=None, tags=None): + """ + Serialize a sequence of Python objects into a YAML stream. + If stream is None, return the produced string instead. + """ + getvalue = None + if stream is None: + if encoding is None: + from StringIO import StringIO + else: + from cStringIO import StringIO + stream = StringIO() + getvalue = stream.getvalue + dumper = Dumper(stream, default_style=default_style, + default_flow_style=default_flow_style, + canonical=canonical, indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break, + encoding=encoding, version=version, tags=tags, + explicit_start=explicit_start, explicit_end=explicit_end) + dumper.open() + for data in documents: + dumper.represent(data) + dumper.close() + if getvalue: + return getvalue() + +def dump(data, stream=None, Dumper=Dumper, **kwds): + """ + Serialize a Python object into a YAML stream. + If stream is None, return the produced string instead. + """ + return dump_all([data], stream, Dumper=Dumper, **kwds) + +def safe_dump_all(documents, stream=None, **kwds): + """ + Serialize a sequence of Python objects into a YAML stream. + Produce only basic YAML tags. + If stream is None, return the produced string instead. + """ + return dump_all(documents, stream, Dumper=SafeDumper, **kwds) + +def safe_dump(data, stream=None, **kwds): + """ + Serialize a Python object into a YAML stream. + Produce only basic YAML tags. + If stream is None, return the produced string instead. + """ + return dump_all([data], stream, Dumper=SafeDumper, **kwds) + +def add_implicit_resolver(tag, regexp, first=None, + Loader=Loader, Dumper=Dumper): + """ + Add an implicit scalar detector. + If an implicit scalar value matches the given regexp, + the corresponding tag is assigned to the scalar. + first is a sequence of possible initial characters or None. + """ + Loader.add_implicit_resolver(tag, regexp, first) + Dumper.add_implicit_resolver(tag, regexp, first) + +def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper): + """ + Add a path based resolver for the given tag. + A path is a list of keys that forms a path + to a node in the representation tree. + Keys can be string values, integers, or None. + """ + Loader.add_path_resolver(tag, path, kind) + Dumper.add_path_resolver(tag, path, kind) + +def add_constructor(tag, constructor, Loader=Loader): + """ + Add a constructor for the given tag. + Constructor is a function that accepts a Loader instance + and a node object and produces the corresponding Python object. + """ + Loader.add_constructor(tag, constructor) + +def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader): + """ + Add a multi-constructor for the given tag prefix. + Multi-constructor is called for a node if its tag starts with tag_prefix. + Multi-constructor accepts a Loader instance, a tag suffix, + and a node object and produces the corresponding Python object. + """ + Loader.add_multi_constructor(tag_prefix, multi_constructor) + +def add_representer(data_type, representer, Dumper=Dumper): + """ + Add a representer for the given type. + Representer is a function accepting a Dumper instance + and an instance of the given data type + and producing the corresponding representation node. + """ + Dumper.add_representer(data_type, representer) + +def add_multi_representer(data_type, multi_representer, Dumper=Dumper): + """ + Add a representer for the given type. + Multi-representer is a function accepting a Dumper instance + and an instance of the given data type or subtype + and producing the corresponding representation node. + """ + Dumper.add_multi_representer(data_type, multi_representer) + +class YAMLObjectMetaclass(type): + """ + The metaclass for YAMLObject. + """ + def __init__(cls, name, bases, kwds): + super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) + if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: + cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) + cls.yaml_dumper.add_representer(cls, cls.to_yaml) + +class YAMLObject(object): + """ + An object that can dump itself to a YAML stream + and load itself from a YAML stream. + """ + + __metaclass__ = YAMLObjectMetaclass + __slots__ = () # no direct instantiation, so allow immutable subclasses + + yaml_loader = Loader + yaml_dumper = Dumper + + yaml_tag = None + yaml_flow_style = None + + def from_yaml(cls, loader, node): + """ + Convert a representation node to a Python object. + """ + return loader.construct_yaml_object(node, cls) + from_yaml = classmethod(from_yaml) + + def to_yaml(cls, dumper, data): + """ + Convert a Python object to a representation node. + """ + return dumper.represent_yaml_object(cls.yaml_tag, data, cls, + flow_style=cls.yaml_flow_style) + to_yaml = classmethod(to_yaml) + diff --git a/plugins_available/util/yaml/composer.py b/plugins_available/util/yaml/composer.py new file mode 100644 index 0000000..06e5ac7 --- /dev/null +++ b/plugins_available/util/yaml/composer.py @@ -0,0 +1,139 @@ + +__all__ = ['Composer', 'ComposerError'] + +from error import MarkedYAMLError +from events import * +from nodes import * + +class ComposerError(MarkedYAMLError): + pass + +class Composer(object): + + def __init__(self): + self.anchors = {} + + def check_node(self): + # Drop the STREAM-START event. + if self.check_event(StreamStartEvent): + self.get_event() + + # If there are more documents available? + return not self.check_event(StreamEndEvent) + + def get_node(self): + # Get the root node of the next document. + if not self.check_event(StreamEndEvent): + return self.compose_document() + + def get_single_node(self): + # Drop the STREAM-START event. + self.get_event() + + # Compose a document if the stream is not empty. + document = None + if not self.check_event(StreamEndEvent): + document = self.compose_document() + + # Ensure that the stream contains no more documents. + if not self.check_event(StreamEndEvent): + event = self.get_event() + raise ComposerError("expected a single document in the stream", + document.start_mark, "but found another document", + event.start_mark) + + # Drop the STREAM-END event. + self.get_event() + + return document + + def compose_document(self): + # Drop the DOCUMENT-START event. + self.get_event() + + # Compose the root node. + node = self.compose_node(None, None) + + # Drop the DOCUMENT-END event. + self.get_event() + + self.anchors = {} + return node + + def compose_node(self, parent, index): + if self.check_event(AliasEvent): + event = self.get_event() + anchor = event.anchor + if anchor not in self.anchors: + raise ComposerError(None, None, "found undefined alias %r" + % anchor.encode('utf-8'), event.start_mark) + return self.anchors[anchor] + event = self.peek_event() + anchor = event.anchor + if anchor is not None: + if anchor in self.anchors: + raise ComposerError("found duplicate anchor %r; first occurence" + % anchor.encode('utf-8'), self.anchors[anchor].start_mark, + "second occurence", event.start_mark) + self.descend_resolver(parent, index) + if self.check_event(ScalarEvent): + node = self.compose_scalar_node(anchor) + elif self.check_event(SequenceStartEvent): + node = self.compose_sequence_node(anchor) + elif self.check_event(MappingStartEvent): + node = self.compose_mapping_node(anchor) + self.ascend_resolver() + return node + + def compose_scalar_node(self, anchor): + event = self.get_event() + tag = event.tag + if tag is None or tag == u'!': + tag = self.resolve(ScalarNode, event.value, event.implicit) + node = ScalarNode(tag, event.value, + event.start_mark, event.end_mark, style=event.style) + if anchor is not None: + self.anchors[anchor] = node + return node + + def compose_sequence_node(self, anchor): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == u'!': + tag = self.resolve(SequenceNode, None, start_event.implicit) + node = SequenceNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + if anchor is not None: + self.anchors[anchor] = node + index = 0 + while not self.check_event(SequenceEndEvent): + node.value.append(self.compose_node(node, index)) + index += 1 + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node + + def compose_mapping_node(self, anchor): + start_event = self.get_event() + tag = start_event.tag + if tag is None or tag == u'!': + tag = self.resolve(MappingNode, None, start_event.implicit) + node = MappingNode(tag, [], + start_event.start_mark, None, + flow_style=start_event.flow_style) + if anchor is not None: + self.anchors[anchor] = node + while not self.check_event(MappingEndEvent): + #key_event = self.peek_event() + item_key = self.compose_node(node, None) + #if item_key in node.value: + # raise ComposerError("while composing a mapping", start_event.start_mark, + # "found duplicate key", key_event.start_mark) + item_value = self.compose_node(node, item_key) + #node.value[item_key] = item_value + node.value.append((item_key, item_value)) + end_event = self.get_event() + node.end_mark = end_event.end_mark + return node + diff --git a/plugins_available/util/yaml/constructor.py b/plugins_available/util/yaml/constructor.py new file mode 100644 index 0000000..420c434 --- /dev/null +++ b/plugins_available/util/yaml/constructor.py @@ -0,0 +1,684 @@ + +__all__ = ['BaseConstructor', 'SafeConstructor', 'Constructor', + 'ConstructorError'] + +from error import * +from nodes import * + +import datetime + +try: + set +except NameError: + from sets import Set as set + +import binascii, re, sys, types + +class ConstructorError(MarkedYAMLError): + pass + +class BaseConstructor(object): + + yaml_constructors = {} + yaml_multi_constructors = {} + + def __init__(self): + self.constructed_objects = {} + self.recursive_objects = {} + self.state_generators = [] + self.deep_construct = False + + def check_data(self): + # If there are more documents available? + return self.check_node() + + def get_data(self): + # Construct and return the next document. + if self.check_node(): + return self.construct_document(self.get_node()) + + def get_single_data(self): + # Ensure that the stream contains a single document and construct it. + node = self.get_single_node() + if node is not None: + return self.construct_document(node) + return None + + def construct_document(self, node): + data = self.construct_object(node) + while self.state_generators: + state_generators = self.state_generators + self.state_generators = [] + for generator in state_generators: + for dummy in generator: + pass + self.constructed_objects = {} + self.recursive_objects = {} + self.deep_construct = False + return data + + def construct_object(self, node, deep=False): + if deep: + old_deep = self.deep_construct + self.deep_construct = True + if node in self.constructed_objects: + return self.constructed_objects[node] + if node in self.recursive_objects: + raise ConstructorError(None, None, + "found unconstructable recursive node", node.start_mark) + self.recursive_objects[node] = None + constructor = None + tag_suffix = None + if node.tag in self.yaml_constructors: + constructor = self.yaml_constructors[node.tag] + else: + for tag_prefix in self.yaml_multi_constructors: + if node.tag.startswith(tag_prefix): + tag_suffix = node.tag[len(tag_prefix):] + constructor = self.yaml_multi_constructors[tag_prefix] + break + else: + if None in self.yaml_multi_constructors: + tag_suffix = node.tag + constructor = self.yaml_multi_constructors[None] + elif None in self.yaml_constructors: + constructor = self.yaml_constructors[None] + elif isinstance(node, ScalarNode): + constructor = self.__class__.construct_scalar + elif isinstance(node, SequenceNode): + constructor = self.__class__.construct_sequence + elif isinstance(node, MappingNode): + constructor = self.__class__.construct_mapping + if tag_suffix is None: + data = constructor(self, node) + else: + data = constructor(self, tag_suffix, node) + if isinstance(data, types.GeneratorType): + generator = data + data = generator.next() + if self.deep_construct: + for dummy in generator: + pass + else: + self.state_generators.append(generator) + self.constructed_objects[node] = data + del self.recursive_objects[node] + if deep: + self.deep_construct = old_deep + return data + + def construct_scalar(self, node): + if not isinstance(node, ScalarNode): + raise ConstructorError(None, None, + "expected a scalar node, but found %s" % node.id, + node.start_mark) + return node.value + + def construct_sequence(self, node, deep=False): + if not isinstance(node, SequenceNode): + raise ConstructorError(None, None, + "expected a sequence node, but found %s" % node.id, + node.start_mark) + return [self.construct_object(child, deep=deep) + for child in node.value] + + def construct_mapping(self, node, deep=False): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark) + mapping = {} + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + try: + hash(key) + except TypeError, exc: + raise ConstructorError("while constructing a mapping", node.start_mark, + "found unacceptable key (%s)" % exc, key_node.start_mark) + value = self.construct_object(value_node, deep=deep) + mapping[key] = value + return mapping + + def construct_pairs(self, node, deep=False): + if not isinstance(node, MappingNode): + raise ConstructorError(None, None, + "expected a mapping node, but found %s" % node.id, + node.start_mark) + pairs = [] + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + value = self.construct_object(value_node, deep=deep) + pairs.append((key, value)) + return pairs + + def add_constructor(cls, tag, constructor): + if not 'yaml_constructors' in cls.__dict__: + cls.yaml_constructors = cls.yaml_constructors.copy() + cls.yaml_constructors[tag] = constructor + add_constructor = classmethod(add_constructor) + + def add_multi_constructor(cls, tag_prefix, multi_constructor): + if not 'yaml_multi_constructors' in cls.__dict__: + cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() + cls.yaml_multi_constructors[tag_prefix] = multi_constructor + add_multi_constructor = classmethod(add_multi_constructor) + +class SafeConstructor(BaseConstructor): + + def construct_scalar(self, node): + if isinstance(node, MappingNode): + for key_node, value_node in node.value: + if key_node.tag == u'tag:yaml.org,2002:value': + return self.construct_scalar(value_node) + return BaseConstructor.construct_scalar(self, node) + + def flatten_mapping(self, node): + merge = [] + index = 0 + while index < len(node.value): + key_node, value_node = node.value[index] + if key_node.tag == u'tag:yaml.org,2002:merge': + del node.value[index] + if isinstance(value_node, MappingNode): + self.flatten_mapping(value_node) + merge.extend(value_node.value) + elif isinstance(value_node, SequenceNode): + submerge = [] + for subnode in value_node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found %s" + % subnode.id, subnode.start_mark) + self.flatten_mapping(subnode) + submerge.append(subnode.value) + submerge.reverse() + for value in submerge: + merge.extend(value) + else: + raise ConstructorError("while constructing a mapping", node.start_mark, + "expected a mapping or list of mappings for merging, but found %s" + % value_node.id, value_node.start_mark) + elif key_node.tag == u'tag:yaml.org,2002:value': + key_node.tag = u'tag:yaml.org,2002:str' + index += 1 + else: + index += 1 + if merge: + node.value = merge + node.value + + def construct_mapping(self, node, deep=False): + if isinstance(node, MappingNode): + self.flatten_mapping(node) + return BaseConstructor.construct_mapping(self, node, deep=deep) + + def construct_yaml_null(self, node): + self.construct_scalar(node) + return None + + bool_values = { + u'yes': True, + u'no': False, + u'true': True, + u'false': False, + u'on': True, + u'off': False, + } + + def construct_yaml_bool(self, node): + value = self.construct_scalar(node) + return self.bool_values[value.lower()] + + def construct_yaml_int(self, node): + value = str(self.construct_scalar(node)) + value = value.replace('_', '') + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '0': + return 0 + elif value.startswith('0b'): + return sign*int(value[2:], 2) + elif value.startswith('0x'): + return sign*int(value[2:], 16) + elif value[0] == '0': + return sign*int(value, 8) + elif ':' in value: + digits = [int(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return sign*int(value) + + inf_value = 1e300 + while inf_value != inf_value*inf_value: + inf_value *= inf_value + nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99). + + def construct_yaml_float(self, node): + value = str(self.construct_scalar(node)) + value = value.replace('_', '').lower() + sign = +1 + if value[0] == '-': + sign = -1 + if value[0] in '+-': + value = value[1:] + if value == '.inf': + return sign*self.inf_value + elif value == '.nan': + return self.nan_value + elif ':' in value: + digits = [float(part) for part in value.split(':')] + digits.reverse() + base = 1 + value = 0.0 + for digit in digits: + value += digit*base + base *= 60 + return sign*value + else: + return sign*float(value) + + def construct_yaml_binary(self, node): + value = self.construct_scalar(node) + try: + return str(value).decode('base64') + except (binascii.Error, UnicodeEncodeError), exc: + raise ConstructorError(None, None, + "failed to decode base64 data: %s" % exc, node.start_mark) + + timestamp_regexp = re.compile( + ur'''^(?P[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[0-9][0-9]))?))?)?$''', re.X) + + def construct_yaml_timestamp(self, node): + value = self.construct_scalar(node) + match = self.timestamp_regexp.match(node.value) + values = match.groupdict() + year = int(values['year']) + month = int(values['month']) + day = int(values['day']) + if not values['hour']: + return datetime.date(year, month, day) + hour = int(values['hour']) + minute = int(values['minute']) + second = int(values['second']) + fraction = 0 + if values['fraction']: + fraction = values['fraction'][:6] + while len(fraction) < 6: + fraction += '0' + fraction = int(fraction) + delta = None + if values['tz_sign']: + tz_hour = int(values['tz_hour']) + tz_minute = int(values['tz_minute'] or 0) + delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) + if values['tz_sign'] == '-': + delta = -delta + data = datetime.datetime(year, month, day, hour, minute, second, fraction) + if delta: + data -= delta + return data + + def construct_yaml_omap(self, node): + # Note: we do not check for duplicate keys, because it's too + # CPU-expensive. + omap = [] + yield omap + if not isinstance(node, SequenceNode): + raise ConstructorError("while constructing an ordered map", node.start_mark, + "expected a sequence, but found %s" % node.id, node.start_mark) + for subnode in node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing an ordered map", node.start_mark, + "expected a mapping of length 1, but found %s" % subnode.id, + subnode.start_mark) + if len(subnode.value) != 1: + raise ConstructorError("while constructing an ordered map", node.start_mark, + "expected a single mapping item, but found %d items" % len(subnode.value), + subnode.start_mark) + key_node, value_node = subnode.value[0] + key = self.construct_object(key_node) + value = self.construct_object(value_node) + omap.append((key, value)) + + def construct_yaml_pairs(self, node): + # Note: the same code as `construct_yaml_omap`. + pairs = [] + yield pairs + if not isinstance(node, SequenceNode): + raise ConstructorError("while constructing pairs", node.start_mark, + "expected a sequence, but found %s" % node.id, node.start_mark) + for subnode in node.value: + if not isinstance(subnode, MappingNode): + raise ConstructorError("while constructing pairs", node.start_mark, + "expected a mapping of length 1, but found %s" % subnode.id, + subnode.start_mark) + if len(subnode.value) != 1: + raise ConstructorError("while constructing pairs", node.start_mark, + "expected a single mapping item, but found %d items" % len(subnode.value), + subnode.start_mark) + key_node, value_node = subnode.value[0] + key = self.construct_object(key_node) + value = self.construct_object(value_node) + pairs.append((key, value)) + + def construct_yaml_set(self, node): + data = set() + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_yaml_str(self, node): + value = self.construct_scalar(node) + try: + return value.encode('ascii') + except UnicodeEncodeError: + return value + + def construct_yaml_seq(self, node): + data = [] + yield data + data.extend(self.construct_sequence(node)) + + def construct_yaml_map(self, node): + data = {} + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_yaml_object(self, node, cls): + data = cls.__new__(cls) + yield data + if hasattr(data, '__setstate__'): + state = self.construct_mapping(node, deep=True) + data.__setstate__(state) + else: + state = self.construct_mapping(node) + data.__dict__.update(state) + + def construct_undefined(self, node): + raise ConstructorError(None, None, + "could not determine a constructor for the tag %r" % node.tag.encode('utf-8'), + node.start_mark) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:null', + SafeConstructor.construct_yaml_null) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:bool', + SafeConstructor.construct_yaml_bool) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:int', + SafeConstructor.construct_yaml_int) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:float', + SafeConstructor.construct_yaml_float) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:binary', + SafeConstructor.construct_yaml_binary) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:timestamp', + SafeConstructor.construct_yaml_timestamp) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:omap', + SafeConstructor.construct_yaml_omap) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:pairs', + SafeConstructor.construct_yaml_pairs) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:set', + SafeConstructor.construct_yaml_set) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:str', + SafeConstructor.construct_yaml_str) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:seq', + SafeConstructor.construct_yaml_seq) + +SafeConstructor.add_constructor( + u'tag:yaml.org,2002:map', + SafeConstructor.construct_yaml_map) + +SafeConstructor.add_constructor(None, + SafeConstructor.construct_undefined) + +class Constructor(SafeConstructor): + + def construct_python_str(self, node): + return self.construct_scalar(node).encode('utf-8') + + def construct_python_unicode(self, node): + return self.construct_scalar(node) + + def construct_python_long(self, node): + return long(self.construct_yaml_int(node)) + + def construct_python_complex(self, node): + return complex(self.construct_scalar(node)) + + def construct_python_tuple(self, node): + return tuple(self.construct_sequence(node)) + + def find_python_module(self, name, mark): + if not name: + raise ConstructorError("while constructing a Python module", mark, + "expected non-empty name appended to the tag", mark) + try: + __import__(name) + except ImportError, exc: + raise ConstructorError("while constructing a Python module", mark, + "cannot find module %r (%s)" % (name.encode('utf-8'), exc), mark) + return sys.modules[name] + + def find_python_name(self, name, mark): + if not name: + raise ConstructorError("while constructing a Python object", mark, + "expected non-empty name appended to the tag", mark) + if u'.' in name: + # Python 2.4 only + #module_name, object_name = name.rsplit('.', 1) + items = name.split('.') + object_name = items.pop() + module_name = '.'.join(items) + else: + module_name = '__builtin__' + object_name = name + try: + __import__(module_name) + except ImportError, exc: + raise ConstructorError("while constructing a Python object", mark, + "cannot find module %r (%s)" % (module_name.encode('utf-8'), exc), mark) + module = sys.modules[module_name] + if not hasattr(module, object_name): + raise ConstructorError("while constructing a Python object", mark, + "cannot find %r in the module %r" % (object_name.encode('utf-8'), + module.__name__), mark) + return getattr(module, object_name) + + def construct_python_name(self, suffix, node): + value = self.construct_scalar(node) + if value: + raise ConstructorError("while constructing a Python name", node.start_mark, + "expected the empty value, but found %r" % value.encode('utf-8'), + node.start_mark) + return self.find_python_name(suffix, node.start_mark) + + def construct_python_module(self, suffix, node): + value = self.construct_scalar(node) + if value: + raise ConstructorError("while constructing a Python module", node.start_mark, + "expected the empty value, but found %r" % value.encode('utf-8'), + node.start_mark) + return self.find_python_module(suffix, node.start_mark) + + class classobj: pass + + def make_python_instance(self, suffix, node, + args=None, kwds=None, newobj=False): + if not args: + args = [] + if not kwds: + kwds = {} + cls = self.find_python_name(suffix, node.start_mark) + if newobj and isinstance(cls, type(self.classobj)) \ + and not args and not kwds: + instance = self.classobj() + instance.__class__ = cls + return instance + elif newobj and isinstance(cls, type): + return cls.__new__(cls, *args, **kwds) + else: + return cls(*args, **kwds) + + def set_python_instance_state(self, instance, state): + if hasattr(instance, '__setstate__'): + instance.__setstate__(state) + else: + slotstate = {} + if isinstance(state, tuple) and len(state) == 2: + state, slotstate = state + if hasattr(instance, '__dict__'): + instance.__dict__.update(state) + elif state: + slotstate.update(state) + for key, value in slotstate.items(): + setattr(object, key, value) + + def construct_python_object(self, suffix, node): + # Format: + # !!python/object:module.name { ... state ... } + instance = self.make_python_instance(suffix, node, newobj=True) + yield instance + deep = hasattr(instance, '__setstate__') + state = self.construct_mapping(node, deep=deep) + self.set_python_instance_state(instance, state) + + def construct_python_object_apply(self, suffix, node, newobj=False): + # Format: + # !!python/object/apply # (or !!python/object/new) + # args: [ ... arguments ... ] + # kwds: { ... keywords ... } + # state: ... state ... + # listitems: [ ... listitems ... ] + # dictitems: { ... dictitems ... } + # or short format: + # !!python/object/apply [ ... arguments ... ] + # The difference between !!python/object/apply and !!python/object/new + # is how an object is created, check make_python_instance for details. + if isinstance(node, SequenceNode): + args = self.construct_sequence(node, deep=True) + kwds = {} + state = {} + listitems = [] + dictitems = {} + else: + value = self.construct_mapping(node, deep=True) + args = value.get('args', []) + kwds = value.get('kwds', {}) + state = value.get('state', {}) + listitems = value.get('listitems', []) + dictitems = value.get('dictitems', {}) + instance = self.make_python_instance(suffix, node, args, kwds, newobj) + if state: + self.set_python_instance_state(instance, state) + if listitems: + instance.extend(listitems) + if dictitems: + for key in dictitems: + instance[key] = dictitems[key] + return instance + + def construct_python_object_new(self, suffix, node): + return self.construct_python_object_apply(suffix, node, newobj=True) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/none', + Constructor.construct_yaml_null) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/bool', + Constructor.construct_yaml_bool) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/str', + Constructor.construct_python_str) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/unicode', + Constructor.construct_python_unicode) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/int', + Constructor.construct_yaml_int) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/long', + Constructor.construct_python_long) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/float', + Constructor.construct_yaml_float) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/complex', + Constructor.construct_python_complex) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/list', + Constructor.construct_yaml_seq) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/tuple', + Constructor.construct_python_tuple) + +Constructor.add_constructor( + u'tag:yaml.org,2002:python/dict', + Constructor.construct_yaml_map) + +Constructor.add_multi_constructor( + u'tag:yaml.org,2002:python/name:', + Constructor.construct_python_name) + +Constructor.add_multi_constructor( + u'tag:yaml.org,2002:python/module:', + Constructor.construct_python_module) + +Constructor.add_multi_constructor( + u'tag:yaml.org,2002:python/object:', + Constructor.construct_python_object) + +Constructor.add_multi_constructor( + u'tag:yaml.org,2002:python/object/apply:', + Constructor.construct_python_object_apply) + +Constructor.add_multi_constructor( + u'tag:yaml.org,2002:python/object/new:', + Constructor.construct_python_object_new) + diff --git a/plugins_available/util/yaml/cyaml.py b/plugins_available/util/yaml/cyaml.py new file mode 100644 index 0000000..68dcd75 --- /dev/null +++ b/plugins_available/util/yaml/cyaml.py @@ -0,0 +1,85 @@ + +__all__ = ['CBaseLoader', 'CSafeLoader', 'CLoader', + 'CBaseDumper', 'CSafeDumper', 'CDumper'] + +from _yaml import CParser, CEmitter + +from constructor import * + +from serializer import * +from representer import * + +from resolver import * + +class CBaseLoader(CParser, BaseConstructor, BaseResolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + BaseConstructor.__init__(self) + BaseResolver.__init__(self) + +class CSafeLoader(CParser, SafeConstructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + SafeConstructor.__init__(self) + Resolver.__init__(self) + +class CLoader(CParser, Constructor, Resolver): + + def __init__(self, stream): + CParser.__init__(self, stream) + Constructor.__init__(self) + Resolver.__init__(self) + +class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver): + + def __init__(self, stream, + default_style=None, default_flow_style=None, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None): + CEmitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style) + Resolver.__init__(self) + +class CSafeDumper(CEmitter, SafeRepresenter, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=None, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None): + CEmitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + SafeRepresenter.__init__(self, default_style=default_style, + default_flow_style=default_flow_style) + Resolver.__init__(self) + +class CDumper(CEmitter, Serializer, Representer, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=None, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None): + CEmitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, encoding=encoding, + allow_unicode=allow_unicode, line_break=line_break, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style) + Resolver.__init__(self) + diff --git a/plugins_available/util/yaml/dumper.py b/plugins_available/util/yaml/dumper.py new file mode 100644 index 0000000..f811d2c --- /dev/null +++ b/plugins_available/util/yaml/dumper.py @@ -0,0 +1,62 @@ + +__all__ = ['BaseDumper', 'SafeDumper', 'Dumper'] + +from emitter import * +from serializer import * +from representer import * +from resolver import * + +class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver): + + def __init__(self, stream, + default_style=None, default_flow_style=None, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None): + Emitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + Serializer.__init__(self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style) + Resolver.__init__(self) + +class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=None, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None): + Emitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + Serializer.__init__(self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + SafeRepresenter.__init__(self, default_style=default_style, + default_flow_style=default_flow_style) + Resolver.__init__(self) + +class Dumper(Emitter, Serializer, Representer, Resolver): + + def __init__(self, stream, + default_style=None, default_flow_style=None, + canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None, + encoding=None, explicit_start=None, explicit_end=None, + version=None, tags=None): + Emitter.__init__(self, stream, canonical=canonical, + indent=indent, width=width, + allow_unicode=allow_unicode, line_break=line_break) + Serializer.__init__(self, encoding=encoding, + explicit_start=explicit_start, explicit_end=explicit_end, + version=version, tags=tags) + Representer.__init__(self, default_style=default_style, + default_flow_style=default_flow_style) + Resolver.__init__(self) + diff --git a/plugins_available/util/yaml/emitter.py b/plugins_available/util/yaml/emitter.py new file mode 100644 index 0000000..4cb2c8a --- /dev/null +++ b/plugins_available/util/yaml/emitter.py @@ -0,0 +1,1135 @@ + +# Emitter expects events obeying the following grammar: +# stream ::= STREAM-START document* STREAM-END +# document ::= DOCUMENT-START node DOCUMENT-END +# node ::= SCALAR | sequence | mapping +# sequence ::= SEQUENCE-START node* SEQUENCE-END +# mapping ::= MAPPING-START (node node)* MAPPING-END + +__all__ = ['Emitter', 'EmitterError'] + +from error import YAMLError +from events import * + +class EmitterError(YAMLError): + pass + +class ScalarAnalysis(object): + def __init__(self, scalar, empty, multiline, + allow_flow_plain, allow_block_plain, + allow_single_quoted, allow_double_quoted, + allow_block): + self.scalar = scalar + self.empty = empty + self.multiline = multiline + self.allow_flow_plain = allow_flow_plain + self.allow_block_plain = allow_block_plain + self.allow_single_quoted = allow_single_quoted + self.allow_double_quoted = allow_double_quoted + self.allow_block = allow_block + +class Emitter(object): + + DEFAULT_TAG_PREFIXES = { + u'!' : u'!', + u'tag:yaml.org,2002:' : u'!!', + } + + def __init__(self, stream, canonical=None, indent=None, width=None, + allow_unicode=None, line_break=None): + + # The stream should have the methods `write` and possibly `flush`. + self.stream = stream + + # Encoding can be overriden by STREAM-START. + self.encoding = None + + # Emitter is a state machine with a stack of states to handle nested + # structures. + self.states = [] + self.state = self.expect_stream_start + + # Current event and the event queue. + self.events = [] + self.event = None + + # The current indentation level and the stack of previous indents. + self.indents = [] + self.indent = None + + # Flow level. + self.flow_level = 0 + + # Contexts. + self.root_context = False + self.sequence_context = False + self.mapping_context = False + self.simple_key_context = False + + # Characteristics of the last emitted character: + # - current position. + # - is it a whitespace? + # - is it an indention character + # (indentation space, '-', '?', or ':')? + self.line = 0 + self.column = 0 + self.whitespace = True + self.indention = True + + # Whether the document requires an explicit document indicator + self.open_ended = False + + # Formatting details. + self.canonical = canonical + self.allow_unicode = allow_unicode + self.best_indent = 2 + if indent and 1 < indent < 10: + self.best_indent = indent + self.best_width = 80 + if width and width > self.best_indent*2: + self.best_width = width + self.best_line_break = u'\n' + if line_break in [u'\r', u'\n', u'\r\n']: + self.best_line_break = line_break + + # Tag prefixes. + self.tag_prefixes = None + + # Prepared anchor and tag. + self.prepared_anchor = None + self.prepared_tag = None + + # Scalar analysis and style. + self.analysis = None + self.style = None + + def emit(self, event): + self.events.append(event) + while not self.need_more_events(): + self.event = self.events.pop(0) + self.state() + self.event = None + + # In some cases, we wait for a few next events before emitting. + + def need_more_events(self): + if not self.events: + return True + event = self.events[0] + if isinstance(event, DocumentStartEvent): + return self.need_events(1) + elif isinstance(event, SequenceStartEvent): + return self.need_events(2) + elif isinstance(event, MappingStartEvent): + return self.need_events(3) + else: + return False + + def need_events(self, count): + level = 0 + for event in self.events[1:]: + if isinstance(event, (DocumentStartEvent, CollectionStartEvent)): + level += 1 + elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)): + level -= 1 + elif isinstance(event, StreamEndEvent): + level = -1 + if level < 0: + return False + return (len(self.events) < count+1) + + def increase_indent(self, flow=False, indentless=False): + self.indents.append(self.indent) + if self.indent is None: + if flow: + self.indent = self.best_indent + else: + self.indent = 0 + elif not indentless: + self.indent += self.best_indent + + # States. + + # Stream handlers. + + def expect_stream_start(self): + if isinstance(self.event, StreamStartEvent): + if self.event.encoding and not getattr(self.stream, 'encoding', None): + self.encoding = self.event.encoding + self.write_stream_start() + self.state = self.expect_first_document_start + else: + raise EmitterError("expected StreamStartEvent, but got %s" + % self.event) + + def expect_nothing(self): + raise EmitterError("expected nothing, but got %s" % self.event) + + # Document handlers. + + def expect_first_document_start(self): + return self.expect_document_start(first=True) + + def expect_document_start(self, first=False): + if isinstance(self.event, DocumentStartEvent): + if (self.event.version or self.event.tags) and self.open_ended: + self.write_indicator(u'...', True) + self.write_indent() + if self.event.version: + version_text = self.prepare_version(self.event.version) + self.write_version_directive(version_text) + self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() + if self.event.tags: + handles = self.event.tags.keys() + handles.sort() + for handle in handles: + prefix = self.event.tags[handle] + self.tag_prefixes[prefix] = handle + handle_text = self.prepare_tag_handle(handle) + prefix_text = self.prepare_tag_prefix(prefix) + self.write_tag_directive(handle_text, prefix_text) + implicit = (first and not self.event.explicit and not self.canonical + and not self.event.version and not self.event.tags + and not self.check_empty_document()) + if not implicit: + self.write_indent() + self.write_indicator(u'---', True) + if self.canonical: + self.write_indent() + self.state = self.expect_document_root + elif isinstance(self.event, StreamEndEvent): + if self.open_ended: + self.write_indicator(u'...', True) + self.write_indent() + self.write_stream_end() + self.state = self.expect_nothing + else: + raise EmitterError("expected DocumentStartEvent, but got %s" + % self.event) + + def expect_document_end(self): + if isinstance(self.event, DocumentEndEvent): + self.write_indent() + if self.event.explicit: + self.write_indicator(u'...', True) + self.write_indent() + self.flush_stream() + self.state = self.expect_document_start + else: + raise EmitterError("expected DocumentEndEvent, but got %s" + % self.event) + + def expect_document_root(self): + self.states.append(self.expect_document_end) + self.expect_node(root=True) + + # Node handlers. + + def expect_node(self, root=False, sequence=False, mapping=False, + simple_key=False): + self.root_context = root + self.sequence_context = sequence + self.mapping_context = mapping + self.simple_key_context = simple_key + if isinstance(self.event, AliasEvent): + self.expect_alias() + elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): + self.process_anchor(u'&') + self.process_tag() + if isinstance(self.event, ScalarEvent): + self.expect_scalar() + elif isinstance(self.event, SequenceStartEvent): + if self.flow_level or self.canonical or self.event.flow_style \ + or self.check_empty_sequence(): + self.expect_flow_sequence() + else: + self.expect_block_sequence() + elif isinstance(self.event, MappingStartEvent): + if self.flow_level or self.canonical or self.event.flow_style \ + or self.check_empty_mapping(): + self.expect_flow_mapping() + else: + self.expect_block_mapping() + else: + raise EmitterError("expected NodeEvent, but got %s" % self.event) + + def expect_alias(self): + if self.event.anchor is None: + raise EmitterError("anchor is not specified for alias") + self.process_anchor(u'*') + self.state = self.states.pop() + + def expect_scalar(self): + self.increase_indent(flow=True) + self.process_scalar() + self.indent = self.indents.pop() + self.state = self.states.pop() + + # Flow sequence handlers. + + def expect_flow_sequence(self): + self.write_indicator(u'[', True, whitespace=True) + self.flow_level += 1 + self.increase_indent(flow=True) + self.state = self.expect_first_flow_sequence_item + + def expect_first_flow_sequence_item(self): + if isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + self.write_indicator(u']', False) + self.state = self.states.pop() + else: + if self.canonical or self.column > self.best_width: + self.write_indent() + self.states.append(self.expect_flow_sequence_item) + self.expect_node(sequence=True) + + def expect_flow_sequence_item(self): + if isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + if self.canonical: + self.write_indicator(u',', False) + self.write_indent() + self.write_indicator(u']', False) + self.state = self.states.pop() + else: + self.write_indicator(u',', False) + if self.canonical or self.column > self.best_width: + self.write_indent() + self.states.append(self.expect_flow_sequence_item) + self.expect_node(sequence=True) + + # Flow mapping handlers. + + def expect_flow_mapping(self): + self.write_indicator(u'{', True, whitespace=True) + self.flow_level += 1 + self.increase_indent(flow=True) + self.state = self.expect_first_flow_mapping_key + + def expect_first_flow_mapping_key(self): + if isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + self.write_indicator(u'}', False) + self.state = self.states.pop() + else: + if self.canonical or self.column > self.best_width: + self.write_indent() + if not self.canonical and self.check_simple_key(): + self.states.append(self.expect_flow_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator(u'?', True) + self.states.append(self.expect_flow_mapping_value) + self.expect_node(mapping=True) + + def expect_flow_mapping_key(self): + if isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.flow_level -= 1 + if self.canonical: + self.write_indicator(u',', False) + self.write_indent() + self.write_indicator(u'}', False) + self.state = self.states.pop() + else: + self.write_indicator(u',', False) + if self.canonical or self.column > self.best_width: + self.write_indent() + if not self.canonical and self.check_simple_key(): + self.states.append(self.expect_flow_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator(u'?', True) + self.states.append(self.expect_flow_mapping_value) + self.expect_node(mapping=True) + + def expect_flow_mapping_simple_value(self): + self.write_indicator(u':', False) + self.states.append(self.expect_flow_mapping_key) + self.expect_node(mapping=True) + + def expect_flow_mapping_value(self): + if self.canonical or self.column > self.best_width: + self.write_indent() + self.write_indicator(u':', True) + self.states.append(self.expect_flow_mapping_key) + self.expect_node(mapping=True) + + # Block sequence handlers. + + def expect_block_sequence(self): + indentless = (self.mapping_context and not self.indention) + self.increase_indent(flow=False, indentless=indentless) + self.state = self.expect_first_block_sequence_item + + def expect_first_block_sequence_item(self): + return self.expect_block_sequence_item(first=True) + + def expect_block_sequence_item(self, first=False): + if not first and isinstance(self.event, SequenceEndEvent): + self.indent = self.indents.pop() + self.state = self.states.pop() + else: + self.write_indent() + self.write_indicator(u'-', True, indention=True) + self.states.append(self.expect_block_sequence_item) + self.expect_node(sequence=True) + + # Block mapping handlers. + + def expect_block_mapping(self): + self.increase_indent(flow=False) + self.state = self.expect_first_block_mapping_key + + def expect_first_block_mapping_key(self): + return self.expect_block_mapping_key(first=True) + + def expect_block_mapping_key(self, first=False): + if not first and isinstance(self.event, MappingEndEvent): + self.indent = self.indents.pop() + self.state = self.states.pop() + else: + self.write_indent() + if self.check_simple_key(): + self.states.append(self.expect_block_mapping_simple_value) + self.expect_node(mapping=True, simple_key=True) + else: + self.write_indicator(u'?', True, indention=True) + self.states.append(self.expect_block_mapping_value) + self.expect_node(mapping=True) + + def expect_block_mapping_simple_value(self): + self.write_indicator(u':', False) + self.states.append(self.expect_block_mapping_key) + self.expect_node(mapping=True) + + def expect_block_mapping_value(self): + self.write_indent() + self.write_indicator(u':', True, indention=True) + self.states.append(self.expect_block_mapping_key) + self.expect_node(mapping=True) + + # Checkers. + + def check_empty_sequence(self): + return (isinstance(self.event, SequenceStartEvent) and self.events + and isinstance(self.events[0], SequenceEndEvent)) + + def check_empty_mapping(self): + return (isinstance(self.event, MappingStartEvent) and self.events + and isinstance(self.events[0], MappingEndEvent)) + + def check_empty_document(self): + if not isinstance(self.event, DocumentStartEvent) or not self.events: + return False + event = self.events[0] + return (isinstance(event, ScalarEvent) and event.anchor is None + and event.tag is None and event.implicit and event.value == u'') + + def check_simple_key(self): + length = 0 + if isinstance(self.event, NodeEvent) and self.event.anchor is not None: + if self.prepared_anchor is None: + self.prepared_anchor = self.prepare_anchor(self.event.anchor) + length += len(self.prepared_anchor) + if isinstance(self.event, (ScalarEvent, CollectionStartEvent)) \ + and self.event.tag is not None: + if self.prepared_tag is None: + self.prepared_tag = self.prepare_tag(self.event.tag) + length += len(self.prepared_tag) + if isinstance(self.event, ScalarEvent): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + length += len(self.analysis.scalar) + return (length < 128 and (isinstance(self.event, AliasEvent) + or (isinstance(self.event, ScalarEvent) + and not self.analysis.empty and not self.analysis.multiline) + or self.check_empty_sequence() or self.check_empty_mapping())) + + # Anchor, Tag, and Scalar processors. + + def process_anchor(self, indicator): + if self.event.anchor is None: + self.prepared_anchor = None + return + if self.prepared_anchor is None: + self.prepared_anchor = self.prepare_anchor(self.event.anchor) + if self.prepared_anchor: + self.write_indicator(indicator+self.prepared_anchor, True) + self.prepared_anchor = None + + def process_tag(self): + tag = self.event.tag + if isinstance(self.event, ScalarEvent): + if self.style is None: + self.style = self.choose_scalar_style() + if ((not self.canonical or tag is None) and + ((self.style == '' and self.event.implicit[0]) + or (self.style != '' and self.event.implicit[1]))): + self.prepared_tag = None + return + if self.event.implicit[0] and tag is None: + tag = u'!' + self.prepared_tag = None + else: + if (not self.canonical or tag is None) and self.event.implicit: + self.prepared_tag = None + return + if tag is None: + raise EmitterError("tag is not specified") + if self.prepared_tag is None: + self.prepared_tag = self.prepare_tag(tag) + if self.prepared_tag: + self.write_indicator(self.prepared_tag, True) + self.prepared_tag = None + + def choose_scalar_style(self): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + if self.event.style == '"' or self.canonical: + return '"' + if not self.event.style and self.event.implicit[0]: + if (not (self.simple_key_context and + (self.analysis.empty or self.analysis.multiline)) + and (self.flow_level and self.analysis.allow_flow_plain + or (not self.flow_level and self.analysis.allow_block_plain))): + return '' + if self.event.style and self.event.style in '|>': + if (not self.flow_level and not self.simple_key_context + and self.analysis.allow_block): + return self.event.style + if not self.event.style or self.event.style == '\'': + if (self.analysis.allow_single_quoted and + not (self.simple_key_context and self.analysis.multiline)): + return '\'' + return '"' + + def process_scalar(self): + if self.analysis is None: + self.analysis = self.analyze_scalar(self.event.value) + if self.style is None: + self.style = self.choose_scalar_style() + split = (not self.simple_key_context) + #if self.analysis.multiline and split \ + # and (not self.style or self.style in '\'\"'): + # self.write_indent() + if self.style == '"': + self.write_double_quoted(self.analysis.scalar, split) + elif self.style == '\'': + self.write_single_quoted(self.analysis.scalar, split) + elif self.style == '>': + self.write_folded(self.analysis.scalar) + elif self.style == '|': + self.write_literal(self.analysis.scalar) + else: + self.write_plain(self.analysis.scalar, split) + self.analysis = None + self.style = None + + # Analyzers. + + def prepare_version(self, version): + major, minor = version + if major != 1: + raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) + return u'%d.%d' % (major, minor) + + def prepare_tag_handle(self, handle): + if not handle: + raise EmitterError("tag handle must not be empty") + if handle[0] != u'!' or handle[-1] != u'!': + raise EmitterError("tag handle must start and end with '!': %r" + % (handle.encode('utf-8'))) + for ch in handle[1:-1]: + if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_'): + raise EmitterError("invalid character %r in the tag handle: %r" + % (ch.encode('utf-8'), handle.encode('utf-8'))) + return handle + + def prepare_tag_prefix(self, prefix): + if not prefix: + raise EmitterError("tag prefix must not be empty") + chunks = [] + start = end = 0 + if prefix[0] == u'!': + end = 1 + while end < len(prefix): + ch = prefix[end] + if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-;/?!:@&=+$,_.~*\'()[]': + end += 1 + else: + if start < end: + chunks.append(prefix[start:end]) + start = end = end+1 + data = ch.encode('utf-8') + for ch in data: + chunks.append(u'%%%02X' % ord(ch)) + if start < end: + chunks.append(prefix[start:end]) + return u''.join(chunks) + + def prepare_tag(self, tag): + if not tag: + raise EmitterError("tag must not be empty") + if tag == u'!': + return tag + handle = None + suffix = tag + prefixes = self.tag_prefixes.keys() + prefixes.sort() + for prefix in prefixes: + if tag.startswith(prefix) \ + and (prefix == u'!' or len(prefix) < len(tag)): + handle = self.tag_prefixes[prefix] + suffix = tag[len(prefix):] + chunks = [] + start = end = 0 + while end < len(suffix): + ch = suffix[end] + if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-;/?:@&=+$,_.~*\'()[]' \ + or (ch == u'!' and handle != u'!'): + end += 1 + else: + if start < end: + chunks.append(suffix[start:end]) + start = end = end+1 + data = ch.encode('utf-8') + for ch in data: + chunks.append(u'%%%02X' % ord(ch)) + if start < end: + chunks.append(suffix[start:end]) + suffix_text = u''.join(chunks) + if handle: + return u'%s%s' % (handle, suffix_text) + else: + return u'!<%s>' % suffix_text + + def prepare_anchor(self, anchor): + if not anchor: + raise EmitterError("anchor must not be empty") + for ch in anchor: + if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_'): + raise EmitterError("invalid character %r in the anchor: %r" + % (ch.encode('utf-8'), anchor.encode('utf-8'))) + return anchor + + def analyze_scalar(self, scalar): + + # Empty scalar is a special case. + if not scalar: + return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, + allow_flow_plain=False, allow_block_plain=True, + allow_single_quoted=True, allow_double_quoted=True, + allow_block=False) + + # Indicators and special characters. + block_indicators = False + flow_indicators = False + line_breaks = False + special_characters = False + + # Important whitespace combinations. + leading_space = False + leading_break = False + trailing_space = False + trailing_break = False + break_space = False + space_break = False + + # Check document indicators. + if scalar.startswith(u'---') or scalar.startswith(u'...'): + block_indicators = True + flow_indicators = True + + # First character or preceded by a whitespace. + preceeded_by_whitespace = True + + # Last character or followed by a whitespace. + followed_by_whitespace = (len(scalar) == 1 or + scalar[1] in u'\0 \t\r\n\x85\u2028\u2029') + + # The previous character is a space. + previous_space = False + + # The previous character is a break. + previous_break = False + + index = 0 + while index < len(scalar): + ch = scalar[index] + + # Check for indicators. + if index == 0: + # Leading indicators are special characters. + if ch in u'#,[]{}&*!|>\'\"%@`': + flow_indicators = True + block_indicators = True + if ch in u'?:': + flow_indicators = True + if followed_by_whitespace: + block_indicators = True + if ch == u'-' and followed_by_whitespace: + flow_indicators = True + block_indicators = True + else: + # Some indicators cannot appear within a scalar as well. + if ch in u',?[]{}': + flow_indicators = True + if ch == u':': + flow_indicators = True + if followed_by_whitespace: + block_indicators = True + if ch == u'#' and preceeded_by_whitespace: + flow_indicators = True + block_indicators = True + + # Check for line breaks, special, and unicode characters. + if ch in u'\n\x85\u2028\u2029': + line_breaks = True + if not (ch == u'\n' or u'\x20' <= ch <= u'\x7E'): + if (ch == u'\x85' or u'\xA0' <= ch <= u'\uD7FF' + or u'\uE000' <= ch <= u'\uFFFD') and ch != u'\uFEFF': + unicode_characters = True + if not self.allow_unicode: + special_characters = True + else: + special_characters = True + + # Detect important whitespace combinations. + if ch == u' ': + if index == 0: + leading_space = True + if index == len(scalar)-1: + trailing_space = True + if previous_break: + break_space = True + previous_space = True + previous_break = False + elif ch in u'\n\x85\u2028\u2029': + if index == 0: + leading_break = True + if index == len(scalar)-1: + trailing_break = True + if previous_space: + space_break = True + previous_space = False + previous_break = True + else: + previous_space = False + previous_break = False + + # Prepare for the next character. + index += 1 + preceeded_by_whitespace = (ch in u'\0 \t\r\n\x85\u2028\u2029') + followed_by_whitespace = (index+1 >= len(scalar) or + scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029') + + # Let's decide what styles are allowed. + allow_flow_plain = True + allow_block_plain = True + allow_single_quoted = True + allow_double_quoted = True + allow_block = True + + # Leading and trailing whitespaces are bad for plain scalars. + if (leading_space or leading_break + or trailing_space or trailing_break): + allow_flow_plain = allow_block_plain = False + + # We do not permit trailing spaces for block scalars. + if trailing_space: + allow_block = False + + # Spaces at the beginning of a new line are only acceptable for block + # scalars. + if break_space: + allow_flow_plain = allow_block_plain = allow_single_quoted = False + + # Spaces followed by breaks, as well as special character are only + # allowed for double quoted scalars. + if space_break or special_characters: + allow_flow_plain = allow_block_plain = \ + allow_single_quoted = allow_block = False + + # Although the plain scalar writer supports breaks, we never emit + # multiline plain scalars. + if line_breaks: + allow_flow_plain = allow_block_plain = False + + # Flow indicators are forbidden for flow plain scalars. + if flow_indicators: + allow_flow_plain = False + + # Block indicators are forbidden for block plain scalars. + if block_indicators: + allow_block_plain = False + + return ScalarAnalysis(scalar=scalar, + empty=False, multiline=line_breaks, + allow_flow_plain=allow_flow_plain, + allow_block_plain=allow_block_plain, + allow_single_quoted=allow_single_quoted, + allow_double_quoted=allow_double_quoted, + allow_block=allow_block) + + # Writers. + + def flush_stream(self): + if hasattr(self.stream, 'flush'): + self.stream.flush() + + def write_stream_start(self): + # Write BOM if needed. + if self.encoding and self.encoding.startswith('utf-16'): + self.stream.write(u'\uFEFF'.encode(self.encoding)) + + def write_stream_end(self): + self.flush_stream() + + def write_indicator(self, indicator, need_whitespace, + whitespace=False, indention=False): + if self.whitespace or not need_whitespace: + data = indicator + else: + data = u' '+indicator + self.whitespace = whitespace + self.indention = self.indention and indention + self.column += len(data) + self.open_ended = False + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_indent(self): + indent = self.indent or 0 + if not self.indention or self.column > indent \ + or (self.column == indent and not self.whitespace): + self.write_line_break() + if self.column < indent: + self.whitespace = True + data = u' '*(indent-self.column) + self.column = indent + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_line_break(self, data=None): + if data is None: + data = self.best_line_break + self.whitespace = True + self.indention = True + self.line += 1 + self.column = 0 + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + + def write_version_directive(self, version_text): + data = u'%%YAML %s' % version_text + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_line_break() + + def write_tag_directive(self, handle_text, prefix_text): + data = u'%%TAG %s %s' % (handle_text, prefix_text) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_line_break() + + # Scalar streams. + + def write_single_quoted(self, text, split=True): + self.write_indicator(u'\'', True) + spaces = False + breaks = False + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if spaces: + if ch is None or ch != u' ': + if start+1 == end and self.column > self.best_width and split \ + and start != 0 and end != len(text): + self.write_indent() + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + elif breaks: + if ch is None or ch not in u'\n\x85\u2028\u2029': + if text[start] == u'\n': + self.write_line_break() + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + self.write_indent() + start = end + else: + if ch is None or ch in u' \n\x85\u2028\u2029' or ch == u'\'': + if start < end: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch == u'\'': + data = u'\'\'' + self.column += 2 + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + 1 + if ch is not None: + spaces = (ch == u' ') + breaks = (ch in u'\n\x85\u2028\u2029') + end += 1 + self.write_indicator(u'\'', False) + + ESCAPE_REPLACEMENTS = { + u'\0': u'0', + u'\x07': u'a', + u'\x08': u'b', + u'\x09': u't', + u'\x0A': u'n', + u'\x0B': u'v', + u'\x0C': u'f', + u'\x0D': u'r', + u'\x1B': u'e', + u'\"': u'\"', + u'\\': u'\\', + u'\x85': u'N', + u'\xA0': u'_', + u'\u2028': u'L', + u'\u2029': u'P', + } + + def write_double_quoted(self, text, split=True): + self.write_indicator(u'"', True) + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if ch is None or ch in u'"\\\x85\u2028\u2029\uFEFF' \ + or not (u'\x20' <= ch <= u'\x7E' + or (self.allow_unicode + and (u'\xA0' <= ch <= u'\uD7FF' + or u'\uE000' <= ch <= u'\uFFFD'))): + if start < end: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch is not None: + if ch in self.ESCAPE_REPLACEMENTS: + data = u'\\'+self.ESCAPE_REPLACEMENTS[ch] + elif ch <= u'\xFF': + data = u'\\x%02X' % ord(ch) + elif ch <= u'\uFFFF': + data = u'\\u%04X' % ord(ch) + else: + data = u'\\U%08X' % ord(ch) + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end+1 + if 0 < end < len(text)-1 and (ch == u' ' or start >= end) \ + and self.column+(end-start) > self.best_width and split: + data = text[start:end]+u'\\' + if start < end: + start = end + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.write_indent() + self.whitespace = False + self.indention = False + if text[start] == u' ': + data = u'\\' + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + end += 1 + self.write_indicator(u'"', False) + + def determine_block_hints(self, text): + hints = u'' + if text: + if text[0] in u' \n\x85\u2028\u2029': + hints += unicode(self.best_indent) + if text[-1] not in u'\n\x85\u2028\u2029': + hints += u'-' + elif len(text) == 1 or text[-2] in u'\n\x85\u2028\u2029': + hints += u'+' + return hints + + def write_folded(self, text): + hints = self.determine_block_hints(text) + self.write_indicator(u'>'+hints, True) + if hints[-1:] == u'+': + self.open_ended = True + self.write_line_break() + leading_space = True + spaces = False + breaks = True + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if breaks: + if ch is None or ch not in u'\n\x85\u2028\u2029': + if not leading_space and ch is not None and ch != u' ' \ + and text[start] == u'\n': + self.write_line_break() + leading_space = (ch == u' ') + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + if ch is not None: + self.write_indent() + start = end + elif spaces: + if ch != u' ': + if start+1 == end and self.column > self.best_width: + self.write_indent() + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + else: + if ch is None or ch in u' \n\x85\u2028\u2029': + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + if ch is None: + self.write_line_break() + start = end + if ch is not None: + breaks = (ch in u'\n\x85\u2028\u2029') + spaces = (ch == u' ') + end += 1 + + def write_literal(self, text): + hints = self.determine_block_hints(text) + self.write_indicator(u'|'+hints, True) + if hints[-1:] == u'+': + self.open_ended = True + self.write_line_break() + breaks = True + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if breaks: + if ch is None or ch not in u'\n\x85\u2028\u2029': + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + if ch is not None: + self.write_indent() + start = end + else: + if ch is None or ch in u'\n\x85\u2028\u2029': + data = text[start:end] + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + if ch is None: + self.write_line_break() + start = end + if ch is not None: + breaks = (ch in u'\n\x85\u2028\u2029') + end += 1 + + def write_plain(self, text, split=True): + if self.root_context: + self.open_ended = True + if not text: + return + if not self.whitespace: + data = u' ' + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + self.whitespace = False + self.indention = False + spaces = False + breaks = False + start = end = 0 + while end <= len(text): + ch = None + if end < len(text): + ch = text[end] + if spaces: + if ch != u' ': + if start+1 == end and self.column > self.best_width and split: + self.write_indent() + self.whitespace = False + self.indention = False + else: + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + elif breaks: + if ch not in u'\n\x85\u2028\u2029': + if text[start] == u'\n': + self.write_line_break() + for br in text[start:end]: + if br == u'\n': + self.write_line_break() + else: + self.write_line_break(br) + self.write_indent() + self.whitespace = False + self.indention = False + start = end + else: + if ch is None or ch in u' \n\x85\u2028\u2029': + data = text[start:end] + self.column += len(data) + if self.encoding: + data = data.encode(self.encoding) + self.stream.write(data) + start = end + if ch is not None: + spaces = (ch == u' ') + breaks = (ch in u'\n\x85\u2028\u2029') + end += 1 + diff --git a/plugins_available/util/yaml/error.py b/plugins_available/util/yaml/error.py new file mode 100644 index 0000000..577686d --- /dev/null +++ b/plugins_available/util/yaml/error.py @@ -0,0 +1,75 @@ + +__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError'] + +class Mark(object): + + def __init__(self, name, index, line, column, buffer, pointer): + self.name = name + self.index = index + self.line = line + self.column = column + self.buffer = buffer + self.pointer = pointer + + def get_snippet(self, indent=4, max_length=75): + if self.buffer is None: + return None + head = '' + start = self.pointer + while start > 0 and self.buffer[start-1] not in u'\0\r\n\x85\u2028\u2029': + start -= 1 + if self.pointer-start > max_length/2-1: + head = ' ... ' + start += 5 + break + tail = '' + end = self.pointer + while end < len(self.buffer) and self.buffer[end] not in u'\0\r\n\x85\u2028\u2029': + end += 1 + if end-self.pointer > max_length/2-1: + tail = ' ... ' + end -= 5 + break + snippet = self.buffer[start:end].encode('utf-8') + return ' '*indent + head + snippet + tail + '\n' \ + + ' '*(indent+self.pointer-start+len(head)) + '^' + + def __str__(self): + snippet = self.get_snippet() + where = " in \"%s\", line %d, column %d" \ + % (self.name, self.line+1, self.column+1) + if snippet is not None: + where += ":\n"+snippet + return where + +class YAMLError(Exception): + pass + +class MarkedYAMLError(YAMLError): + + def __init__(self, context=None, context_mark=None, + problem=None, problem_mark=None, note=None): + self.context = context + self.context_mark = context_mark + self.problem = problem + self.problem_mark = problem_mark + self.note = note + + def __str__(self): + lines = [] + if self.context is not None: + lines.append(self.context) + if self.context_mark is not None \ + and (self.problem is None or self.problem_mark is None + or self.context_mark.name != self.problem_mark.name + or self.context_mark.line != self.problem_mark.line + or self.context_mark.column != self.problem_mark.column): + lines.append(str(self.context_mark)) + if self.problem is not None: + lines.append(self.problem) + if self.problem_mark is not None: + lines.append(str(self.problem_mark)) + if self.note is not None: + lines.append(self.note) + return '\n'.join(lines) + diff --git a/plugins_available/util/yaml/events.py b/plugins_available/util/yaml/events.py new file mode 100644 index 0000000..f79ad38 --- /dev/null +++ b/plugins_available/util/yaml/events.py @@ -0,0 +1,86 @@ + +# Abstract classes. + +class Event(object): + def __init__(self, start_mark=None, end_mark=None): + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + attributes = [key for key in ['anchor', 'tag', 'implicit', 'value'] + if hasattr(self, key)] + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) + +class NodeEvent(Event): + def __init__(self, anchor, start_mark=None, end_mark=None): + self.anchor = anchor + self.start_mark = start_mark + self.end_mark = end_mark + +class CollectionStartEvent(NodeEvent): + def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None, + flow_style=None): + self.anchor = anchor + self.tag = tag + self.implicit = implicit + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + +class CollectionEndEvent(Event): + pass + +# Implementations. + +class StreamStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + +class StreamEndEvent(Event): + pass + +class DocumentStartEvent(Event): + def __init__(self, start_mark=None, end_mark=None, + explicit=None, version=None, tags=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + self.version = version + self.tags = tags + +class DocumentEndEvent(Event): + def __init__(self, start_mark=None, end_mark=None, + explicit=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.explicit = explicit + +class AliasEvent(NodeEvent): + pass + +class ScalarEvent(NodeEvent): + def __init__(self, anchor, tag, implicit, value, + start_mark=None, end_mark=None, style=None): + self.anchor = anchor + self.tag = tag + self.implicit = implicit + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + +class SequenceStartEvent(CollectionStartEvent): + pass + +class SequenceEndEvent(CollectionEndEvent): + pass + +class MappingStartEvent(CollectionStartEvent): + pass + +class MappingEndEvent(CollectionEndEvent): + pass + diff --git a/plugins_available/util/yaml/loader.py b/plugins_available/util/yaml/loader.py new file mode 100644 index 0000000..293ff46 --- /dev/null +++ b/plugins_available/util/yaml/loader.py @@ -0,0 +1,40 @@ + +__all__ = ['BaseLoader', 'SafeLoader', 'Loader'] + +from reader import * +from scanner import * +from parser import * +from composer import * +from constructor import * +from resolver import * + +class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + BaseConstructor.__init__(self) + BaseResolver.__init__(self) + +class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + SafeConstructor.__init__(self) + Resolver.__init__(self) + +class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + Constructor.__init__(self) + Resolver.__init__(self) + diff --git a/plugins_available/util/yaml/nodes.py b/plugins_available/util/yaml/nodes.py new file mode 100644 index 0000000..c4f070c --- /dev/null +++ b/plugins_available/util/yaml/nodes.py @@ -0,0 +1,49 @@ + +class Node(object): + def __init__(self, tag, value, start_mark, end_mark): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + value = self.value + #if isinstance(value, list): + # if len(value) == 0: + # value = '' + # elif len(value) == 1: + # value = '<1 item>' + # else: + # value = '<%d items>' % len(value) + #else: + # if len(value) > 75: + # value = repr(value[:70]+u' ... ') + # else: + # value = repr(value) + value = repr(value) + return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) + +class ScalarNode(Node): + id = 'scalar' + def __init__(self, tag, value, + start_mark=None, end_mark=None, style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + +class CollectionNode(Node): + def __init__(self, tag, value, + start_mark=None, end_mark=None, flow_style=None): + self.tag = tag + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + self.flow_style = flow_style + +class SequenceNode(CollectionNode): + id = 'sequence' + +class MappingNode(CollectionNode): + id = 'mapping' + diff --git a/plugins_available/util/yaml/parser.py b/plugins_available/util/yaml/parser.py new file mode 100644 index 0000000..b6a7416 --- /dev/null +++ b/plugins_available/util/yaml/parser.py @@ -0,0 +1,584 @@ + +# The following YAML grammar is LL(1) and is parsed by a recursive descent +# parser. +# +# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +# implicit_document ::= block_node DOCUMENT-END* +# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +# block_node_or_indentless_sequence ::= +# ALIAS +# | properties (block_content | indentless_block_sequence)? +# | block_content +# | indentless_block_sequence +# block_node ::= ALIAS +# | properties block_content? +# | block_content +# flow_node ::= ALIAS +# | properties flow_content? +# | flow_content +# properties ::= TAG ANCHOR? | ANCHOR TAG? +# block_content ::= block_collection | flow_collection | SCALAR +# flow_content ::= flow_collection | SCALAR +# block_collection ::= block_sequence | block_mapping +# flow_collection ::= flow_sequence | flow_mapping +# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +# indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +# block_mapping ::= BLOCK-MAPPING_START +# ((KEY block_node_or_indentless_sequence?)? +# (VALUE block_node_or_indentless_sequence?)?)* +# BLOCK-END +# flow_sequence ::= FLOW-SEQUENCE-START +# (flow_sequence_entry FLOW-ENTRY)* +# flow_sequence_entry? +# FLOW-SEQUENCE-END +# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +# flow_mapping ::= FLOW-MAPPING-START +# (flow_mapping_entry FLOW-ENTRY)* +# flow_mapping_entry? +# FLOW-MAPPING-END +# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +# +# FIRST sets: +# +# stream: { STREAM-START } +# explicit_document: { DIRECTIVE DOCUMENT-START } +# implicit_document: FIRST(block_node) +# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START } +# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START } +# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } +# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } +# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START } +# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } +# block_sequence: { BLOCK-SEQUENCE-START } +# block_mapping: { BLOCK-MAPPING-START } +# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY } +# indentless_sequence: { ENTRY } +# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } +# flow_sequence: { FLOW-SEQUENCE-START } +# flow_mapping: { FLOW-MAPPING-START } +# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } +# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } + +__all__ = ['Parser', 'ParserError'] + +from error import MarkedYAMLError +from tokens import * +from events import * +from scanner import * + +class ParserError(MarkedYAMLError): + pass + +class Parser(object): + # Since writing a recursive-descendant parser is a straightforward task, we + # do not give many comments here. + + DEFAULT_TAGS = { + u'!': u'!', + u'!!': u'tag:yaml.org,2002:', + } + + def __init__(self): + self.current_event = None + self.yaml_version = None + self.tag_handles = {} + self.states = [] + self.marks = [] + self.state = self.parse_stream_start + + def check_event(self, *choices): + # Check the type of the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + if self.current_event is not None: + if not choices: + return True + for choice in choices: + if isinstance(self.current_event, choice): + return True + return False + + def peek_event(self): + # Get the next event. + if self.current_event is None: + if self.state: + self.current_event = self.state() + return self.current_event + + def get_event(self): + # Get the next event and proceed further. + if self.current_event is None: + if self.state: + self.current_event = self.state() + value = self.current_event + self.current_event = None + return value + + # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END + # implicit_document ::= block_node DOCUMENT-END* + # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* + + def parse_stream_start(self): + + # Parse the stream start. + token = self.get_token() + event = StreamStartEvent(token.start_mark, token.end_mark, + encoding=token.encoding) + + # Prepare the next state. + self.state = self.parse_implicit_document_start + + return event + + def parse_implicit_document_start(self): + + # Parse an implicit document. + if not self.check_token(DirectiveToken, DocumentStartToken, + StreamEndToken): + self.tag_handles = self.DEFAULT_TAGS + token = self.peek_token() + start_mark = end_mark = token.start_mark + event = DocumentStartEvent(start_mark, end_mark, + explicit=False) + + # Prepare the next state. + self.states.append(self.parse_document_end) + self.state = self.parse_block_node + + return event + + else: + return self.parse_document_start() + + def parse_document_start(self): + + # Parse any extra document end indicators. + while self.check_token(DocumentEndToken): + self.get_token() + + # Parse an explicit document. + if not self.check_token(StreamEndToken): + token = self.peek_token() + start_mark = token.start_mark + version, tags = self.process_directives() + if not self.check_token(DocumentStartToken): + raise ParserError(None, None, + "expected '', but found %r" + % self.peek_token().id, + self.peek_token().start_mark) + token = self.get_token() + end_mark = token.end_mark + event = DocumentStartEvent(start_mark, end_mark, + explicit=True, version=version, tags=tags) + self.states.append(self.parse_document_end) + self.state = self.parse_document_content + else: + # Parse the end of the stream. + token = self.get_token() + event = StreamEndEvent(token.start_mark, token.end_mark) + assert not self.states + assert not self.marks + self.state = None + return event + + def parse_document_end(self): + + # Parse the document end. + token = self.peek_token() + start_mark = end_mark = token.start_mark + explicit = False + if self.check_token(DocumentEndToken): + token = self.get_token() + end_mark = token.end_mark + explicit = True + event = DocumentEndEvent(start_mark, end_mark, + explicit=explicit) + + # Prepare the next state. + self.state = self.parse_document_start + + return event + + def parse_document_content(self): + if self.check_token(DirectiveToken, + DocumentStartToken, DocumentEndToken, StreamEndToken): + event = self.process_empty_scalar(self.peek_token().start_mark) + self.state = self.states.pop() + return event + else: + return self.parse_block_node() + + def process_directives(self): + self.yaml_version = None + self.tag_handles = {} + while self.check_token(DirectiveToken): + token = self.get_token() + if token.name == u'YAML': + if self.yaml_version is not None: + raise ParserError(None, None, + "found duplicate YAML directive", token.start_mark) + major, minor = token.value + if major != 1: + raise ParserError(None, None, + "found incompatible YAML document (version 1.* is required)", + token.start_mark) + self.yaml_version = token.value + elif token.name == u'TAG': + handle, prefix = token.value + if handle in self.tag_handles: + raise ParserError(None, None, + "duplicate tag handle %r" % handle.encode('utf-8'), + token.start_mark) + self.tag_handles[handle] = prefix + if self.tag_handles: + value = self.yaml_version, self.tag_handles.copy() + else: + value = self.yaml_version, None + for key in self.DEFAULT_TAGS: + if key not in self.tag_handles: + self.tag_handles[key] = self.DEFAULT_TAGS[key] + return value + + # block_node_or_indentless_sequence ::= ALIAS + # | properties (block_content | indentless_block_sequence)? + # | block_content + # | indentless_block_sequence + # block_node ::= ALIAS + # | properties block_content? + # | block_content + # flow_node ::= ALIAS + # | properties flow_content? + # | flow_content + # properties ::= TAG ANCHOR? | ANCHOR TAG? + # block_content ::= block_collection | flow_collection | SCALAR + # flow_content ::= flow_collection | SCALAR + # block_collection ::= block_sequence | block_mapping + # flow_collection ::= flow_sequence | flow_mapping + + def parse_block_node(self): + return self.parse_node(block=True) + + def parse_flow_node(self): + return self.parse_node() + + def parse_block_node_or_indentless_sequence(self): + return self.parse_node(block=True, indentless_sequence=True) + + def parse_node(self, block=False, indentless_sequence=False): + if self.check_token(AliasToken): + token = self.get_token() + event = AliasEvent(token.value, token.start_mark, token.end_mark) + self.state = self.states.pop() + else: + anchor = None + tag = None + start_mark = end_mark = tag_mark = None + if self.check_token(AnchorToken): + token = self.get_token() + start_mark = token.start_mark + end_mark = token.end_mark + anchor = token.value + if self.check_token(TagToken): + token = self.get_token() + tag_mark = token.start_mark + end_mark = token.end_mark + tag = token.value + elif self.check_token(TagToken): + token = self.get_token() + start_mark = tag_mark = token.start_mark + end_mark = token.end_mark + tag = token.value + if self.check_token(AnchorToken): + token = self.get_token() + end_mark = token.end_mark + anchor = token.value + if tag is not None: + handle, suffix = tag + if handle is not None: + if handle not in self.tag_handles: + raise ParserError("while parsing a node", start_mark, + "found undefined tag handle %r" % handle.encode('utf-8'), + tag_mark) + tag = self.tag_handles[handle]+suffix + else: + tag = suffix + #if tag == u'!': + # raise ParserError("while parsing a node", start_mark, + # "found non-specific tag '!'", tag_mark, + # "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.") + if start_mark is None: + start_mark = end_mark = self.peek_token().start_mark + event = None + implicit = (tag is None or tag == u'!') + if indentless_sequence and self.check_token(BlockEntryToken): + end_mark = self.peek_token().end_mark + event = SequenceStartEvent(anchor, tag, implicit, + start_mark, end_mark) + self.state = self.parse_indentless_sequence_entry + else: + if self.check_token(ScalarToken): + token = self.get_token() + end_mark = token.end_mark + if (token.plain and tag is None) or tag == u'!': + implicit = (True, False) + elif tag is None: + implicit = (False, True) + else: + implicit = (False, False) + event = ScalarEvent(anchor, tag, implicit, token.value, + start_mark, end_mark, style=token.style) + self.state = self.states.pop() + elif self.check_token(FlowSequenceStartToken): + end_mark = self.peek_token().end_mark + event = SequenceStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_sequence_first_entry + elif self.check_token(FlowMappingStartToken): + end_mark = self.peek_token().end_mark + event = MappingStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=True) + self.state = self.parse_flow_mapping_first_key + elif block and self.check_token(BlockSequenceStartToken): + end_mark = self.peek_token().start_mark + event = SequenceStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=False) + self.state = self.parse_block_sequence_first_entry + elif block and self.check_token(BlockMappingStartToken): + end_mark = self.peek_token().start_mark + event = MappingStartEvent(anchor, tag, implicit, + start_mark, end_mark, flow_style=False) + self.state = self.parse_block_mapping_first_key + elif anchor is not None or tag is not None: + # Empty scalars are allowed even if a tag or an anchor is + # specified. + event = ScalarEvent(anchor, tag, (implicit, False), u'', + start_mark, end_mark) + self.state = self.states.pop() + else: + if block: + node = 'block' + else: + node = 'flow' + token = self.peek_token() + raise ParserError("while parsing a %s node" % node, start_mark, + "expected the node content, but found %r" % token.id, + token.start_mark) + return event + + # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END + + def parse_block_sequence_first_entry(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_block_sequence_entry() + + def parse_block_sequence_entry(self): + if self.check_token(BlockEntryToken): + token = self.get_token() + if not self.check_token(BlockEntryToken, BlockEndToken): + self.states.append(self.parse_block_sequence_entry) + return self.parse_block_node() + else: + self.state = self.parse_block_sequence_entry + return self.process_empty_scalar(token.end_mark) + if not self.check_token(BlockEndToken): + token = self.peek_token() + raise ParserError("while parsing a block collection", self.marks[-1], + "expected , but found %r" % token.id, token.start_mark) + token = self.get_token() + event = SequenceEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + # indentless_sequence ::= (BLOCK-ENTRY block_node?)+ + + def parse_indentless_sequence_entry(self): + if self.check_token(BlockEntryToken): + token = self.get_token() + if not self.check_token(BlockEntryToken, + KeyToken, ValueToken, BlockEndToken): + self.states.append(self.parse_indentless_sequence_entry) + return self.parse_block_node() + else: + self.state = self.parse_indentless_sequence_entry + return self.process_empty_scalar(token.end_mark) + token = self.peek_token() + event = SequenceEndEvent(token.start_mark, token.start_mark) + self.state = self.states.pop() + return event + + # block_mapping ::= BLOCK-MAPPING_START + # ((KEY block_node_or_indentless_sequence?)? + # (VALUE block_node_or_indentless_sequence?)?)* + # BLOCK-END + + def parse_block_mapping_first_key(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_block_mapping_key() + + def parse_block_mapping_key(self): + if self.check_token(KeyToken): + token = self.get_token() + if not self.check_token(KeyToken, ValueToken, BlockEndToken): + self.states.append(self.parse_block_mapping_value) + return self.parse_block_node_or_indentless_sequence() + else: + self.state = self.parse_block_mapping_value + return self.process_empty_scalar(token.end_mark) + if not self.check_token(BlockEndToken): + token = self.peek_token() + raise ParserError("while parsing a block mapping", self.marks[-1], + "expected , but found %r" % token.id, token.start_mark) + token = self.get_token() + event = MappingEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_block_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(KeyToken, ValueToken, BlockEndToken): + self.states.append(self.parse_block_mapping_key) + return self.parse_block_node_or_indentless_sequence() + else: + self.state = self.parse_block_mapping_key + return self.process_empty_scalar(token.end_mark) + else: + self.state = self.parse_block_mapping_key + token = self.peek_token() + return self.process_empty_scalar(token.start_mark) + + # flow_sequence ::= FLOW-SEQUENCE-START + # (flow_sequence_entry FLOW-ENTRY)* + # flow_sequence_entry? + # FLOW-SEQUENCE-END + # flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + # + # Note that while production rules for both flow_sequence_entry and + # flow_mapping_entry are equal, their interpretations are different. + # For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?` + # generate an inline mapping (set syntax). + + def parse_flow_sequence_first_entry(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_sequence_entry(first=True) + + def parse_flow_sequence_entry(self, first=False): + if not self.check_token(FlowSequenceEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + else: + token = self.peek_token() + raise ParserError("while parsing a flow sequence", self.marks[-1], + "expected ',' or ']', but got %r" % token.id, token.start_mark) + + if self.check_token(KeyToken): + token = self.peek_token() + event = MappingStartEvent(None, None, True, + token.start_mark, token.end_mark, + flow_style=True) + self.state = self.parse_flow_sequence_entry_mapping_key + return event + elif not self.check_token(FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry) + return self.parse_flow_node() + token = self.get_token() + event = SequenceEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_sequence_entry_mapping_key(self): + token = self.get_token() + if not self.check_token(ValueToken, + FlowEntryToken, FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry_mapping_value) + return self.parse_flow_node() + else: + self.state = self.parse_flow_sequence_entry_mapping_value + return self.process_empty_scalar(token.end_mark) + + def parse_flow_sequence_entry_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(FlowEntryToken, FlowSequenceEndToken): + self.states.append(self.parse_flow_sequence_entry_mapping_end) + return self.parse_flow_node() + else: + self.state = self.parse_flow_sequence_entry_mapping_end + return self.process_empty_scalar(token.end_mark) + else: + self.state = self.parse_flow_sequence_entry_mapping_end + token = self.peek_token() + return self.process_empty_scalar(token.start_mark) + + def parse_flow_sequence_entry_mapping_end(self): + self.state = self.parse_flow_sequence_entry + token = self.peek_token() + return MappingEndEvent(token.start_mark, token.start_mark) + + # flow_mapping ::= FLOW-MAPPING-START + # (flow_mapping_entry FLOW-ENTRY)* + # flow_mapping_entry? + # FLOW-MAPPING-END + # flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + + def parse_flow_mapping_first_key(self): + token = self.get_token() + self.marks.append(token.start_mark) + return self.parse_flow_mapping_key(first=True) + + def parse_flow_mapping_key(self, first=False): + if not self.check_token(FlowMappingEndToken): + if not first: + if self.check_token(FlowEntryToken): + self.get_token() + else: + token = self.peek_token() + raise ParserError("while parsing a flow mapping", self.marks[-1], + "expected ',' or '}', but got %r" % token.id, token.start_mark) + if self.check_token(KeyToken): + token = self.get_token() + if not self.check_token(ValueToken, + FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_value) + return self.parse_flow_node() + else: + self.state = self.parse_flow_mapping_value + return self.process_empty_scalar(token.end_mark) + elif not self.check_token(FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_empty_value) + return self.parse_flow_node() + token = self.get_token() + event = MappingEndEvent(token.start_mark, token.end_mark) + self.state = self.states.pop() + self.marks.pop() + return event + + def parse_flow_mapping_value(self): + if self.check_token(ValueToken): + token = self.get_token() + if not self.check_token(FlowEntryToken, FlowMappingEndToken): + self.states.append(self.parse_flow_mapping_key) + return self.parse_flow_node() + else: + self.state = self.parse_flow_mapping_key + return self.process_empty_scalar(token.end_mark) + else: + self.state = self.parse_flow_mapping_key + token = self.peek_token() + return self.process_empty_scalar(token.start_mark) + + def parse_flow_mapping_empty_value(self): + self.state = self.parse_flow_mapping_key + return self.process_empty_scalar(self.peek_token().start_mark) + + def process_empty_scalar(self, mark): + return ScalarEvent(None, None, (True, False), u'', mark, mark) + diff --git a/plugins_available/util/yaml/reader.py b/plugins_available/util/yaml/reader.py new file mode 100644 index 0000000..1e7a4db --- /dev/null +++ b/plugins_available/util/yaml/reader.py @@ -0,0 +1,225 @@ +# This module contains abstractions for the input stream. You don't have to +# looks further, there are no pretty code. +# +# We define two classes here. +# +# Mark(source, line, column) +# It's just a record and its only use is producing nice error messages. +# Parser does not use it for any other purposes. +# +# Reader(source, data) +# Reader determines the encoding of `data` and converts it to unicode. +# Reader provides the following methods and attributes: +# reader.peek(length=1) - return the next `length` characters +# reader.forward(length=1) - move the current position to `length` characters. +# reader.index - the number of the current character. +# reader.line, stream.column - the line and the column of the current character. + +__all__ = ['Reader', 'ReaderError'] + +from error import YAMLError, Mark + +import codecs, re + +# Unfortunately, codec functions in Python 2.3 does not support the `finish` +# arguments, so we have to write our own wrappers. + +try: + codecs.utf_8_decode('', 'strict', False) + from codecs import utf_8_decode, utf_16_le_decode, utf_16_be_decode + +except TypeError: + + def utf_16_le_decode(data, errors, finish=False): + if not finish and len(data) % 2 == 1: + data = data[:-1] + return codecs.utf_16_le_decode(data, errors) + + def utf_16_be_decode(data, errors, finish=False): + if not finish and len(data) % 2 == 1: + data = data[:-1] + return codecs.utf_16_be_decode(data, errors) + + def utf_8_decode(data, errors, finish=False): + if not finish: + # We are trying to remove a possible incomplete multibyte character + # from the suffix of the data. + # The first byte of a multi-byte sequence is in the range 0xc0 to 0xfd. + # All further bytes are in the range 0x80 to 0xbf. + # UTF-8 encoded UCS characters may be up to six bytes long. + count = 0 + while count < 5 and count < len(data) \ + and '\x80' <= data[-count-1] <= '\xBF': + count -= 1 + if count < 5 and count < len(data) \ + and '\xC0' <= data[-count-1] <= '\xFD': + data = data[:-count-1] + return codecs.utf_8_decode(data, errors) + +class ReaderError(YAMLError): + + def __init__(self, name, position, character, encoding, reason): + self.name = name + self.character = character + self.position = position + self.encoding = encoding + self.reason = reason + + def __str__(self): + if isinstance(self.character, str): + return "'%s' codec can't decode byte #x%02x: %s\n" \ + " in \"%s\", position %d" \ + % (self.encoding, ord(self.character), self.reason, + self.name, self.position) + else: + return "unacceptable character #x%04x: %s\n" \ + " in \"%s\", position %d" \ + % (self.character, self.reason, + self.name, self.position) + +class Reader(object): + # Reader: + # - determines the data encoding and converts it to unicode, + # - checks if characters are in allowed range, + # - adds '\0' to the end. + + # Reader accepts + # - a `str` object, + # - a `unicode` object, + # - a file-like object with its `read` method returning `str`, + # - a file-like object with its `read` method returning `unicode`. + + # Yeah, it's ugly and slow. + + def __init__(self, stream): + self.name = None + self.stream = None + self.stream_pointer = 0 + self.eof = True + self.buffer = u'' + self.pointer = 0 + self.raw_buffer = None + self.raw_decode = None + self.encoding = None + self.index = 0 + self.line = 0 + self.column = 0 + if isinstance(stream, unicode): + self.name = "" + self.check_printable(stream) + self.buffer = stream+u'\0' + elif isinstance(stream, str): + self.name = "" + self.raw_buffer = stream + self.determine_encoding() + else: + self.stream = stream + self.name = getattr(stream, 'name', "") + self.eof = False + self.raw_buffer = '' + self.determine_encoding() + + def peek(self, index=0): + try: + return self.buffer[self.pointer+index] + except IndexError: + self.update(index+1) + return self.buffer[self.pointer+index] + + def prefix(self, length=1): + if self.pointer+length >= len(self.buffer): + self.update(length) + return self.buffer[self.pointer:self.pointer+length] + + def forward(self, length=1): + if self.pointer+length+1 >= len(self.buffer): + self.update(length+1) + while length: + ch = self.buffer[self.pointer] + self.pointer += 1 + self.index += 1 + if ch in u'\n\x85\u2028\u2029' \ + or (ch == u'\r' and self.buffer[self.pointer] != u'\n'): + self.line += 1 + self.column = 0 + elif ch != u'\uFEFF': + self.column += 1 + length -= 1 + + def get_mark(self): + if self.stream is None: + return Mark(self.name, self.index, self.line, self.column, + self.buffer, self.pointer) + else: + return Mark(self.name, self.index, self.line, self.column, + None, None) + + def determine_encoding(self): + while not self.eof and len(self.raw_buffer) < 2: + self.update_raw() + if not isinstance(self.raw_buffer, unicode): + if self.raw_buffer.startswith(codecs.BOM_UTF16_LE): + self.raw_decode = utf_16_le_decode + self.encoding = 'utf-16-le' + elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE): + self.raw_decode = utf_16_be_decode + self.encoding = 'utf-16-be' + else: + self.raw_decode = utf_8_decode + self.encoding = 'utf-8' + self.update(1) + + NON_PRINTABLE = re.compile(u'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD]') + def check_printable(self, data): + match = self.NON_PRINTABLE.search(data) + if match: + character = match.group() + position = self.index+(len(self.buffer)-self.pointer)+match.start() + raise ReaderError(self.name, position, ord(character), + 'unicode', "special characters are not allowed") + + def update(self, length): + if self.raw_buffer is None: + return + self.buffer = self.buffer[self.pointer:] + self.pointer = 0 + while len(self.buffer) < length: + if not self.eof: + self.update_raw() + if self.raw_decode is not None: + try: + data, converted = self.raw_decode(self.raw_buffer, + 'strict', self.eof) + except UnicodeDecodeError, exc: + character = exc.object[exc.start] + if self.stream is not None: + position = self.stream_pointer-len(self.raw_buffer)+exc.start + else: + position = exc.start + raise ReaderError(self.name, position, character, + exc.encoding, exc.reason) + else: + data = self.raw_buffer + converted = len(data) + self.check_printable(data) + self.buffer += data + self.raw_buffer = self.raw_buffer[converted:] + if self.eof: + self.buffer += u'\0' + self.raw_buffer = None + break + + def update_raw(self, size=1024): + data = self.stream.read(size) + if data: + self.raw_buffer += data + self.stream_pointer += len(data) + else: + self.eof = True + +#try: +# import psyco +# psyco.bind(Reader) +#except ImportError: +# pass + diff --git a/plugins_available/util/yaml/representer.py b/plugins_available/util/yaml/representer.py new file mode 100644 index 0000000..f5606ec --- /dev/null +++ b/plugins_available/util/yaml/representer.py @@ -0,0 +1,489 @@ + +__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer', + 'RepresenterError'] + +from error import * +from nodes import * + +import datetime + +try: + set +except NameError: + from sets import Set as set + +import sys, copy_reg, types + +class RepresenterError(YAMLError): + pass + +class BaseRepresenter(object): + + yaml_representers = {} + yaml_multi_representers = {} + + def __init__(self, default_style=None, default_flow_style=None): + self.default_style = default_style + self.default_flow_style = default_flow_style + self.represented_objects = {} + self.object_keeper = [] + self.alias_key = None + + def represent(self, data): + node = self.represent_data(data) + self.serialize(node) + self.represented_objects = {} + self.object_keeper = [] + self.alias_key = None + + def get_classobj_bases(self, cls): + bases = [cls] + for base in cls.__bases__: + bases.extend(self.get_classobj_bases(base)) + return bases + + def represent_data(self, data): + if self.ignore_aliases(data): + self.alias_key = None + else: + self.alias_key = id(data) + if self.alias_key is not None: + if self.alias_key in self.represented_objects: + node = self.represented_objects[self.alias_key] + #if node is None: + # raise RepresenterError("recursive objects are not allowed: %r" % data) + return node + #self.represented_objects[alias_key] = None + self.object_keeper.append(data) + data_types = type(data).__mro__ + if type(data) is types.InstanceType: + data_types = self.get_classobj_bases(data.__class__)+list(data_types) + if data_types[0] in self.yaml_representers: + node = self.yaml_representers[data_types[0]](self, data) + else: + for data_type in data_types: + if data_type in self.yaml_multi_representers: + node = self.yaml_multi_representers[data_type](self, data) + break + else: + if None in self.yaml_multi_representers: + node = self.yaml_multi_representers[None](self, data) + elif None in self.yaml_representers: + node = self.yaml_representers[None](self, data) + else: + node = ScalarNode(None, unicode(data)) + #if alias_key is not None: + # self.represented_objects[alias_key] = node + return node + + def add_representer(cls, data_type, representer): + if not 'yaml_representers' in cls.__dict__: + cls.yaml_representers = cls.yaml_representers.copy() + cls.yaml_representers[data_type] = representer + add_representer = classmethod(add_representer) + + def add_multi_representer(cls, data_type, representer): + if not 'yaml_multi_representers' in cls.__dict__: + cls.yaml_multi_representers = cls.yaml_multi_representers.copy() + cls.yaml_multi_representers[data_type] = representer + add_multi_representer = classmethod(add_multi_representer) + + def represent_scalar(self, tag, value, style=None): + if style is None: + style = self.default_style + node = ScalarNode(tag, value, style=style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + return node + + def represent_sequence(self, tag, sequence, flow_style=None): + value = [] + node = SequenceNode(tag, value, flow_style=flow_style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + best_style = True + for item in sequence: + node_item = self.represent_data(item) + if not (isinstance(node_item, ScalarNode) and not node_item.style): + best_style = False + value.append(node_item) + if flow_style is None: + if self.default_flow_style is not None: + node.flow_style = self.default_flow_style + else: + node.flow_style = best_style + return node + + def represent_mapping(self, tag, mapping, flow_style=None): + value = [] + node = MappingNode(tag, value, flow_style=flow_style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + best_style = True + if hasattr(mapping, 'items'): + mapping = mapping.items() + mapping.sort() + for item_key, item_value in mapping: + node_key = self.represent_data(item_key) + node_value = self.represent_data(item_value) + if not (isinstance(node_key, ScalarNode) and not node_key.style): + best_style = False + if not (isinstance(node_value, ScalarNode) and not node_value.style): + best_style = False + value.append((node_key, node_value)) + if flow_style is None: + if self.default_flow_style is not None: + node.flow_style = self.default_flow_style + else: + node.flow_style = best_style + return node + + def ignore_aliases(self, data): + return False + +class SafeRepresenter(BaseRepresenter): + + def ignore_aliases(self, data): + if data in [None, ()]: + return True + if isinstance(data, (str, unicode, bool, int, float)): + return True + + def represent_none(self, data): + return self.represent_scalar(u'tag:yaml.org,2002:null', + u'null') + + def represent_str(self, data): + tag = None + style = None + try: + data = unicode(data, 'ascii') + tag = u'tag:yaml.org,2002:str' + except UnicodeDecodeError: + try: + data = unicode(data, 'utf-8') + tag = u'tag:yaml.org,2002:str' + except UnicodeDecodeError: + data = data.encode('base64') + tag = u'tag:yaml.org,2002:binary' + style = '|' + return self.represent_scalar(tag, data, style=style) + + def represent_unicode(self, data): + return self.represent_scalar(u'tag:yaml.org,2002:str', data) + + def represent_bool(self, data): + if data: + value = u'true' + else: + value = u'false' + return self.represent_scalar(u'tag:yaml.org,2002:bool', value) + + def represent_int(self, data): + return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data)) + + def represent_long(self, data): + return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data)) + + inf_value = 1e300 + while repr(inf_value) != repr(inf_value*inf_value): + inf_value *= inf_value + + def represent_float(self, data): + if data != data or (data == 0.0 and data == 1.0): + value = u'.nan' + elif data == self.inf_value: + value = u'.inf' + elif data == -self.inf_value: + value = u'-.inf' + else: + value = unicode(repr(data)).lower() + # Note that in some cases `repr(data)` represents a float number + # without the decimal parts. For instance: + # >>> repr(1e17) + # '1e17' + # Unfortunately, this is not a valid float representation according + # to the definition of the `!!float` tag. We fix this by adding + # '.0' before the 'e' symbol. + if u'.' not in value and u'e' in value: + value = value.replace(u'e', u'.0e', 1) + return self.represent_scalar(u'tag:yaml.org,2002:float', value) + + def represent_list(self, data): + #pairs = (len(data) > 0 and isinstance(data, list)) + #if pairs: + # for item in data: + # if not isinstance(item, tuple) or len(item) != 2: + # pairs = False + # break + #if not pairs: + return self.represent_sequence(u'tag:yaml.org,2002:seq', data) + #value = [] + #for item_key, item_value in data: + # value.append(self.represent_mapping(u'tag:yaml.org,2002:map', + # [(item_key, item_value)])) + #return SequenceNode(u'tag:yaml.org,2002:pairs', value) + + def represent_dict(self, data): + return self.represent_mapping(u'tag:yaml.org,2002:map', data) + + def represent_set(self, data): + value = {} + for key in data: + value[key] = None + return self.represent_mapping(u'tag:yaml.org,2002:set', value) + + def represent_date(self, data): + value = unicode(data.isoformat()) + return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value) + + def represent_datetime(self, data): + value = unicode(data.isoformat(' ')) + return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value) + + def represent_yaml_object(self, tag, data, cls, flow_style=None): + if hasattr(data, '__getstate__'): + state = data.__getstate__() + else: + state = data.__dict__.copy() + return self.represent_mapping(tag, state, flow_style=flow_style) + + def represent_undefined(self, data): + raise RepresenterError("cannot represent an object: %s" % data) + +SafeRepresenter.add_representer(type(None), + SafeRepresenter.represent_none) + +SafeRepresenter.add_representer(str, + SafeRepresenter.represent_str) + +SafeRepresenter.add_representer(unicode, + SafeRepresenter.represent_unicode) + +SafeRepresenter.add_representer(bool, + SafeRepresenter.represent_bool) + +SafeRepresenter.add_representer(int, + SafeRepresenter.represent_int) + +SafeRepresenter.add_representer(long, + SafeRepresenter.represent_long) + +SafeRepresenter.add_representer(float, + SafeRepresenter.represent_float) + +SafeRepresenter.add_representer(list, + SafeRepresenter.represent_list) + +SafeRepresenter.add_representer(tuple, + SafeRepresenter.represent_list) + +SafeRepresenter.add_representer(dict, + SafeRepresenter.represent_dict) + +SafeRepresenter.add_representer(set, + SafeRepresenter.represent_set) + +SafeRepresenter.add_representer(datetime.date, + SafeRepresenter.represent_date) + +SafeRepresenter.add_representer(datetime.datetime, + SafeRepresenter.represent_datetime) + +SafeRepresenter.add_representer(None, + SafeRepresenter.represent_undefined) + +class Representer(SafeRepresenter): + + def represent_str(self, data): + tag = None + style = None + try: + data = unicode(data, 'ascii') + tag = u'tag:yaml.org,2002:str' + except UnicodeDecodeError: + try: + data = unicode(data, 'utf-8') + tag = u'tag:yaml.org,2002:python/str' + except UnicodeDecodeError: + data = data.encode('base64') + tag = u'tag:yaml.org,2002:binary' + style = '|' + return self.represent_scalar(tag, data, style=style) + + def represent_unicode(self, data): + tag = None + try: + data.encode('ascii') + tag = u'tag:yaml.org,2002:python/unicode' + except UnicodeEncodeError: + tag = u'tag:yaml.org,2002:str' + return self.represent_scalar(tag, data) + + def represent_long(self, data): + tag = u'tag:yaml.org,2002:int' + if int(data) is not data: + tag = u'tag:yaml.org,2002:python/long' + return self.represent_scalar(tag, unicode(data)) + + def represent_complex(self, data): + if data.imag == 0.0: + data = u'%r' % data.real + elif data.real == 0.0: + data = u'%rj' % data.imag + elif data.imag > 0: + data = u'%r+%rj' % (data.real, data.imag) + else: + data = u'%r%rj' % (data.real, data.imag) + return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data) + + def represent_tuple(self, data): + return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data) + + def represent_name(self, data): + name = u'%s.%s' % (data.__module__, data.__name__) + return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'') + + def represent_module(self, data): + return self.represent_scalar( + u'tag:yaml.org,2002:python/module:'+data.__name__, u'') + + def represent_instance(self, data): + # For instances of classic classes, we use __getinitargs__ and + # __getstate__ to serialize the data. + + # If data.__getinitargs__ exists, the object must be reconstructed by + # calling cls(**args), where args is a tuple returned by + # __getinitargs__. Otherwise, the cls.__init__ method should never be + # called and the class instance is created by instantiating a trivial + # class and assigning to the instance's __class__ variable. + + # If data.__getstate__ exists, it returns the state of the object. + # Otherwise, the state of the object is data.__dict__. + + # We produce either a !!python/object or !!python/object/new node. + # If data.__getinitargs__ does not exist and state is a dictionary, we + # produce a !!python/object node . Otherwise we produce a + # !!python/object/new node. + + cls = data.__class__ + class_name = u'%s.%s' % (cls.__module__, cls.__name__) + args = None + state = None + if hasattr(data, '__getinitargs__'): + args = list(data.__getinitargs__()) + if hasattr(data, '__getstate__'): + state = data.__getstate__() + else: + state = data.__dict__ + if args is None and isinstance(state, dict): + return self.represent_mapping( + u'tag:yaml.org,2002:python/object:'+class_name, state) + if isinstance(state, dict) and not state: + return self.represent_sequence( + u'tag:yaml.org,2002:python/object/new:'+class_name, args) + value = {} + if args: + value['args'] = args + value['state'] = state + return self.represent_mapping( + u'tag:yaml.org,2002:python/object/new:'+class_name, value) + + def represent_object(self, data): + # We use __reduce__ API to save the data. data.__reduce__ returns + # a tuple of length 2-5: + # (function, args, state, listitems, dictitems) + + # For reconstructing, we calls function(*args), then set its state, + # listitems, and dictitems if they are not None. + + # A special case is when function.__name__ == '__newobj__'. In this + # case we create the object with args[0].__new__(*args). + + # Another special case is when __reduce__ returns a string - we don't + # support it. + + # We produce a !!python/object, !!python/object/new or + # !!python/object/apply node. + + cls = type(data) + if cls in copy_reg.dispatch_table: + reduce = copy_reg.dispatch_table[cls](data) + elif hasattr(data, '__reduce_ex__'): + reduce = data.__reduce_ex__(2) + elif hasattr(data, '__reduce__'): + reduce = data.__reduce__() + else: + raise RepresenterError("cannot represent object: %r" % data) + reduce = (list(reduce)+[None]*5)[:5] + function, args, state, listitems, dictitems = reduce + args = list(args) + if state is None: + state = {} + if listitems is not None: + listitems = list(listitems) + if dictitems is not None: + dictitems = dict(dictitems) + if function.__name__ == '__newobj__': + function = args[0] + args = args[1:] + tag = u'tag:yaml.org,2002:python/object/new:' + newobj = True + else: + tag = u'tag:yaml.org,2002:python/object/apply:' + newobj = False + function_name = u'%s.%s' % (function.__module__, function.__name__) + if not args and not listitems and not dictitems \ + and isinstance(state, dict) and newobj: + return self.represent_mapping( + u'tag:yaml.org,2002:python/object:'+function_name, state) + if not listitems and not dictitems \ + and isinstance(state, dict) and not state: + return self.represent_sequence(tag+function_name, args) + value = {} + if args: + value['args'] = args + if state or not isinstance(state, dict): + value['state'] = state + if listitems: + value['listitems'] = listitems + if dictitems: + value['dictitems'] = dictitems + return self.represent_mapping(tag+function_name, value) + +Representer.add_representer(str, + Representer.represent_str) + +Representer.add_representer(unicode, + Representer.represent_unicode) + +Representer.add_representer(long, + Representer.represent_long) + +Representer.add_representer(complex, + Representer.represent_complex) + +Representer.add_representer(tuple, + Representer.represent_tuple) + +Representer.add_representer(type, + Representer.represent_name) + +Representer.add_representer(types.ClassType, + Representer.represent_name) + +Representer.add_representer(types.FunctionType, + Representer.represent_name) + +Representer.add_representer(types.BuiltinFunctionType, + Representer.represent_name) + +Representer.add_representer(types.ModuleType, + Representer.represent_module) + +Representer.add_multi_representer(types.InstanceType, + Representer.represent_instance) + +Representer.add_multi_representer(object, + Representer.represent_object) + diff --git a/plugins_available/util/yaml/resolver.py b/plugins_available/util/yaml/resolver.py new file mode 100644 index 0000000..6b5ab87 --- /dev/null +++ b/plugins_available/util/yaml/resolver.py @@ -0,0 +1,224 @@ + +__all__ = ['BaseResolver', 'Resolver'] + +from error import * +from nodes import * + +import re + +class ResolverError(YAMLError): + pass + +class BaseResolver(object): + + DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str' + DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq' + DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map' + + yaml_implicit_resolvers = {} + yaml_path_resolvers = {} + + def __init__(self): + self.resolver_exact_paths = [] + self.resolver_prefix_paths = [] + + def add_implicit_resolver(cls, tag, regexp, first): + if not 'yaml_implicit_resolvers' in cls.__dict__: + cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() + if first is None: + first = [None] + for ch in first: + cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) + add_implicit_resolver = classmethod(add_implicit_resolver) + + def add_path_resolver(cls, tag, path, kind=None): + # Note: `add_path_resolver` is experimental. The API could be changed. + # `new_path` is a pattern that is matched against the path from the + # root to the node that is being considered. `node_path` elements are + # tuples `(node_check, index_check)`. `node_check` is a node class: + # `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None` + # matches any kind of a node. `index_check` could be `None`, a boolean + # value, a string value, or a number. `None` and `False` match against + # any _value_ of sequence and mapping nodes. `True` matches against + # any _key_ of a mapping node. A string `index_check` matches against + # a mapping value that corresponds to a scalar key which content is + # equal to the `index_check` value. An integer `index_check` matches + # against a sequence value with the index equal to `index_check`. + if not 'yaml_path_resolvers' in cls.__dict__: + cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy() + new_path = [] + for element in path: + if isinstance(element, (list, tuple)): + if len(element) == 2: + node_check, index_check = element + elif len(element) == 1: + node_check = element[0] + index_check = True + else: + raise ResolverError("Invalid path element: %s" % element) + else: + node_check = None + index_check = element + if node_check is str: + node_check = ScalarNode + elif node_check is list: + node_check = SequenceNode + elif node_check is dict: + node_check = MappingNode + elif node_check not in [ScalarNode, SequenceNode, MappingNode] \ + and not isinstance(node_check, basestring) \ + and node_check is not None: + raise ResolverError("Invalid node checker: %s" % node_check) + if not isinstance(index_check, (basestring, int)) \ + and index_check is not None: + raise ResolverError("Invalid index checker: %s" % index_check) + new_path.append((node_check, index_check)) + if kind is str: + kind = ScalarNode + elif kind is list: + kind = SequenceNode + elif kind is dict: + kind = MappingNode + elif kind not in [ScalarNode, SequenceNode, MappingNode] \ + and kind is not None: + raise ResolverError("Invalid node kind: %s" % kind) + cls.yaml_path_resolvers[tuple(new_path), kind] = tag + add_path_resolver = classmethod(add_path_resolver) + + def descend_resolver(self, current_node, current_index): + if not self.yaml_path_resolvers: + return + exact_paths = {} + prefix_paths = [] + if current_node: + depth = len(self.resolver_prefix_paths) + for path, kind in self.resolver_prefix_paths[-1]: + if self.check_resolver_prefix(depth, path, kind, + current_node, current_index): + if len(path) > depth: + prefix_paths.append((path, kind)) + else: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + for path, kind in self.yaml_path_resolvers: + if not path: + exact_paths[kind] = self.yaml_path_resolvers[path, kind] + else: + prefix_paths.append((path, kind)) + self.resolver_exact_paths.append(exact_paths) + self.resolver_prefix_paths.append(prefix_paths) + + def ascend_resolver(self): + if not self.yaml_path_resolvers: + return + self.resolver_exact_paths.pop() + self.resolver_prefix_paths.pop() + + def check_resolver_prefix(self, depth, path, kind, + current_node, current_index): + node_check, index_check = path[depth-1] + if isinstance(node_check, basestring): + if current_node.tag != node_check: + return + elif node_check is not None: + if not isinstance(current_node, node_check): + return + if index_check is True and current_index is not None: + return + if (index_check is False or index_check is None) \ + and current_index is None: + return + if isinstance(index_check, basestring): + if not (isinstance(current_index, ScalarNode) + and index_check == current_index.value): + return + elif isinstance(index_check, int) and not isinstance(index_check, bool): + if index_check != current_index: + return + return True + + def resolve(self, kind, value, implicit): + if kind is ScalarNode and implicit[0]: + if value == u'': + resolvers = self.yaml_implicit_resolvers.get(u'', []) + else: + resolvers = self.yaml_implicit_resolvers.get(value[0], []) + resolvers += self.yaml_implicit_resolvers.get(None, []) + for tag, regexp in resolvers: + if regexp.match(value): + return tag + implicit = implicit[1] + if self.yaml_path_resolvers: + exact_paths = self.resolver_exact_paths[-1] + if kind in exact_paths: + return exact_paths[kind] + if None in exact_paths: + return exact_paths[None] + if kind is ScalarNode: + return self.DEFAULT_SCALAR_TAG + elif kind is SequenceNode: + return self.DEFAULT_SEQUENCE_TAG + elif kind is MappingNode: + return self.DEFAULT_MAPPING_TAG + +class Resolver(BaseResolver): + pass + +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:bool', + re.compile(ur'''^(?:yes|Yes|YES|no|No|NO + |true|True|TRUE|false|False|FALSE + |on|On|ON|off|Off|OFF)$''', re.X), + list(u'yYnNtTfFoO')) + +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:float', + re.compile(ur'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? + |\.[0-9_]+(?:[eE][-+][0-9]+)? + |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* + |[-+]?\.(?:inf|Inf|INF) + |\.(?:nan|NaN|NAN))$''', re.X), + list(u'-+0123456789.')) + +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:int', + re.compile(ur'''^(?:[-+]?0b[0-1_]+ + |[-+]?0[0-7_]+ + |[-+]?(?:0|[1-9][0-9_]*) + |[-+]?0x[0-9a-fA-F_]+ + |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), + list(u'-+0123456789')) + +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:merge', + re.compile(ur'^(?:<<)$'), + [u'<']) + +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:null', + re.compile(ur'''^(?: ~ + |null|Null|NULL + | )$''', re.X), + [u'~', u'n', u'N', u'']) + +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:timestamp', + re.compile(ur'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] + |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]? + (?:[Tt]|[ \t]+)[0-9][0-9]? + :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)? + (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X), + list(u'0123456789')) + +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:value', + re.compile(ur'^(?:=)$'), + [u'=']) + +# The following resolver is only for documentation purposes. It cannot work +# because plain scalars cannot start with '!', '&', or '*'. +Resolver.add_implicit_resolver( + u'tag:yaml.org,2002:yaml', + re.compile(ur'^(?:!|&|\*)$'), + list(u'!&*')) + diff --git a/plugins_available/util/yaml/scanner.py b/plugins_available/util/yaml/scanner.py new file mode 100644 index 0000000..5228fad --- /dev/null +++ b/plugins_available/util/yaml/scanner.py @@ -0,0 +1,1457 @@ + +# Scanner produces tokens of the following types: +# STREAM-START +# STREAM-END +# DIRECTIVE(name, value) +# DOCUMENT-START +# DOCUMENT-END +# BLOCK-SEQUENCE-START +# BLOCK-MAPPING-START +# BLOCK-END +# FLOW-SEQUENCE-START +# FLOW-MAPPING-START +# FLOW-SEQUENCE-END +# FLOW-MAPPING-END +# BLOCK-ENTRY +# FLOW-ENTRY +# KEY +# VALUE +# ALIAS(value) +# ANCHOR(value) +# TAG(value) +# SCALAR(value, plain, style) +# +# Read comments in the Scanner code for more details. +# + +__all__ = ['Scanner', 'ScannerError'] + +from error import MarkedYAMLError +from tokens import * + +class ScannerError(MarkedYAMLError): + pass + +class SimpleKey(object): + # See below simple keys treatment. + + def __init__(self, token_number, required, index, line, column, mark): + self.token_number = token_number + self.required = required + self.index = index + self.line = line + self.column = column + self.mark = mark + +class Scanner(object): + + def __init__(self): + """Initialize the scanner.""" + # It is assumed that Scanner and Reader will have a common descendant. + # Reader do the dirty work of checking for BOM and converting the + # input data to Unicode. It also adds NUL to the end. + # + # Reader supports the following methods + # self.peek(i=0) # peek the next i-th character + # self.prefix(l=1) # peek the next l characters + # self.forward(l=1) # read the next l characters and move the pointer. + + # Had we reached the end of the stream? + self.done = False + + # The number of unclosed '{' and '['. `flow_level == 0` means block + # context. + self.flow_level = 0 + + # List of processed tokens that are not yet emitted. + self.tokens = [] + + # Add the STREAM-START token. + self.fetch_stream_start() + + # Number of tokens that were emitted through the `get_token` method. + self.tokens_taken = 0 + + # The current indentation level. + self.indent = -1 + + # Past indentation levels. + self.indents = [] + + # Variables related to simple keys treatment. + + # A simple key is a key that is not denoted by the '?' indicator. + # Example of simple keys: + # --- + # block simple key: value + # ? not a simple key: + # : { flow simple key: value } + # We emit the KEY token before all keys, so when we find a potential + # simple key, we try to locate the corresponding ':' indicator. + # Simple keys should be limited to a single line and 1024 characters. + + # Can a simple key start at the current position? A simple key may + # start: + # - at the beginning of the line, not counting indentation spaces + # (in block context), + # - after '{', '[', ',' (in the flow context), + # - after '?', ':', '-' (in the block context). + # In the block context, this flag also signifies if a block collection + # may start at the current position. + self.allow_simple_key = True + + # Keep track of possible simple keys. This is a dictionary. The key + # is `flow_level`; there can be no more that one possible simple key + # for each level. The value is a SimpleKey record: + # (token_number, required, index, line, column, mark) + # A simple key may start with ALIAS, ANCHOR, TAG, SCALAR(flow), + # '[', or '{' tokens. + self.possible_simple_keys = {} + + # Public methods. + + def check_token(self, *choices): + # Check if the next token is one of the given types. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + if not choices: + return True + for choice in choices: + if isinstance(self.tokens[0], choice): + return True + return False + + def peek_token(self): + # Return the next token, but do not delete if from the queue. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + return self.tokens[0] + + def get_token(self): + # Return the next token. + while self.need_more_tokens(): + self.fetch_more_tokens() + if self.tokens: + self.tokens_taken += 1 + return self.tokens.pop(0) + + # Private methods. + + def need_more_tokens(self): + if self.done: + return False + if not self.tokens: + return True + # The current token may be a potential simple key, so we + # need to look further. + self.stale_possible_simple_keys() + if self.next_possible_simple_key() == self.tokens_taken: + return True + + def fetch_more_tokens(self): + + # Eat whitespaces and comments until we reach the next token. + self.scan_to_next_token() + + # Remove obsolete possible simple keys. + self.stale_possible_simple_keys() + + # Compare the current indentation and column. It may add some tokens + # and decrease the current indentation level. + self.unwind_indent(self.column) + + # Peek the next character. + ch = self.peek() + + # Is it the end of stream? + if ch == u'\0': + return self.fetch_stream_end() + + # Is it a directive? + if ch == u'%' and self.check_directive(): + return self.fetch_directive() + + # Is it the document start? + if ch == u'-' and self.check_document_start(): + return self.fetch_document_start() + + # Is it the document end? + if ch == u'.' and self.check_document_end(): + return self.fetch_document_end() + + # TODO: support for BOM within a stream. + #if ch == u'\uFEFF': + # return self.fetch_bom() <-- issue BOMToken + + # Note: the order of the following checks is NOT significant. + + # Is it the flow sequence start indicator? + if ch == u'[': + return self.fetch_flow_sequence_start() + + # Is it the flow mapping start indicator? + if ch == u'{': + return self.fetch_flow_mapping_start() + + # Is it the flow sequence end indicator? + if ch == u']': + return self.fetch_flow_sequence_end() + + # Is it the flow mapping end indicator? + if ch == u'}': + return self.fetch_flow_mapping_end() + + # Is it the flow entry indicator? + if ch == u',': + return self.fetch_flow_entry() + + # Is it the block entry indicator? + if ch == u'-' and self.check_block_entry(): + return self.fetch_block_entry() + + # Is it the key indicator? + if ch == u'?' and self.check_key(): + return self.fetch_key() + + # Is it the value indicator? + if ch == u':' and self.check_value(): + return self.fetch_value() + + # Is it an alias? + if ch == u'*': + return self.fetch_alias() + + # Is it an anchor? + if ch == u'&': + return self.fetch_anchor() + + # Is it a tag? + if ch == u'!': + return self.fetch_tag() + + # Is it a literal scalar? + if ch == u'|' and not self.flow_level: + return self.fetch_literal() + + # Is it a folded scalar? + if ch == u'>' and not self.flow_level: + return self.fetch_folded() + + # Is it a single quoted scalar? + if ch == u'\'': + return self.fetch_single() + + # Is it a double quoted scalar? + if ch == u'\"': + return self.fetch_double() + + # It must be a plain scalar then. + if self.check_plain(): + return self.fetch_plain() + + # No? It's an error. Let's produce a nice error message. + raise ScannerError("while scanning for the next token", None, + "found character %r that cannot start any token" + % ch.encode('utf-8'), self.get_mark()) + + # Simple keys treatment. + + def next_possible_simple_key(self): + # Return the number of the nearest possible simple key. Actually we + # don't need to loop through the whole dictionary. We may replace it + # with the following code: + # if not self.possible_simple_keys: + # return None + # return self.possible_simple_keys[ + # min(self.possible_simple_keys.keys())].token_number + min_token_number = None + for level in self.possible_simple_keys: + key = self.possible_simple_keys[level] + if min_token_number is None or key.token_number < min_token_number: + min_token_number = key.token_number + return min_token_number + + def stale_possible_simple_keys(self): + # Remove entries that are no longer possible simple keys. According to + # the YAML specification, simple keys + # - should be limited to a single line, + # - should be no longer than 1024 characters. + # Disabling this procedure will allow simple keys of any length and + # height (may cause problems if indentation is broken though). + for level in self.possible_simple_keys.keys(): + key = self.possible_simple_keys[level] + if key.line != self.line \ + or self.index-key.index > 1024: + if key.required: + raise ScannerError("while scanning a simple key", key.mark, + "could not found expected ':'", self.get_mark()) + del self.possible_simple_keys[level] + + def save_possible_simple_key(self): + # The next token may start a simple key. We check if it's possible + # and save its position. This function is called for + # ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'. + + # Check if a simple key is required at the current position. + required = not self.flow_level and self.indent == self.column + + # A simple key is required only if it is the first token in the current + # line. Therefore it is always allowed. + assert self.allow_simple_key or not required + + # The next token might be a simple key. Let's save it's number and + # position. + if self.allow_simple_key: + self.remove_possible_simple_key() + token_number = self.tokens_taken+len(self.tokens) + key = SimpleKey(token_number, required, + self.index, self.line, self.column, self.get_mark()) + self.possible_simple_keys[self.flow_level] = key + + def remove_possible_simple_key(self): + # Remove the saved possible key position at the current flow level. + if self.flow_level in self.possible_simple_keys: + key = self.possible_simple_keys[self.flow_level] + + if key.required: + raise ScannerError("while scanning a simple key", key.mark, + "could not found expected ':'", self.get_mark()) + + del self.possible_simple_keys[self.flow_level] + + # Indentation functions. + + def unwind_indent(self, column): + + ## In flow context, tokens should respect indentation. + ## Actually the condition should be `self.indent >= column` according to + ## the spec. But this condition will prohibit intuitively correct + ## constructions such as + ## key : { + ## } + #if self.flow_level and self.indent > column: + # raise ScannerError(None, None, + # "invalid intendation or unclosed '[' or '{'", + # self.get_mark()) + + # In the flow context, indentation is ignored. We make the scanner less + # restrictive then specification requires. + if self.flow_level: + return + + # In block context, we may need to issue the BLOCK-END tokens. + while self.indent > column: + mark = self.get_mark() + self.indent = self.indents.pop() + self.tokens.append(BlockEndToken(mark, mark)) + + def add_indent(self, column): + # Check if we need to increase indentation. + if self.indent < column: + self.indents.append(self.indent) + self.indent = column + return True + return False + + # Fetchers. + + def fetch_stream_start(self): + # We always add STREAM-START as the first token and STREAM-END as the + # last token. + + # Read the token. + mark = self.get_mark() + + # Add STREAM-START. + self.tokens.append(StreamStartToken(mark, mark, + encoding=self.encoding)) + + + def fetch_stream_end(self): + + # Set the current intendation to -1. + self.unwind_indent(-1) + + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + self.possible_simple_keys = {} + + # Read the token. + mark = self.get_mark() + + # Add STREAM-END. + self.tokens.append(StreamEndToken(mark, mark)) + + # The steam is finished. + self.done = True + + def fetch_directive(self): + + # Set the current intendation to -1. + self.unwind_indent(-1) + + # Reset simple keys. + self.remove_possible_simple_key() + self.allow_simple_key = False + + # Scan and add DIRECTIVE. + self.tokens.append(self.scan_directive()) + + def fetch_document_start(self): + self.fetch_document_indicator(DocumentStartToken) + + def fetch_document_end(self): + self.fetch_document_indicator(DocumentEndToken) + + def fetch_document_indicator(self, TokenClass): + + # Set the current intendation to -1. + self.unwind_indent(-1) + + # Reset simple keys. Note that there could not be a block collection + # after '---'. + self.remove_possible_simple_key() + self.allow_simple_key = False + + # Add DOCUMENT-START or DOCUMENT-END. + start_mark = self.get_mark() + self.forward(3) + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_start(self): + self.fetch_flow_collection_start(FlowSequenceStartToken) + + def fetch_flow_mapping_start(self): + self.fetch_flow_collection_start(FlowMappingStartToken) + + def fetch_flow_collection_start(self, TokenClass): + + # '[' and '{' may start a simple key. + self.save_possible_simple_key() + + # Increase the flow level. + self.flow_level += 1 + + # Simple keys are allowed after '[' and '{'. + self.allow_simple_key = True + + # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_sequence_end(self): + self.fetch_flow_collection_end(FlowSequenceEndToken) + + def fetch_flow_mapping_end(self): + self.fetch_flow_collection_end(FlowMappingEndToken) + + def fetch_flow_collection_end(self, TokenClass): + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Decrease the flow level. + self.flow_level -= 1 + + # No simple keys after ']' or '}'. + self.allow_simple_key = False + + # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(TokenClass(start_mark, end_mark)) + + def fetch_flow_entry(self): + + # Simple keys are allowed after ','. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add FLOW-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(FlowEntryToken(start_mark, end_mark)) + + def fetch_block_entry(self): + + # Block context needs additional checks. + if not self.flow_level: + + # Are we allowed to start a new entry? + if not self.allow_simple_key: + raise ScannerError(None, None, + "sequence entries are not allowed here", + self.get_mark()) + + # We may need to add BLOCK-SEQUENCE-START. + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockSequenceStartToken(mark, mark)) + + # It's an error for the block entry to occur in the flow context, + # but we let the parser detect this. + else: + pass + + # Simple keys are allowed after '-'. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add BLOCK-ENTRY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(BlockEntryToken(start_mark, end_mark)) + + def fetch_key(self): + + # Block context needs additional checks. + if not self.flow_level: + + # Are we allowed to start a key (not nessesary a simple)? + if not self.allow_simple_key: + raise ScannerError(None, None, + "mapping keys are not allowed here", + self.get_mark()) + + # We may need to add BLOCK-MAPPING-START. + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockMappingStartToken(mark, mark)) + + # Simple keys are allowed after '?' in the block context. + self.allow_simple_key = not self.flow_level + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add KEY. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(KeyToken(start_mark, end_mark)) + + def fetch_value(self): + + # Do we determine a simple key? + if self.flow_level in self.possible_simple_keys: + + # Add KEY. + key = self.possible_simple_keys[self.flow_level] + del self.possible_simple_keys[self.flow_level] + self.tokens.insert(key.token_number-self.tokens_taken, + KeyToken(key.mark, key.mark)) + + # If this key starts a new block mapping, we need to add + # BLOCK-MAPPING-START. + if not self.flow_level: + if self.add_indent(key.column): + self.tokens.insert(key.token_number-self.tokens_taken, + BlockMappingStartToken(key.mark, key.mark)) + + # There cannot be two simple keys one after another. + self.allow_simple_key = False + + # It must be a part of a complex key. + else: + + # Block context needs additional checks. + # (Do we really need them? They will be catched by the parser + # anyway.) + if not self.flow_level: + + # We are allowed to start a complex value if and only if + # we can start a simple key. + if not self.allow_simple_key: + raise ScannerError(None, None, + "mapping values are not allowed here", + self.get_mark()) + + # If this value starts a new block mapping, we need to add + # BLOCK-MAPPING-START. It will be detected as an error later by + # the parser. + if not self.flow_level: + if self.add_indent(self.column): + mark = self.get_mark() + self.tokens.append(BlockMappingStartToken(mark, mark)) + + # Simple keys are allowed after ':' in the block context. + self.allow_simple_key = not self.flow_level + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Add VALUE. + start_mark = self.get_mark() + self.forward() + end_mark = self.get_mark() + self.tokens.append(ValueToken(start_mark, end_mark)) + + def fetch_alias(self): + + # ALIAS could be a simple key. + self.save_possible_simple_key() + + # No simple keys after ALIAS. + self.allow_simple_key = False + + # Scan and add ALIAS. + self.tokens.append(self.scan_anchor(AliasToken)) + + def fetch_anchor(self): + + # ANCHOR could start a simple key. + self.save_possible_simple_key() + + # No simple keys after ANCHOR. + self.allow_simple_key = False + + # Scan and add ANCHOR. + self.tokens.append(self.scan_anchor(AnchorToken)) + + def fetch_tag(self): + + # TAG could start a simple key. + self.save_possible_simple_key() + + # No simple keys after TAG. + self.allow_simple_key = False + + # Scan and add TAG. + self.tokens.append(self.scan_tag()) + + def fetch_literal(self): + self.fetch_block_scalar(style='|') + + def fetch_folded(self): + self.fetch_block_scalar(style='>') + + def fetch_block_scalar(self, style): + + # A simple key may follow a block scalar. + self.allow_simple_key = True + + # Reset possible simple key on the current level. + self.remove_possible_simple_key() + + # Scan and add SCALAR. + self.tokens.append(self.scan_block_scalar(style)) + + def fetch_single(self): + self.fetch_flow_scalar(style='\'') + + def fetch_double(self): + self.fetch_flow_scalar(style='"') + + def fetch_flow_scalar(self, style): + + # A flow scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after flow scalars. + self.allow_simple_key = False + + # Scan and add SCALAR. + self.tokens.append(self.scan_flow_scalar(style)) + + def fetch_plain(self): + + # A plain scalar could be a simple key. + self.save_possible_simple_key() + + # No simple keys after plain scalars. But note that `scan_plain` will + # change this flag if the scan is finished at the beginning of the + # line. + self.allow_simple_key = False + + # Scan and add SCALAR. May change `allow_simple_key`. + self.tokens.append(self.scan_plain()) + + # Checkers. + + def check_directive(self): + + # DIRECTIVE: ^ '%' ... + # The '%' indicator is already checked. + if self.column == 0: + return True + + def check_document_start(self): + + # DOCUMENT-START: ^ '---' (' '|'\n') + if self.column == 0: + if self.prefix(3) == u'---' \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return True + + def check_document_end(self): + + # DOCUMENT-END: ^ '...' (' '|'\n') + if self.column == 0: + if self.prefix(3) == u'...' \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return True + + def check_block_entry(self): + + # BLOCK-ENTRY: '-' (' '|'\n') + return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' + + def check_key(self): + + # KEY(flow context): '?' + if self.flow_level: + return True + + # KEY(block context): '?' (' '|'\n') + else: + return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' + + def check_value(self): + + # VALUE(flow context): ':' + if self.flow_level: + return True + + # VALUE(block context): ':' (' '|'\n') + else: + return self.peek(1) in u'\0 \t\r\n\x85\u2028\u2029' + + def check_plain(self): + + # A plain scalar may start with any non-space character except: + # '-', '?', ':', ',', '[', ']', '{', '}', + # '#', '&', '*', '!', '|', '>', '\'', '\"', + # '%', '@', '`'. + # + # It may also start with + # '-', '?', ':' + # if it is followed by a non-space character. + # + # Note that we limit the last rule to the block context (except the + # '-' character) because we want the flow context to be space + # independent. + ch = self.peek() + return ch not in u'\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ + or (self.peek(1) not in u'\0 \t\r\n\x85\u2028\u2029' + and (ch == u'-' or (not self.flow_level and ch in u'?:'))) + + # Scanners. + + def scan_to_next_token(self): + # We ignore spaces, line breaks and comments. + # If we find a line break in the block context, we set the flag + # `allow_simple_key` on. + # The byte order mark is stripped if it's the first character in the + # stream. We do not yet support BOM inside the stream as the + # specification requires. Any such mark will be considered as a part + # of the document. + # + # TODO: We need to make tab handling rules more sane. A good rule is + # Tabs cannot precede tokens + # BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END, + # KEY(block), VALUE(block), BLOCK-ENTRY + # So the checking code is + # if : + # self.allow_simple_keys = False + # We also need to add the check for `allow_simple_keys == True` to + # `unwind_indent` before issuing BLOCK-END. + # Scanners for block, flow, and plain scalars need to be modified. + + if self.index == 0 and self.peek() == u'\uFEFF': + self.forward() + found = False + while not found: + while self.peek() == u' ': + self.forward() + if self.peek() == u'#': + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + if self.scan_line_break(): + if not self.flow_level: + self.allow_simple_key = True + else: + found = True + + def scan_directive(self): + # See the specification for details. + start_mark = self.get_mark() + self.forward() + name = self.scan_directive_name(start_mark) + value = None + if name == u'YAML': + value = self.scan_yaml_directive_value(start_mark) + end_mark = self.get_mark() + elif name == u'TAG': + value = self.scan_tag_directive_value(start_mark) + end_mark = self.get_mark() + else: + end_mark = self.get_mark() + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + self.scan_directive_ignored_line(start_mark) + return DirectiveToken(name, value, start_mark, end_mark) + + def scan_directive_name(self, start_mark): + # See the specification for details. + length = 0 + ch = self.peek(length) + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_': + length += 1 + ch = self.peek(length) + if not length: + raise ScannerError("while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + value = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + return value + + def scan_yaml_directive_value(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + major = self.scan_yaml_directive_number(start_mark) + if self.peek() != '.': + raise ScannerError("while scanning a directive", start_mark, + "expected a digit or '.', but found %r" + % self.peek().encode('utf-8'), + self.get_mark()) + self.forward() + minor = self.scan_yaml_directive_number(start_mark) + if self.peek() not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected a digit or ' ', but found %r" + % self.peek().encode('utf-8'), + self.get_mark()) + return (major, minor) + + def scan_yaml_directive_number(self, start_mark): + # See the specification for details. + ch = self.peek() + if not (u'0' <= ch <= u'9'): + raise ScannerError("while scanning a directive", start_mark, + "expected a digit, but found %r" % ch.encode('utf-8'), + self.get_mark()) + length = 0 + while u'0' <= self.peek(length) <= u'9': + length += 1 + value = int(self.prefix(length)) + self.forward(length) + return value + + def scan_tag_directive_value(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + handle = self.scan_tag_directive_handle(start_mark) + while self.peek() == u' ': + self.forward() + prefix = self.scan_tag_directive_prefix(start_mark) + return (handle, prefix) + + def scan_tag_directive_handle(self, start_mark): + # See the specification for details. + value = self.scan_tag_handle('directive', start_mark) + ch = self.peek() + if ch != u' ': + raise ScannerError("while scanning a directive", start_mark, + "expected ' ', but found %r" % ch.encode('utf-8'), + self.get_mark()) + return value + + def scan_tag_directive_prefix(self, start_mark): + # See the specification for details. + value = self.scan_tag_uri('directive', start_mark) + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected ' ', but found %r" % ch.encode('utf-8'), + self.get_mark()) + return value + + def scan_directive_ignored_line(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + if self.peek() == u'#': + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + ch = self.peek() + if ch not in u'\0\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a directive", start_mark, + "expected a comment or a line break, but found %r" + % ch.encode('utf-8'), self.get_mark()) + self.scan_line_break() + + def scan_anchor(self, TokenClass): + # The specification does not restrict characters for anchors and + # aliases. This may lead to problems, for instance, the document: + # [ *alias, value ] + # can be interpteted in two ways, as + # [ "value" ] + # and + # [ *alias , "value" ] + # Therefore we restrict aliases to numbers and ASCII letters. + start_mark = self.get_mark() + indicator = self.peek() + if indicator == u'*': + name = 'alias' + else: + name = 'anchor' + self.forward() + length = 0 + ch = self.peek(length) + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_': + length += 1 + ch = self.peek(length) + if not length: + raise ScannerError("while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + value = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch not in u'\0 \t\r\n\x85\u2028\u2029?:,]}%@`': + raise ScannerError("while scanning an %s" % name, start_mark, + "expected alphabetic or numeric character, but found %r" + % ch.encode('utf-8'), self.get_mark()) + end_mark = self.get_mark() + return TokenClass(value, start_mark, end_mark) + + def scan_tag(self): + # See the specification for details. + start_mark = self.get_mark() + ch = self.peek(1) + if ch == u'<': + handle = None + self.forward(2) + suffix = self.scan_tag_uri('tag', start_mark) + if self.peek() != u'>': + raise ScannerError("while parsing a tag", start_mark, + "expected '>', but found %r" % self.peek().encode('utf-8'), + self.get_mark()) + self.forward() + elif ch in u'\0 \t\r\n\x85\u2028\u2029': + handle = None + suffix = u'!' + self.forward() + else: + length = 1 + use_handle = False + while ch not in u'\0 \r\n\x85\u2028\u2029': + if ch == u'!': + use_handle = True + break + length += 1 + ch = self.peek(length) + handle = u'!' + if use_handle: + handle = self.scan_tag_handle('tag', start_mark) + else: + handle = u'!' + self.forward() + suffix = self.scan_tag_uri('tag', start_mark) + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a tag", start_mark, + "expected ' ', but found %r" % ch.encode('utf-8'), + self.get_mark()) + value = (handle, suffix) + end_mark = self.get_mark() + return TagToken(value, start_mark, end_mark) + + def scan_block_scalar(self, style): + # See the specification for details. + + if style == '>': + folded = True + else: + folded = False + + chunks = [] + start_mark = self.get_mark() + + # Scan the header. + self.forward() + chomping, increment = self.scan_block_scalar_indicators(start_mark) + self.scan_block_scalar_ignored_line(start_mark) + + # Determine the indentation level and go to the first non-empty line. + min_indent = self.indent+1 + if min_indent < 1: + min_indent = 1 + if increment is None: + breaks, max_indent, end_mark = self.scan_block_scalar_indentation() + indent = max(min_indent, max_indent) + else: + indent = min_indent+increment-1 + breaks, end_mark = self.scan_block_scalar_breaks(indent) + line_break = u'' + + # Scan the inner part of the block scalar. + while self.column == indent and self.peek() != u'\0': + chunks.extend(breaks) + leading_non_space = self.peek() not in u' \t' + length = 0 + while self.peek(length) not in u'\0\r\n\x85\u2028\u2029': + length += 1 + chunks.append(self.prefix(length)) + self.forward(length) + line_break = self.scan_line_break() + breaks, end_mark = self.scan_block_scalar_breaks(indent) + if self.column == indent and self.peek() != u'\0': + + # Unfortunately, folding rules are ambiguous. + # + # This is the folding according to the specification: + + if folded and line_break == u'\n' \ + and leading_non_space and self.peek() not in u' \t': + if not breaks: + chunks.append(u' ') + else: + chunks.append(line_break) + + # This is Clark Evans's interpretation (also in the spec + # examples): + # + #if folded and line_break == u'\n': + # if not breaks: + # if self.peek() not in ' \t': + # chunks.append(u' ') + # else: + # chunks.append(line_break) + #else: + # chunks.append(line_break) + else: + break + + # Chomp the tail. + if chomping is not False: + chunks.append(line_break) + if chomping is True: + chunks.extend(breaks) + + # We are done. + return ScalarToken(u''.join(chunks), False, start_mark, end_mark, + style) + + def scan_block_scalar_indicators(self, start_mark): + # See the specification for details. + chomping = None + increment = None + ch = self.peek() + if ch in u'+-': + if ch == '+': + chomping = True + else: + chomping = False + self.forward() + ch = self.peek() + if ch in u'0123456789': + increment = int(ch) + if increment == 0: + raise ScannerError("while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark()) + self.forward() + elif ch in u'0123456789': + increment = int(ch) + if increment == 0: + raise ScannerError("while scanning a block scalar", start_mark, + "expected indentation indicator in the range 1-9, but found 0", + self.get_mark()) + self.forward() + ch = self.peek() + if ch in u'+-': + if ch == '+': + chomping = True + else: + chomping = False + self.forward() + ch = self.peek() + if ch not in u'\0 \r\n\x85\u2028\u2029': + raise ScannerError("while scanning a block scalar", start_mark, + "expected chomping or indentation indicators, but found %r" + % ch.encode('utf-8'), self.get_mark()) + return chomping, increment + + def scan_block_scalar_ignored_line(self, start_mark): + # See the specification for details. + while self.peek() == u' ': + self.forward() + if self.peek() == u'#': + while self.peek() not in u'\0\r\n\x85\u2028\u2029': + self.forward() + ch = self.peek() + if ch not in u'\0\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a block scalar", start_mark, + "expected a comment or a line break, but found %r" + % ch.encode('utf-8'), self.get_mark()) + self.scan_line_break() + + def scan_block_scalar_indentation(self): + # See the specification for details. + chunks = [] + max_indent = 0 + end_mark = self.get_mark() + while self.peek() in u' \r\n\x85\u2028\u2029': + if self.peek() != u' ': + chunks.append(self.scan_line_break()) + end_mark = self.get_mark() + else: + self.forward() + if self.column > max_indent: + max_indent = self.column + return chunks, max_indent, end_mark + + def scan_block_scalar_breaks(self, indent): + # See the specification for details. + chunks = [] + end_mark = self.get_mark() + while self.column < indent and self.peek() == u' ': + self.forward() + while self.peek() in u'\r\n\x85\u2028\u2029': + chunks.append(self.scan_line_break()) + end_mark = self.get_mark() + while self.column < indent and self.peek() == u' ': + self.forward() + return chunks, end_mark + + def scan_flow_scalar(self, style): + # See the specification for details. + # Note that we loose indentation rules for quoted scalars. Quoted + # scalars don't need to adhere indentation because " and ' clearly + # mark the beginning and the end of them. Therefore we are less + # restrictive then the specification requires. We only need to check + # that document separators are not included in scalars. + if style == '"': + double = True + else: + double = False + chunks = [] + start_mark = self.get_mark() + quote = self.peek() + self.forward() + chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) + while self.peek() != quote: + chunks.extend(self.scan_flow_scalar_spaces(double, start_mark)) + chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) + self.forward() + end_mark = self.get_mark() + return ScalarToken(u''.join(chunks), False, start_mark, end_mark, + style) + + ESCAPE_REPLACEMENTS = { + u'0': u'\0', + u'a': u'\x07', + u'b': u'\x08', + u't': u'\x09', + u'\t': u'\x09', + u'n': u'\x0A', + u'v': u'\x0B', + u'f': u'\x0C', + u'r': u'\x0D', + u'e': u'\x1B', + u' ': u'\x20', + u'\"': u'\"', + u'\\': u'\\', + u'N': u'\x85', + u'_': u'\xA0', + u'L': u'\u2028', + u'P': u'\u2029', + } + + ESCAPE_CODES = { + u'x': 2, + u'u': 4, + u'U': 8, + } + + def scan_flow_scalar_non_spaces(self, double, start_mark): + # See the specification for details. + chunks = [] + while True: + length = 0 + while self.peek(length) not in u'\'\"\\\0 \t\r\n\x85\u2028\u2029': + length += 1 + if length: + chunks.append(self.prefix(length)) + self.forward(length) + ch = self.peek() + if not double and ch == u'\'' and self.peek(1) == u'\'': + chunks.append(u'\'') + self.forward(2) + elif (double and ch == u'\'') or (not double and ch in u'\"\\'): + chunks.append(ch) + self.forward() + elif double and ch == u'\\': + self.forward() + ch = self.peek() + if ch in self.ESCAPE_REPLACEMENTS: + chunks.append(self.ESCAPE_REPLACEMENTS[ch]) + self.forward() + elif ch in self.ESCAPE_CODES: + length = self.ESCAPE_CODES[ch] + self.forward() + for k in range(length): + if self.peek(k) not in u'0123456789ABCDEFabcdef': + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "expected escape sequence of %d hexdecimal numbers, but found %r" % + (length, self.peek(k).encode('utf-8')), self.get_mark()) + code = int(self.prefix(length), 16) + chunks.append(unichr(code)) + self.forward(length) + elif ch in u'\r\n\x85\u2028\u2029': + self.scan_line_break() + chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) + else: + raise ScannerError("while scanning a double-quoted scalar", start_mark, + "found unknown escape character %r" % ch.encode('utf-8'), self.get_mark()) + else: + return chunks + + def scan_flow_scalar_spaces(self, double, start_mark): + # See the specification for details. + chunks = [] + length = 0 + while self.peek(length) in u' \t': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch == u'\0': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected end of stream", self.get_mark()) + elif ch in u'\r\n\x85\u2028\u2029': + line_break = self.scan_line_break() + breaks = self.scan_flow_scalar_breaks(double, start_mark) + if line_break != u'\n': + chunks.append(line_break) + elif not breaks: + chunks.append(u' ') + chunks.extend(breaks) + else: + chunks.append(whitespaces) + return chunks + + def scan_flow_scalar_breaks(self, double, start_mark): + # See the specification for details. + chunks = [] + while True: + # Instead of checking indentation, we check for document + # separators. + prefix = self.prefix(3) + if (prefix == u'---' or prefix == u'...') \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + raise ScannerError("while scanning a quoted scalar", start_mark, + "found unexpected document separator", self.get_mark()) + while self.peek() in u' \t': + self.forward() + if self.peek() in u'\r\n\x85\u2028\u2029': + chunks.append(self.scan_line_break()) + else: + return chunks + + def scan_plain(self): + # See the specification for details. + # We add an additional restriction for the flow context: + # plain scalars in the flow context cannot contain ',', ':' and '?'. + # We also keep track of the `allow_simple_key` flag here. + # Indentation rules are loosed for the flow context. + chunks = [] + start_mark = self.get_mark() + end_mark = start_mark + indent = self.indent+1 + # We allow zero indentation for scalars, but then we need to check for + # document separators at the beginning of the line. + #if indent == 0: + # indent = 1 + spaces = [] + while True: + length = 0 + if self.peek() == u'#': + break + while True: + ch = self.peek(length) + if ch in u'\0 \t\r\n\x85\u2028\u2029' \ + or (not self.flow_level and ch == u':' and + self.peek(length+1) in u'\0 \t\r\n\x85\u2028\u2029') \ + or (self.flow_level and ch in u',:?[]{}'): + break + length += 1 + # It's not clear what we should do with ':' in the flow context. + if (self.flow_level and ch == u':' + and self.peek(length+1) not in u'\0 \t\r\n\x85\u2028\u2029,[]{}'): + self.forward(length) + raise ScannerError("while scanning a plain scalar", start_mark, + "found unexpected ':'", self.get_mark(), + "Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.") + if length == 0: + break + self.allow_simple_key = False + chunks.extend(spaces) + chunks.append(self.prefix(length)) + self.forward(length) + end_mark = self.get_mark() + spaces = self.scan_plain_spaces(indent, start_mark) + if not spaces or self.peek() == u'#' \ + or (not self.flow_level and self.column < indent): + break + return ScalarToken(u''.join(chunks), True, start_mark, end_mark) + + def scan_plain_spaces(self, indent, start_mark): + # See the specification for details. + # The specification is really confusing about tabs in plain scalars. + # We just forbid them completely. Do not use tabs in YAML! + chunks = [] + length = 0 + while self.peek(length) in u' ': + length += 1 + whitespaces = self.prefix(length) + self.forward(length) + ch = self.peek() + if ch in u'\r\n\x85\u2028\u2029': + line_break = self.scan_line_break() + self.allow_simple_key = True + prefix = self.prefix(3) + if (prefix == u'---' or prefix == u'...') \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return + breaks = [] + while self.peek() in u' \r\n\x85\u2028\u2029': + if self.peek() == ' ': + self.forward() + else: + breaks.append(self.scan_line_break()) + prefix = self.prefix(3) + if (prefix == u'---' or prefix == u'...') \ + and self.peek(3) in u'\0 \t\r\n\x85\u2028\u2029': + return + if line_break != u'\n': + chunks.append(line_break) + elif not breaks: + chunks.append(u' ') + chunks.extend(breaks) + elif whitespaces: + chunks.append(whitespaces) + return chunks + + def scan_tag_handle(self, name, start_mark): + # See the specification for details. + # For some strange reasons, the specification does not allow '_' in + # tag handles. I have allowed it anyway. + ch = self.peek() + if ch != u'!': + raise ScannerError("while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch.encode('utf-8'), + self.get_mark()) + length = 1 + ch = self.peek(length) + if ch != u' ': + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-_': + length += 1 + ch = self.peek(length) + if ch != u'!': + self.forward(length) + raise ScannerError("while scanning a %s" % name, start_mark, + "expected '!', but found %r" % ch.encode('utf-8'), + self.get_mark()) + length += 1 + value = self.prefix(length) + self.forward(length) + return value + + def scan_tag_uri(self, name, start_mark): + # See the specification for details. + # Note: we do not check if URI is well-formed. + chunks = [] + length = 0 + ch = self.peek(length) + while u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \ + or ch in u'-;/?:@&=+$,_.!~*\'()[]%': + if ch == u'%': + chunks.append(self.prefix(length)) + self.forward(length) + length = 0 + chunks.append(self.scan_uri_escapes(name, start_mark)) + else: + length += 1 + ch = self.peek(length) + if length: + chunks.append(self.prefix(length)) + self.forward(length) + length = 0 + if not chunks: + raise ScannerError("while parsing a %s" % name, start_mark, + "expected URI, but found %r" % ch.encode('utf-8'), + self.get_mark()) + return u''.join(chunks) + + def scan_uri_escapes(self, name, start_mark): + # See the specification for details. + bytes = [] + mark = self.get_mark() + while self.peek() == u'%': + self.forward() + for k in range(2): + if self.peek(k) not in u'0123456789ABCDEFabcdef': + raise ScannerError("while scanning a %s" % name, start_mark, + "expected URI escape sequence of 2 hexdecimal numbers, but found %r" % + (self.peek(k).encode('utf-8')), self.get_mark()) + bytes.append(chr(int(self.prefix(2), 16))) + self.forward(2) + try: + value = unicode(''.join(bytes), 'utf-8') + except UnicodeDecodeError, exc: + raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark) + return value + + def scan_line_break(self): + # Transforms: + # '\r\n' : '\n' + # '\r' : '\n' + # '\n' : '\n' + # '\x85' : '\n' + # '\u2028' : '\u2028' + # '\u2029 : '\u2029' + # default : '' + ch = self.peek() + if ch in u'\r\n\x85': + if self.prefix(2) == u'\r\n': + self.forward(2) + else: + self.forward() + return u'\n' + elif ch in u'\u2028\u2029': + self.forward() + return ch + return u'' + +#try: +# import psyco +# psyco.bind(Scanner) +#except ImportError: +# pass + diff --git a/plugins_available/util/yaml/serializer.py b/plugins_available/util/yaml/serializer.py new file mode 100644 index 0000000..0bf1e96 --- /dev/null +++ b/plugins_available/util/yaml/serializer.py @@ -0,0 +1,111 @@ + +__all__ = ['Serializer', 'SerializerError'] + +from error import YAMLError +from events import * +from nodes import * + +class SerializerError(YAMLError): + pass + +class Serializer(object): + + ANCHOR_TEMPLATE = u'id%03d' + + def __init__(self, encoding=None, + explicit_start=None, explicit_end=None, version=None, tags=None): + self.use_encoding = encoding + self.use_explicit_start = explicit_start + self.use_explicit_end = explicit_end + self.use_version = version + self.use_tags = tags + self.serialized_nodes = {} + self.anchors = {} + self.last_anchor_id = 0 + self.closed = None + + def open(self): + if self.closed is None: + self.emit(StreamStartEvent(encoding=self.use_encoding)) + self.closed = False + elif self.closed: + raise SerializerError("serializer is closed") + else: + raise SerializerError("serializer is already opened") + + def close(self): + if self.closed is None: + raise SerializerError("serializer is not opened") + elif not self.closed: + self.emit(StreamEndEvent()) + self.closed = True + + #def __del__(self): + # self.close() + + def serialize(self, node): + if self.closed is None: + raise SerializerError("serializer is not opened") + elif self.closed: + raise SerializerError("serializer is closed") + self.emit(DocumentStartEvent(explicit=self.use_explicit_start, + version=self.use_version, tags=self.use_tags)) + self.anchor_node(node) + self.serialize_node(node, None, None) + self.emit(DocumentEndEvent(explicit=self.use_explicit_end)) + self.serialized_nodes = {} + self.anchors = {} + self.last_anchor_id = 0 + + def anchor_node(self, node): + if node in self.anchors: + if self.anchors[node] is None: + self.anchors[node] = self.generate_anchor(node) + else: + self.anchors[node] = None + if isinstance(node, SequenceNode): + for item in node.value: + self.anchor_node(item) + elif isinstance(node, MappingNode): + for key, value in node.value: + self.anchor_node(key) + self.anchor_node(value) + + def generate_anchor(self, node): + self.last_anchor_id += 1 + return self.ANCHOR_TEMPLATE % self.last_anchor_id + + def serialize_node(self, node, parent, index): + alias = self.anchors[node] + if node in self.serialized_nodes: + self.emit(AliasEvent(alias)) + else: + self.serialized_nodes[node] = True + self.descend_resolver(parent, index) + if isinstance(node, ScalarNode): + detected_tag = self.resolve(ScalarNode, node.value, (True, False)) + default_tag = self.resolve(ScalarNode, node.value, (False, True)) + implicit = (node.tag == detected_tag), (node.tag == default_tag) + self.emit(ScalarEvent(alias, node.tag, implicit, node.value, + style=node.style)) + elif isinstance(node, SequenceNode): + implicit = (node.tag + == self.resolve(SequenceNode, node.value, True)) + self.emit(SequenceStartEvent(alias, node.tag, implicit, + flow_style=node.flow_style)) + index = 0 + for item in node.value: + self.serialize_node(item, node, index) + index += 1 + self.emit(SequenceEndEvent()) + elif isinstance(node, MappingNode): + implicit = (node.tag + == self.resolve(MappingNode, node.value, True)) + self.emit(MappingStartEvent(alias, node.tag, implicit, + flow_style=node.flow_style)) + for key, value in node.value: + self.serialize_node(key, node, None) + self.serialize_node(value, node, key) + self.emit(MappingEndEvent()) + self.ascend_resolver() + diff --git a/plugins_available/util/yaml/tokens.py b/plugins_available/util/yaml/tokens.py new file mode 100644 index 0000000..4d0b48a --- /dev/null +++ b/plugins_available/util/yaml/tokens.py @@ -0,0 +1,104 @@ + +class Token(object): + def __init__(self, start_mark, end_mark): + self.start_mark = start_mark + self.end_mark = end_mark + def __repr__(self): + attributes = [key for key in self.__dict__ + if not key.endswith('_mark')] + attributes.sort() + arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) + for key in attributes]) + return '%s(%s)' % (self.__class__.__name__, arguments) + +#class BOMToken(Token): +# id = '' + +class DirectiveToken(Token): + id = '' + def __init__(self, name, value, start_mark, end_mark): + self.name = name + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class DocumentStartToken(Token): + id = '' + +class DocumentEndToken(Token): + id = '' + +class StreamStartToken(Token): + id = '' + def __init__(self, start_mark=None, end_mark=None, + encoding=None): + self.start_mark = start_mark + self.end_mark = end_mark + self.encoding = encoding + +class StreamEndToken(Token): + id = '' + +class BlockSequenceStartToken(Token): + id = '' + +class BlockMappingStartToken(Token): + id = '' + +class BlockEndToken(Token): + id = '' + +class FlowSequenceStartToken(Token): + id = '[' + +class FlowMappingStartToken(Token): + id = '{' + +class FlowSequenceEndToken(Token): + id = ']' + +class FlowMappingEndToken(Token): + id = '}' + +class KeyToken(Token): + id = '?' + +class ValueToken(Token): + id = ':' + +class BlockEntryToken(Token): + id = '-' + +class FlowEntryToken(Token): + id = ',' + +class AliasToken(Token): + id = '' + def __init__(self, value, start_mark, end_mark): + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class AnchorToken(Token): + id = '' + def __init__(self, value, start_mark, end_mark): + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class TagToken(Token): + id = '' + def __init__(self, value, start_mark, end_mark): + self.value = value + self.start_mark = start_mark + self.end_mark = end_mark + +class ScalarToken(Token): + id = '' + def __init__(self, value, plain, start_mark, end_mark, style=None): + self.value = value + self.plain = plain + self.start_mark = start_mark + self.end_mark = end_mark + self.style = style + diff --git a/plugins_available/weather.py b/plugins_available/weather.py new file mode 100644 index 0000000..9556b9f --- /dev/null +++ b/plugins_available/weather.py @@ -0,0 +1,72 @@ +"weather, thanks to google" + +import os +import codecs +import thread +import urllib +from lxml import etree + +from util import hook + + +lock = thread.allocate_lock() +stalk = {} + + +def load_stalk(filename, mtimes={}): + if not os.path.exists(filename): + return {} + mtime = os.stat(filename).st_mtime + if mtimes.get(filename, 0) != mtime: + mtimes[filename] = mtime + return dict(x.strip().split(None, 1) for x in + codecs.open(filename, 'r', 'utf-8')) + + +def save_stalk(filename, houses): + out = codecs.open(filename, 'w', 'utf-8') + out.write('\n'.join('%s %s' % x for x in sorted(houses.iteritems()))) #heh + out.flush() + out.close() + + +@hook.command +def weather(bot, input): + ".weather [dontsave] -- queries the google weather API for weather data" + global stalk + + filename = os.path.join(bot.persist_dir, 'weather') + if not stalk: + with lock: + stalk = load_stalk(filename) + + nick = input.nick.lower() + loc = input.inp.strip() + dontsave = loc.endswith(" dontsave") + if dontsave: + loc = loc[:-9].strip().lower() + if not loc: # blank line + loc = stalk.get(nick, '') + if not loc: + return weather.__doc__ + + data = urllib.urlencode({'weather': loc.encode('utf-8')}) + url = 'http://www.google.com/ig/api?' + data + w = etree.parse(url).find('weather') + + if w.find('problem_cause') is not None: + return "Couldn't fetch weather data for '%s', try using a zip or " \ + "postal code." % input.inp + + info = dict((e.tag, e.get('data')) for e in w.find('current_conditions')) + info['city'] = w.find('forecast_information/city').get('data') + info['high'] = w.find('forecast_conditions/high').get('data') + info['low'] = w.find('forecast_conditions/low').get('data') + + input.reply('%(city)s: %(condition)s, %(temp_f)sF/%(temp_c)sC (H:%(high)sF'\ + ', L:%(low)sF), %(humidity)s, %(wind_condition)s.' % info) + + if not dontsave and loc != stalk.get(nick, ''): + with lock: + stalk[nick] = loc + save_stalk(filename, stalk) diff --git a/plugins_available/wikipedia.py b/plugins_available/wikipedia.py new file mode 100644 index 0000000..8c560ec --- /dev/null +++ b/plugins_available/wikipedia.py @@ -0,0 +1,57 @@ +'''Searches wikipedia and returns first sentence of article +Scaevolus 2009''' + +import urllib +from lxml import etree +import re + +from util import hook + + +api_prefix = "http://en.wikipedia.org/w/api.php" +search_url = api_prefix + "?action=opensearch&search=%s&format=xml" + +paren_re = re.compile('\s*\(.*\)$') + + +@hook.command(hook='w(\s+.*|$)') +@hook.command +def wiki(query): + '''.w/.wiki -- gets first sentence of wikipedia ''' \ + '''article on ''' + + if not query.strip(): + return wiki.__doc__ + + q = search_url % (urllib.quote(query.strip(), safe='')) + x = etree.parse(q) + + ns = '{http://opensearch.org/searchsuggest2}' + items = x.findall(ns + 'Section/' + ns + 'Item') + + if items == []: + if x.find('error') is not None: + return 'error: %(code)s: %(info)s' % x.find('error').attrib + else: + return 'no results found' + + def extract(item): + return [item.find(ns + x).text for x in + ('Text', 'Description', 'Url')] + + title, desc, url = extract(items[0]) + + if 'may refer to' in desc: + title, desc, url = extract(items[1]) + + title = paren_re.sub('', title) + + if title.lower() not in desc.lower(): + desc = title + desc + + desc = re.sub('\s+', ' ', desc).strip() #remove excess spaces + + if len(desc) > 300: + desc = desc[:300] + '...' + + return '%s -- %s' % (desc, url) diff --git a/plugins_available/youtube.py b/plugins_available/youtube.py new file mode 100644 index 0000000..28ad6d8 --- /dev/null +++ b/plugins_available/youtube.py @@ -0,0 +1,41 @@ +import re +from lxml import etree +import locale + +from util import hook + + +def ytdata(id): + url = 'http://gdata.youtube.com/feeds/api/videos/' + id + x = etree.parse(url) + + # I can't figure out how to deal with schemas/namespaces properly :( + yt = '{http://gdata.youtube.com/schemas/2007}' + media = '{http://search.yahoo.com/mrss/}' + + rating = x.find('{http://schemas.google.com/g/2005}rating') + data = dict(rating.items()) + data['title'] = x.find('{http://www.w3.org/2005/Atom}title').text + data['views'] = locale.format('%d', int(x.find(yt + 'statistics').get( + 'viewCount')), 1) + length = int(x.find(media + 'group/' + yt + 'duration').get('seconds')) + data['length'] = '' + if length / 3600: # > 1 hour + data['length'] += str(length/3600) + 'h ' + if length / 60: # > 1 minute + data['length'] += str(length/60 % 60) + 'm ' + data['length'] += "%ds" % (length % 60) + + return data + +youtube_re = re.compile(r'.*youtube.*v=([-_a-z0-9]+)', flags=re.IGNORECASE) + + +#@hook.command(hook=r'(.*)', prefix=False) +def youtube(inp): + m = youtube_re.match(inp) + if m: + data = ytdata(m.group(1)) + return '\x02%(title)s\x02 - rated \x02%(average)s/%(max)s\x02 ' \ + '(%(numRaters)s) - views \x02%(views)s\x02 - length \x02' \ + '%(length)s\x02' % data