Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
120 lines (111 sloc) 5.31 KB
"Soros interpreter (see http://numbertext.org)"
from __future__ import unicode_literals
from __future__ import print_function
import re, sys
def run(program, data, lang):
return compile(program, lang).run(data)
def compile(program, lang):
return _Soros(program, lang)
# conversion function
def _tr(text, chars, chars2, delim):
for i in range(0, len(chars)):
text = text.replace(delim + chars[i], chars2[i])
return text
# string literals for metacharacter encoding
_m = "\\\";#$()|[]"
_c = u"\uE000\uE001\uE002\uE003\uE004\uE005\uE006\uE007\uE008\uE009" # Unicode private area
_pipe = u"\uE003"
# separator prefix = \uE00A
# pattern to recognize function calls in the replacement string
_func = re.compile(_tr(r"""(?:\|?(?:\$\()+)? # optional nested calls
(\|?\$\(([^\(\)]*)\)\|?) # inner call (2 subgroups)
(?:\)+\|?)?""", # optional nested calls
_m[4:8], _c[:4], "\\"), re.X) # \$, \(, \), \| -> \uE000..\uE003
class _Soros:
def __init__(self, prg, lang):
self.lines = []
if prg.find("__numbertext__") == -1:
prg = "__numbertext__;" + prg
# default left zero deletion
# and separator function (no separation, if subcall returns with empty string)
prg = prg.replace("__numbertext__", u"""0+(0|[1-9]\\d*) $1
\"([a-z][-a-z]* )0+(0|[1-9]\\d*)\" $(\\1\\2)
\"\uE00A(.*)\uE00A(.+)\uE00A(.*)\" \\1\\2\\3
\"\uE00A.*\uE00A\uE00A.*\"
""")
prg = _tr(prg, _m[:4], _c[:4], "\\") # \\, \", \;, \# -> \uE000..\uE003
# switch off all country-dependent lines, and switch on the requested ones
prg = re.sub(r"(^|[\n;])([^\n;#]*#[^\n]*[[]:[^\n:\]]*:][^\n]*)", r"\1#\2", prg)
prg = re.sub(r"(^|[\n;])#([^\n;#]*#[^\n]*[[]:" + lang.replace("_", "-") + r":][^\n]*)", r"\1\2", prg)
matchline = re.compile("^\s*(\"[^\"]*\"|[^\s]*)\s*(.*[^\s])?\s*$")
prefix = ""
for s in re.sub("(#[^\n]*)?(\n|$)", ";", prg).split(";"):
macro = re.match("== *(.*[^ ]?) ==", s)
if macro != None:
prefix = macro.group(1)
continue
m = matchline.match(s)
if prefix != "" and s != "" and m != None:
s = m.group(1).strip("\"")
space = " " if s != "" else ""
caret = ""
if s[0:1] == "^":
s = s[1:]
caret = "^"
s2 = m.group(2) if m.group(2) != None else ""
s = "\"" + caret + prefix + space + s + "\" " + s2
m = matchline.match(s)
if m != None:
s = _tr(m.group(1).strip("\""), _c[1:4], _m[1:4], "") \
.replace(_c[_m.find("\\")], "\\\\") # -> \\, ", ;, #
if m.group(2) != None:
s2 = m.group(2).strip("\"")
else:
s2 = ""
s2 = _tr(s2, _m[4:], _c[4:], "\\") # \$, \(, \), \|, \[, \] -> \uE004..\uE009
# call inner separator: [ ... $1 ... ] -> $(\uE00A ... \uE00A$1\uE00A ... )
s2 = re.sub(r"[[]\$(\d\d?|\([^\)]+\))",u"$(\uE00A\uE00A|$\\1\uE00A", s2)
s2 = re.sub(r"[[]([^\$[\\]*)\$(\d\d?|\([^\)]+\))",u"$(\uE00A\\1\uE00A$\\2\uE00A", s2)
s2 = re.sub(r"\uE00A]$","|\uE00A)", s2) # add "|" in terminating position
s2 = re.sub(r"]",")", s2)
s2 = re.sub(r"(\$\d|\))\|\$", r"\1||$", s2) # $()|$() -> $()||$()
s2 = _tr(s2, _c[:4], _m[:4], "") # \uE000..\uE003-> \, ", ;, #
s2 = _tr(s2, _m[4:8], _c[:4], "") # $, (, ), | -> \uE000..\uE003
s2 = _tr(s2, _c[4:], _m[4:], "") # \uE004..\uE009 -> $, (, ), |, [, ]
s2 = re.sub(r"\\(\d)", r"\\g<\1>",
re.sub(r"\uE000(\d)", "\uE000\uE001\\\\g<\\1>\uE002", s2))
try:
self.lines = self.lines + [[
re.compile("^" + s.lstrip("^").rstrip("$") + "$"),
s2, s[:1] == "^", s[-1:] == "$"]]
except:
print("Error in following regex line: " + s, file=sys.stderr)
raise
def run(self, data):
return self._run(data, True, True)
def _run(self, data, begin, end):
for i in self.lines:
if not ((begin == False and i[2]) or (end == False and i[3])):
m = i[0].match(data)
if m:
try:
s = m.expand(i[1])
except:
print("Error for the following input: " + data, file=sys.stderr)
raise
n = _func.search(s)
while n:
b = False
e = False
if n.group(1)[0:1] == _pipe or n.group()[0:1] == _pipe:
b = True
elif n.start() == 0:
b = begin
if n.group(1)[-1:] == _pipe or n.group()[-1:] == _pipe:
e = True
elif n.end() == len(s):
e = end
s = s[:n.start(1)] + self._run(n.group(2), b, e) + s[n.end(1):]
n = _func.search(s)
return s
return ""