WIP cscg24
This commit is contained in:
767
cscg24/misc/can-i-haz-lolpython/lolpython.py
Normal file
767
cscg24/misc/can-i-haz-lolpython/lolpython.py
Normal file
@@ -0,0 +1,767 @@
|
||||
#!/usr/bin/env python
|
||||
# Implementation of the LOLPython language.
|
||||
# Converts from LOLPython to Python then optionally runs the Python.
|
||||
|
||||
# This package depends on PLY -- http://www.dabeaz.com/ply/
|
||||
|
||||
# Written by Andrew Dalke <dalke@dalkescientific.com>
|
||||
# Dalke Scientific Software, LLC
|
||||
# 1 June 2007, Gothenburg, Sweden
|
||||
#
|
||||
# This software is in the public domain. For details see:
|
||||
# http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
|
||||
import sys
|
||||
import keyword
|
||||
import os
|
||||
import types
|
||||
from cStringIO import StringIO
|
||||
from ply import *
|
||||
|
||||
|
||||
__NAME__ = "lolpython"
|
||||
__VERSION__ = "1.0"
|
||||
|
||||
# Translating LOLPython tokens to Python tokens
|
||||
# This could be cleaned up. For example, some of
|
||||
# these tokens could be merged into one.
|
||||
tokens = (
|
||||
"NAME", # variable names
|
||||
"RESERVED", # Used for Python reserved names
|
||||
"NUMBER", # Integers and floats
|
||||
"STRING",
|
||||
"OP", # Like the Python OP
|
||||
"CLOSE", # Don't really need this..
|
||||
|
||||
"COMMENT",
|
||||
"AUTOCALL", # write t.value then add '('
|
||||
"INLINE", # write t.value directly
|
||||
"FUTURE", # for the "I FUTURE CAT WITH" statement
|
||||
"PRINT", # VISIBLE -> stdout or COMPLAIN -> stderr
|
||||
|
||||
"ENDMARKER",
|
||||
"COLON",
|
||||
"WS",
|
||||
"NEWLINE",
|
||||
)
|
||||
|
||||
# Helper functions for making given token types
|
||||
def OP(t, value):
|
||||
t.type = "OP"
|
||||
t.value = value
|
||||
return t
|
||||
|
||||
def RESERVED(t, value):
|
||||
t.type = "RESERVED"
|
||||
t.value = value
|
||||
return t
|
||||
|
||||
def AUTOCALL(t, value):
|
||||
t.type = "AUTOCALL"
|
||||
t.value = "tuple"
|
||||
t.lexer.paren_stack.append(")")
|
||||
return t
|
||||
|
||||
def INLINE(t, value):
|
||||
t.type = "INLINE"
|
||||
t.value = value
|
||||
return t
|
||||
|
||||
#####
|
||||
|
||||
# ply uses a large regex for token detection, and sre is limited to
|
||||
# 100 groups. This grammar pushes the limit. I use (?:non-grouping)
|
||||
# parens to keep the count down.
|
||||
|
||||
|
||||
def t_ASSIGN(t): # cannot be a simple pattern because it must
|
||||
r'CAN[ ]+HA[SZ]\b' # come before the t_NAME definition
|
||||
return OP(t, "=")
|
||||
|
||||
def t_SINGLE_QUOTE_STRING(t):
|
||||
r"'([^\\']+|\\'|\\\\)*'" # I think this is right ...
|
||||
t.type = "STRING"
|
||||
t.value = t.value[1:-1].decode("string-escape")
|
||||
return t
|
||||
|
||||
def t_DOUBLE_QUOTE_STRING(t):
|
||||
r'"([^\\"]+|\\"|\\\\)*"'
|
||||
t.type = "STRING"
|
||||
t.value = t.value[1:-1].decode("string-escape")
|
||||
return t
|
||||
|
||||
# and LOL quoted strings! They end with /LOL
|
||||
# No way to have "/LOL" in the string.
|
||||
def t_LOL_STRING(t):
|
||||
r"LOL[ ]*((?!/LOL).|\n)*[ ]*/LOL"
|
||||
t.type = "STRING"
|
||||
t.value = t.value[3:-4].strip(" ")
|
||||
return t
|
||||
|
||||
# Aliases for the same thing - for extra cuteness
|
||||
def t_LSQUARE(t):
|
||||
r"(?:SOME|LOOK[ ]AT|LET[ ]+THE)\b"
|
||||
t.lexer.paren_stack.append(']')
|
||||
return OP(t, "[")
|
||||
|
||||
def t_LPAREN(t):
|
||||
r"(?:WIT|THEZ)\b"
|
||||
t.lexer.paren_stack.append(')')
|
||||
return OP(t, "(")
|
||||
|
||||
def t_LBRACE(t):
|
||||
r"BUCKET\b"
|
||||
t.lexer.paren_stack.append("}")
|
||||
return OP(t, "{")
|
||||
|
||||
def t_CLOSE(t):
|
||||
r"(?:OK(!+|\b)|!+)"
|
||||
stack = t.lexer.paren_stack
|
||||
if t.value.startswith("OK"):
|
||||
num_closes = len(t.value)-1 # OK -> 1, OK! -> 2, OK!!->3
|
||||
else:
|
||||
num_closes = len(t.value) # ! -> 1, !! -> 2
|
||||
# Which close is this? I use "OK" to match (, [ and {
|
||||
if len(stack) < num_closes:
|
||||
raise AssertionError("not enough opens on the stack: line %d"
|
||||
% (t.lineno,))
|
||||
t.value = "".join(stack[-num_closes:][::-1])
|
||||
del stack[-num_closes:]
|
||||
return t
|
||||
|
||||
def t_EQ(t):
|
||||
r"KINDA[ ]+LIKE\b"
|
||||
return OP(t, "==")
|
||||
|
||||
def t_NE(t):
|
||||
r"(?:KINDA[ ]+)?NOT[ ]+LIKE\b"
|
||||
return OP(t, "!=")
|
||||
|
||||
def t_is(t):
|
||||
r"KINDA[ ]+IS\b"
|
||||
return RESERVED(t, "is")
|
||||
|
||||
def t_GT(t):
|
||||
r"ATE[ ]+MORE[ ]+CHEEZBURGERS?[ ]+THAN\b"
|
||||
return OP(t, ">")
|
||||
|
||||
def t_LT(t):
|
||||
r"ATE[ ]+FEWER[ ]+CHEEZBURGERS?[ ]+THAN\b"
|
||||
return OP(t, "<")
|
||||
|
||||
def t_GTE(t):
|
||||
r"BIG[ ]+LIKE\b"
|
||||
return OP(t, ">=")
|
||||
|
||||
def t_LTE(t):
|
||||
r"SMALL[ ]+LIKE\b"
|
||||
return OP(t, "<=")
|
||||
|
||||
def t_RETURN(t):
|
||||
r"U[ ]+TAKE\b"
|
||||
return RESERVED(t, "return")
|
||||
|
||||
def t_yield(t):
|
||||
r"U[ ]+BORROW\b"
|
||||
return RESERVED(t, "yield")
|
||||
|
||||
def t_ELIF(t):
|
||||
r"OR[ ]+IZ\b"
|
||||
return RESERVED(t, "elif")
|
||||
|
||||
def t_ELSE(t):
|
||||
r"(?:(?:I[ ]+GIVE[ ]+UP|IZ[ ]+KEWL|ALL[ ]+DONE)|NOPE)\b"
|
||||
return RESERVED(t, "else")
|
||||
|
||||
def t_COLON(t):
|
||||
r"\?"
|
||||
t.value = ":"
|
||||
return t
|
||||
|
||||
def t_FROM(t):
|
||||
r"IN[ ]+MAI\b"
|
||||
return RESERVED(t, "from")
|
||||
|
||||
def t_EXCEPT(t):
|
||||
r"O[ ]+NOES\b"
|
||||
return RESERVED(t, "except")
|
||||
|
||||
def t_PLUS(t):
|
||||
r"ALONG[ ]+WITH\b"
|
||||
return OP(t, "+")
|
||||
def t_MINUS(t):
|
||||
r"TAKE[ ]+AWAY\b"
|
||||
return OP(t, "-")
|
||||
|
||||
def t_PLUS_EQUAL(t):
|
||||
r"GETZ[ ]+ANOTHR\b"
|
||||
return OP(t, "+=")
|
||||
|
||||
def t_MINUS_EQUAL(t):
|
||||
r"THROW[SZ]?[ ]+AWAY\b"
|
||||
return OP(t, "-=")
|
||||
|
||||
def t_DIV(t):
|
||||
r"SMASHES[ ]+INTO\b"
|
||||
return OP(t, "/")
|
||||
def t_DIV_EQUAL(t):
|
||||
r"SMASHES[ ]+INTO[ ]+HAS\b"
|
||||
return OP(t, "/=")
|
||||
def t_TRUEDIV(t):
|
||||
r"SMASHES[ ]+NICELY[ ]+INTO\b"
|
||||
return OP(t, "//")
|
||||
def t_MUL(t):
|
||||
r"OF[ ]THOSE\b"
|
||||
return OP(t, "*")
|
||||
def t_MUL_EQUAL(t):
|
||||
r"COPIES[ ]+(?:HIM|HER|IT)SELF[ ]+BY\b"
|
||||
return OP(t, "*=")
|
||||
def t_POW(t):
|
||||
r"BY[ ]+GRAYSKULL[ ]+POWER"
|
||||
return OP(t, "**")
|
||||
def t_IN(t):
|
||||
r"IN[ ]+(?:UR|THE|THIS)\b"
|
||||
return OP(t, "in")
|
||||
def t_del(t):
|
||||
r"DO[ ]+NOT[ ]+WANT\b"
|
||||
return RESERVED(t, "del")
|
||||
def t_and(t):
|
||||
r"\&"
|
||||
return RESERVED(t, "and")
|
||||
def t_or(t):
|
||||
r"OR[ ]+MABEE\b"
|
||||
return RESERVED(t, "or")
|
||||
|
||||
def t_pass(t):
|
||||
r"I[ ]+IZ[ ]+CUTE\b"
|
||||
return RESERVED(t, "pass")
|
||||
|
||||
def t_forever(t):
|
||||
r"WHILE[ ]+I[ ]+CUTE\b"
|
||||
return INLINE(t, "while 1")
|
||||
|
||||
def t_def(t):
|
||||
r"SO[ ]+IM[ ]+LIKE\b"
|
||||
return RESERVED(t, "def")
|
||||
|
||||
def t_class(t):
|
||||
r"ME[ ]+MAKE[ ]\b"
|
||||
return RESERVED(t, "class")
|
||||
|
||||
def t_future(t):
|
||||
r"I[ ]+FUTURE[ ]+CAT[ ]+WITH\b"
|
||||
t.type = "FUTURE"
|
||||
return t
|
||||
|
||||
def t_assert(t):
|
||||
r"SO[ ]+GOOD\b"
|
||||
return RESERVED(t, "assert")
|
||||
|
||||
def t_assert_not(t):
|
||||
r"AINT[ ]+GOOD\b"
|
||||
return INLINE(t, "assert not ")
|
||||
|
||||
def t_for(t):
|
||||
r"GIMME[ ]+EACH\b"
|
||||
return RESERVED(t, "for")
|
||||
|
||||
def t_list(t):
|
||||
r"ALL[ ]+OF\b"
|
||||
return AUTOCALL(t, "tuple")
|
||||
|
||||
RESERVED_VALUES = {
|
||||
"EASTERBUNNY": ("NUMBER", "0"),
|
||||
"CHEEZBURGER": ("NUMBER", "1"),
|
||||
"CHOKOLET": ("NUMBER", "-1"),
|
||||
"TWIN": ("NUMBER", "2"),
|
||||
"TWINZ": ("NUMBER", "2"),
|
||||
"TWINS": ("NUMBER", "2"),
|
||||
"EVILTWIN": ("NUMBER", "-2"),
|
||||
"EVILTWINZ": ("NUMBER", "-2"),
|
||||
"EVILTWINS": ("NUMBER", "-2"),
|
||||
"ALLFINGERZ": ("NUMBER", "10"),
|
||||
"TOEZ": ("NUMBER", "-10"),
|
||||
"ONE": ("NUMBER", "1"),
|
||||
"ONCE": ("NUMBER", "1"),
|
||||
"TWO": ("NUMBER", "2"),
|
||||
"TWICE": ("NUMBER", "2"),
|
||||
"THR33": ("NUMBER", "3"),
|
||||
"FOUR": ("NUMBER", "4"),
|
||||
"FIV": ("NUMBER", "5"),
|
||||
"SIKS": ("NUMBER", "6"),
|
||||
"SEVN": ("NUMBER", "7"),
|
||||
"ATE": ("NUMBER", "8"),
|
||||
"NINE": ("NUMBER", "9"),
|
||||
"MEH": ("NAME", "False"),
|
||||
"YEAH": ("NAME", "True"),
|
||||
"VISIBLE": ("PRINT", "stdout"),
|
||||
"COMPLAIN": ("PRINT", "stderr"),
|
||||
"AND": ("OP", ","),
|
||||
"BLACKHOLE": ("RESERVED", "ZeroDivisionError"),
|
||||
"DONOTLIKE": ("AUTOCALL", "AssertionError"),
|
||||
|
||||
"ANTI": ("OP", "-"),
|
||||
"IZ": ("RESERVED", "if"),
|
||||
"GIMME": ("RESERVED", "import"),
|
||||
"LIKE": ("RESERVED", "as"),
|
||||
"OWN": ("OP", "."),
|
||||
|
||||
"PLZ": ("RESERVED", "try"),
|
||||
"HALP": ("RESERVED", "raise"),
|
||||
"WHATEVER": ("RESERVED", "finally"),
|
||||
"KTHX": ("RESERVED", "continue"),
|
||||
"KTHXBYE": ("RESERVED", "break"),
|
||||
|
||||
"OVER": ("OP", "/"),
|
||||
|
||||
"AINT": ("RESERVED", "not"),
|
||||
"ME": ("RESERVED", "self"),
|
||||
|
||||
"STRING": ("AUTOCALL", "str"),
|
||||
"NUMBR": ("AUTOCALL", "int"),
|
||||
"BIGNESS": ("AUTOCALL", "len"),
|
||||
"NUMBRZ": ("AUTOCALL", "range"),
|
||||
"ADDED": ("AUTOCALL", ".append"),
|
||||
|
||||
"ARGZ": ("INLINE", "_lol_sys.argv"),
|
||||
"THINGZ": ("INLINE", "()"), # invisible tuple didn't sound right
|
||||
"THING": ("INLINE", "()"), # sometimes it's better in singular form
|
||||
"MY": ("INLINE", "self."),
|
||||
"MYSELF": ("INLINE", "(self)"),
|
||||
|
||||
"EVEN": ("INLINE", "% 2 == 0"),
|
||||
"ODD": ("INLINE", "% 2 == 1"),
|
||||
"WIF": ("RESERVED", "with"),
|
||||
}
|
||||
|
||||
def t_FLOAT(t):
|
||||
r"""(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]? \d+)?"""
|
||||
t.value = t.value
|
||||
t.type = "NUMBER"
|
||||
return t
|
||||
|
||||
def t_INT(t):
|
||||
r"\d+"
|
||||
t.type = "NUMBER"
|
||||
return t
|
||||
|
||||
def t_INVISIBLE(t):
|
||||
r"INVISIBLE([ ]+(LIST|STRING|BUCKET))?\b"
|
||||
if "LIST" in t.value:
|
||||
t.type = "INLINE"
|
||||
t.value = "[]"
|
||||
elif "STRING" in t.value:
|
||||
t.type = "INLINE"
|
||||
t.value = '""'
|
||||
elif "BUCKET" in t.value:
|
||||
t.type = "INLINE"
|
||||
t.value = "{}"
|
||||
else:
|
||||
RESERVED(t, "None")
|
||||
return t
|
||||
|
||||
# Not consuming the newline. Needed for "IZ EASTERBUNNY? BTW comment"
|
||||
def t_COMMENT(t):
|
||||
r"[ ]*(?:BTW|WTF)[^\n]*"
|
||||
return t
|
||||
|
||||
def t_NAME(t):
|
||||
r'[a-zA-Z_][a-zA-Z0-9_]*'
|
||||
if t.value in RESERVED_VALUES:
|
||||
type, value = RESERVED_VALUES[t.value]
|
||||
t.type = type
|
||||
t.value = value
|
||||
if t.type == "AUTOCALL":
|
||||
t.lexer.paren_stack.append(")")
|
||||
return t
|
||||
|
||||
def t_WS(t):
|
||||
r' [ ]+ '
|
||||
if t.lexer.at_line_start and not t.lexer.paren_stack:
|
||||
return t
|
||||
|
||||
|
||||
# Don't generate newline tokens when inside of parens
|
||||
def t_newline(t):
|
||||
r'\n+'
|
||||
t.lexer.lineno += len(t.value)
|
||||
t.type = "NEWLINE"
|
||||
if not t.lexer.paren_stack:
|
||||
return t
|
||||
|
||||
|
||||
def t_error(t):
|
||||
raise SyntaxError("Unknown symbol %r" % (t.value[0],))
|
||||
print "Skipping", repr(t.value[0])
|
||||
t.lexer.skip(1)
|
||||
|
||||
|
||||
## I implemented INDENT / DEDENT generation as a post-processing filter
|
||||
|
||||
# The original lex token stream contains WS and NEWLINE characters.
|
||||
# WS will only occur before any other tokens on a line.
|
||||
|
||||
# I have three filters. One tags tokens by adding two attributes.
|
||||
# "must_indent" is True if the token must be indented from the
|
||||
# previous code. The other is "at_line_start" which is True for WS
|
||||
# and the first non-WS/non-NEWLINE on a line. It flags the check so
|
||||
# see if the new line has changed indication level.
|
||||
|
||||
# Python's syntax has three INDENT states
|
||||
# 0) no colon hence no need to indent
|
||||
# 1) "if 1: go()" - simple statements have a COLON but no need for an indent
|
||||
# 2) "if 1:\n go()" - complex statements have a COLON NEWLINE and must indent
|
||||
NO_INDENT = 0
|
||||
MAY_INDENT = 1
|
||||
MUST_INDENT = 2
|
||||
|
||||
# only care about whitespace at the start of a line
|
||||
def track_tokens_filter(lexer, tokens):
|
||||
lexer.at_line_start = at_line_start = True
|
||||
indent = NO_INDENT
|
||||
for token in tokens:
|
||||
token.at_line_start = at_line_start
|
||||
|
||||
if token.type == "COLON":
|
||||
at_line_start = False
|
||||
indent = MAY_INDENT
|
||||
token.must_indent = False
|
||||
|
||||
elif token.type == "NEWLINE":
|
||||
at_line_start = True
|
||||
if indent == MAY_INDENT:
|
||||
indent = MUST_INDENT
|
||||
token.must_indent = False
|
||||
|
||||
elif token.type == "WS":
|
||||
assert token.at_line_start == True
|
||||
at_line_start = True
|
||||
token.must_indent = False
|
||||
|
||||
elif token.type == "COMMENT":
|
||||
pass
|
||||
|
||||
else:
|
||||
# A real token; only indent after COLON NEWLINE
|
||||
if indent == MUST_INDENT:
|
||||
token.must_indent = True
|
||||
else:
|
||||
token.must_indent = False
|
||||
at_line_start = False
|
||||
|
||||
indent = NO_INDENT
|
||||
|
||||
yield token
|
||||
lexer.at_line_start = at_line_start
|
||||
|
||||
def _new_token(type, lineno):
|
||||
tok = lex.LexToken()
|
||||
tok.type = type
|
||||
tok.value = None
|
||||
tok.lineno = lineno
|
||||
tok.lexpos = -1
|
||||
return tok
|
||||
|
||||
# Synthesize a DEDENT tag
|
||||
def DEDENT(lineno):
|
||||
return _new_token("DEDENT", lineno)
|
||||
|
||||
# Synthesize an INDENT tag
|
||||
def INDENT(lineno):
|
||||
return _new_token("INDENT", lineno)
|
||||
|
||||
|
||||
# Track the indentation level and emit the right INDENT / DEDENT events.
|
||||
def indentation_filter(tokens):
|
||||
# A stack of indentation levels; will never pop item 0
|
||||
levels = [0]
|
||||
token = None
|
||||
depth = 0
|
||||
prev_was_ws = False
|
||||
for token in tokens:
|
||||
## if 1:
|
||||
## print "Process", token,
|
||||
## if token.at_line_start:
|
||||
## print "at_line_start",
|
||||
## if token.must_indent:
|
||||
## print "must_indent",
|
||||
## print
|
||||
|
||||
# WS only occurs at the start of the line
|
||||
# There may be WS followed by NEWLINE so
|
||||
# only track the depth here. Don't indent/dedent
|
||||
# until there's something real.
|
||||
if token.type == "WS":
|
||||
assert depth == 0
|
||||
depth = len(token.value)
|
||||
prev_was_ws = True
|
||||
# Don't forward WS to the parser
|
||||
continue
|
||||
|
||||
if token.type == "NEWLINE":
|
||||
depth = 0
|
||||
if prev_was_ws or token.at_line_start:
|
||||
# ignore blank lines
|
||||
continue
|
||||
# pass the other cases on through
|
||||
yield token
|
||||
continue
|
||||
|
||||
if token.type == "COMMENT":
|
||||
yield token
|
||||
continue
|
||||
|
||||
# then it must be a real token (not WS, not NEWLINE)
|
||||
# which can affect the indentation level
|
||||
|
||||
prev_was_ws = False
|
||||
if token.must_indent:
|
||||
# The current depth must be larger than the previous level
|
||||
if not (depth > levels[-1]):
|
||||
raise IndentationError("expected an indented block")
|
||||
|
||||
levels.append(depth)
|
||||
yield INDENT(token.lineno)
|
||||
|
||||
elif token.at_line_start:
|
||||
# Must be on the same level or one of the previous levels
|
||||
if depth == levels[-1]:
|
||||
# At the same level
|
||||
pass
|
||||
elif depth > levels[-1]:
|
||||
raise IndentationError("indentation increase but not in new block")
|
||||
else:
|
||||
# Back up; but only if it matches a previous level
|
||||
try:
|
||||
i = levels.index(depth)
|
||||
except ValueError:
|
||||
raise IndentationError("inconsistent indentation")
|
||||
for _ in range(i+1, len(levels)):
|
||||
yield DEDENT(token.lineno)
|
||||
levels.pop()
|
||||
|
||||
yield token
|
||||
|
||||
### Finished processing ###
|
||||
|
||||
# Must dedent any remaining levels
|
||||
if len(levels) > 1:
|
||||
assert token is not None
|
||||
for _ in range(1, len(levels)):
|
||||
yield DEDENT(token.lineno)
|
||||
|
||||
|
||||
# The top-level filter adds an ENDMARKER, if requested.
|
||||
# Python's grammar uses it.
|
||||
def token_filter(lexer, add_endmarker = True):
|
||||
token = None
|
||||
tokens = iter(lexer.token, None)
|
||||
tokens = track_tokens_filter(lexer, tokens)
|
||||
for token in indentation_filter(tokens):
|
||||
yield token
|
||||
|
||||
if add_endmarker:
|
||||
lineno = 1
|
||||
if token is not None:
|
||||
lineno = token.lineno
|
||||
yield _new_token("ENDMARKER", lineno)
|
||||
|
||||
class LOLLexer(object):
|
||||
def __init__(self, debug=0, optimize=0, lextab='lextab', reflags=0):
|
||||
self.lexer = lex.lex(debug=debug, optimize=optimize,
|
||||
lextab=lextab, reflags=reflags)
|
||||
self.token_stream = None
|
||||
def input(self, s, add_endmarker=True):
|
||||
self.lexer.paren_stack = []
|
||||
self.lexer.input(s)
|
||||
self.token_stream = token_filter(self.lexer, add_endmarker)
|
||||
def token(self):
|
||||
try:
|
||||
return self.token_stream.next()
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
# Helper class to generate logically correct indented Python code
|
||||
class IndentWriter(object):
|
||||
def __init__(self, outfile):
|
||||
self.outfile = outfile
|
||||
self.at_first_column = True
|
||||
self.indent = 0
|
||||
def write(self, text):
|
||||
if self.at_first_column:
|
||||
self.outfile.write(" "*self.indent)
|
||||
self.at_first_column = False
|
||||
self.outfile.write(text)
|
||||
|
||||
# Split things up because the from __future__ statements must
|
||||
# go before any other code.
|
||||
HEADER = """# LOLPython to Python converter version 1.0
|
||||
# Written by Andrew Dalke, who should have been working on better things.
|
||||
|
||||
"""
|
||||
|
||||
BODY = """
|
||||
# sys is used for COMPLAIN and ARGZ
|
||||
import sys as _lol_sys
|
||||
|
||||
"""
|
||||
|
||||
def to_python(s):
|
||||
L = LOLLexer()
|
||||
L.input(s)
|
||||
|
||||
header = StringIO()
|
||||
header.write(HEADER)
|
||||
header_output = IndentWriter(header)
|
||||
|
||||
body = StringIO()
|
||||
body.write(BODY)
|
||||
body_output = IndentWriter(body)
|
||||
|
||||
write = body_output.write
|
||||
output = body_output
|
||||
|
||||
for t in iter(L.token_stream):
|
||||
if t.type == "NAME":
|
||||
# Need to escape names which are Python variables Do that
|
||||
# by appending an "_". But then I also need to make sure
|
||||
# that "yield_" does not collide with "yield". And you
|
||||
# thought you were being clever trying to use a Python
|
||||
# variable. :)
|
||||
name = t.value.rstrip("_")
|
||||
if name in keyword.kwlist:
|
||||
write(t.value + "_ ")
|
||||
else:
|
||||
write(t.value + " ")
|
||||
|
||||
elif t.type in ("RESERVED", "OP", "NUMBER", "CLOSE"):
|
||||
# While not pretty, I'll put a space after each
|
||||
# term because it's the simplest solution. Otherwise
|
||||
# I'll need to track the amount of whitespace between
|
||||
# the tokens in the original text.
|
||||
write(t.value+" ")
|
||||
|
||||
# XXX escape names which are special in Python!
|
||||
elif t.type == "STRING":
|
||||
write(repr(t.value) + " ")
|
||||
|
||||
elif t.type == "COMMENT":
|
||||
# Not enough information to keep comments on the correct
|
||||
# indentation level. This is good enough. Ugly though.
|
||||
# Maybe I need to fix the tokenizer.
|
||||
write("#"+ t.value[3:]+"\n")
|
||||
output.at_first_column = True
|
||||
|
||||
elif t.type == "COLON":
|
||||
write(":")
|
||||
|
||||
elif t.type == "INDENT":
|
||||
output.indent += 1
|
||||
pass
|
||||
elif t.type == "DEDENT":
|
||||
output.indent -= 1
|
||||
pass
|
||||
elif t.type == "NEWLINE":
|
||||
write(t.value)
|
||||
output.at_first_column = True
|
||||
output = body_output
|
||||
write = output.write
|
||||
elif t.type == "PRINT":
|
||||
if t.value == "stdout":
|
||||
write("print ")
|
||||
elif t.value == "stderr":
|
||||
write("print >>_lol_sys.stderr, ")
|
||||
else:
|
||||
raise AssertionError(t.value)
|
||||
elif t.type == "AUTOCALL":
|
||||
write(t.value + "(")
|
||||
elif t.type == "INLINE":
|
||||
write(t.value)
|
||||
elif t.type == "ENDMARKER":
|
||||
write("\n# The end.\n")
|
||||
elif t.type == "WS":
|
||||
output.leading_ws = t.value
|
||||
elif t.type == "FUTURE":
|
||||
# Write to the header. This is a hack. Err, a hairball.
|
||||
output = header_output
|
||||
write = output.write
|
||||
write("from __future__ import ")
|
||||
|
||||
else:
|
||||
raise AssertionError(t.type)
|
||||
|
||||
return header.getvalue() + body.getvalue()
|
||||
|
||||
|
||||
# API code for doing the translation and exec'ing the result
|
||||
|
||||
def execfile(infile, module_name="__lolmain__"):
|
||||
"file, module_name -- exec the lolpython file in a newly created module"
|
||||
if not hasattr(infile, "read"):
|
||||
s = open(infile).read()
|
||||
else:
|
||||
s = infile.read()
|
||||
return execstring(s, module_name)
|
||||
|
||||
def execstring(s, module_name="__lolmain__"):
|
||||
"s, module_name -- exec the lolpython string in a newly created module"
|
||||
python_s = to_python(s)
|
||||
# Doing this bit of trickiness so I can have LOLPython code act
|
||||
# like __main__. This fix is enough to fool unittest.
|
||||
m = types.ModuleType(module_name)
|
||||
sys.modules[module_name] = m
|
||||
exec python_s in m.__dict__
|
||||
return m
|
||||
|
||||
def convert_file(infile, outfile):
|
||||
"read LOLPython code from infile, write converted Python code to outfile"
|
||||
if not hasattr(outfile, "write"):
|
||||
outfile = open(outfile, "w")
|
||||
outfile.write(to_python(infile.read()))
|
||||
|
||||
def convert(filenames):
|
||||
"convert LOLPython filenames into corresponding Python '.py' files"
|
||||
if not filenames:
|
||||
convert_file(sys.stdin, sys.stdout)
|
||||
else:
|
||||
for filename in filenames:
|
||||
base, ext = os.path.splitext(filename)
|
||||
convert_file(open(filename), open(base+".py", "w"))
|
||||
|
||||
def help():
|
||||
print """convert and run a lolpython program
|
||||
Commands are:
|
||||
lolpython Read a lolpython program from stdin and execute it
|
||||
lolpython --convert Convert a lolpython program from stdin
|
||||
and generate python to stdout
|
||||
lolpython --convert filename1 [filename....]
|
||||
Convert a list of lolpython files into Python files
|
||||
lolpython filename [arg1 [arg2 ...]]
|
||||
Run a lolpython program using optional arguments
|
||||
"""
|
||||
|
||||
def main(argv):
|
||||
if len(argv) >= 2:
|
||||
if argv[1] == "--convert":
|
||||
convert(argv[2:])
|
||||
return
|
||||
if argv[1] == "--help":
|
||||
help()
|
||||
return
|
||||
if argv[1] == "--version":
|
||||
print __NAME__ + " " + __VERSION__
|
||||
return
|
||||
|
||||
# otherwise, run the lolpython program
|
||||
sys.argv = sys.argv[1:]
|
||||
filename = sys.argv[0]
|
||||
execfile(filename, "__main__")
|
||||
else:
|
||||
# commands from stdin
|
||||
execfile(sys.stdin)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
Reference in New Issue
Block a user