diff options
Diffstat (limited to 'librelogo/source/LibreLogo/LibreLogo.py')
-rw-r--r-- | librelogo/source/LibreLogo/LibreLogo.py | 162 |
1 files changed, 119 insertions, 43 deletions
diff --git a/librelogo/source/LibreLogo/LibreLogo.py b/librelogo/source/LibreLogo/LibreLogo.py index 103b6061244e..53f62a32f199 100644 --- a/librelogo/source/LibreLogo/LibreLogo.py +++ b/librelogo/source/LibreLogo/LibreLogo.py @@ -1697,7 +1697,6 @@ def __loadlang__(lang, a): [r"(?<!:)\b(?:%s)\b" % a['BACKWARD'], "\n)backward("], [r"(?<!:)\b(?:%s)\b" % a['TURNRIGHT'], "\n)turnright("], [r"(?<!:)\b(?:%s)\b" % a['RANDOM'], "Random"], - [r"(?<!:)\b(?:%s)\b(?= \d)" % 'Random', "random.random()*"], [r"(?<!:)\b(?:%s)\b" % a['SET'], "set"], [r"(?<!:)\b(?:%s)\b" % a['RANGE'], "range"], [r"(?<!:)\b(?:%s)\b" % a['LIST'], "list"], @@ -1708,22 +1707,106 @@ def __loadlang__(lang, a): [r"(?<!:)\b(?:%s)\b ?\(" % a['REFINDALL'], "re.findall('(?u)'+"], [r"(?<!:)\b(?:%s)\b" % a['ANY'], "u'any'"], [r"(?<!:)\b(?:%s) (\w+|[[][^\]]*])\b" % a['INPUT'], " Input(\\1)"], - [r"(?<!:)\b(?:%s)\b" % a['PRINT'], "\n)Print("], + [r"(?<!:)\b(?:%s)\b" % a['PRINT'], "\nPrint"], [r"(?<!:)\b(?:%s)\b" % a['TURNLEFT'], "\n)turnleft("], [r"\b([0-9]+([,.][0-9]+)?)(%s)\b" % a['PT'], "\\1"], [r"\b([0-9]+([,.][0-9]+)?)(%s)(?!\w)" % a['INCH'], lambda r: str(float(r.group(1).replace(",", "."))*72)], [r"\b([0-9]+([,.][0-9]+)?)(%s)\b" % a['MM'], lambda r: str(float(r.group(1).replace(",", "."))*__MM_TO_PT__)], [r"\b([0-9]+([,.][0-9]+)?)(%s)\b" % a['CM'], lambda r: str(float(r.group(1).replace(",", "."))*__MM_TO_PT__*10)], - [r"\b(__(?:int|float|string)__len|round|abs|sin|cos|sqrt|log10|set|list|tuple|sorted)\b ((?:\w|\d+([,.]\d+)?|0[xX][0-9a-fA-F]+|[-+*/]| )+)\)" , "\\1(\\2))" ], # fix parsing: (1 + sqrt x) -> (1 + sqrt(x)) [r"(?<=[-*/=+,]) ?\n\)(\w+)\(", "\\1()"], # read attributes, eg. x = fillcolor [r"(?<=return) ?\n\)(\w+)\(", "\\1()"], # return + user function - [r"(?<=(?:Print|label)\() ?\n\)(\w+)\(", "\\1()\n"] # Print/label + user function ] def __concatenation__(r): # keep line positions with extra line breaks s = re.subn("~[ \t]*\n", " ", r.group(0)) return s[0] + "\n" * s[1] +# convert Logo expressions to Python ones by adding +# missing parentheses to procedure and function calls +# f x y z -> f(x, y, z) +# a = [sin len f [cos 45, 6] [1, 2, 3] sin 90] -> +# a = [sin(len(f([cos(45),6],[1, 2, 3], sin(90))] +# NOTE: "f(45)" and "f (45)" are not the same: +# sin(45) + cos 45 -> sin(45) + cos(45) +# sin (45) + cos 45 -> sin(45 + cos(45)) +def __l2p__(i, par, insub, inarray): + first = True + while par["pos"] < len(i): + pos = par["pos"] + ch = i[pos] + ignored = False + # starting parenthesis + if ch in "([": + if insub and not inarray and not first and par["out"][-1:] != "]": + return + par["out"] += ch + par["pos"] += 1 + __l2p__(i, par, insub, True) + # ending parenthesis + elif ch in ")]": + if insub and not inarray: + return + # put character before terminating spaces + par["out"] = re.sub("( *)$", ch + "\\1", par["out"]) + par["pos"] += 1 + return + # starting a subroutine + elif pos in par["sub"]: + if insub and not inarray and not first: + return + first = False + subname = i[pos:par["sub"][pos]] + par["pos"] += len(subname) + par["out"] += subname + # Logo syntax: add parentheses + # for example: foo x y z -> foo(x, y, z) + par["out"] += "(" + for j in range(par["names"][subname]): + # add commas, except if already added, eg. with special RANGE + # (variable argument counts: RANGE 1 or RANGE 1 100 or RANGE 1 100 10) + if j > 0 and par["out"][-1] != ",": + par["out"] = re.sub("( *)$",",\\1", par["out"]) + __l2p__(i, par, True, False) + par["out"] = re.sub("( *)$", ")\\1", par["out"]) + # operators + elif pos in par["op"]: + op = i[pos:par["op"][pos]] + par["out"] += op + par["pos"] += len(op) + __l2p__(i, par, insub, False) + # other atoms + elif pos in par["atom"]: + if insub and not inarray and not first: + return + first = False + atom = i[pos:par["atom"][pos]] + par["out"] += atom + par["pos"] += len(atom) + # handle subroutines with explicite parentheses + # and array indices + if i[par["pos"]:par["pos"]+1] in "([": + first = True + continue + # optional negative or positive sign + elif ch in "-+": + if insub and not inarray and not first: + return + par["out"] += ch + par["pos"] += 1 + ignored = first + elif ch == " ": + par["out"] += ch + par["pos"] += 1 + ignored = first + elif insub and ((ch == "," and not inarray) or (ch != ",")): + return + else: + par["out"] += ch + par["pos"] += 1 + # end of first subexpression, except in the case of ignored characters + if not ignored: + first = False + def __compil__(s): global _, comp, __strings__, __compiled__ try: @@ -1749,7 +1832,6 @@ def __compil__(s): __loadlang__(_.lng, __l12n__(_.lng)) _.decimal = __l12n__(_.lng)['DECIMAL'] - names = {} rmsp = re.compile(r"[ ]*([=+*/]|==|<=|>=|<>|!=|-[ ]+)[ ]*") chsp = re.compile(r"[ \t]+") @@ -1791,6 +1873,10 @@ def __compil__(s): subnames = re.findall(u"(?iu)(?<=__def__ )\w+", s) globs = "" functions = ["range", "__int__", "__float__", "Random", "Input", "__string__", "len", "round", "abs", "sin", "cos", "sqrt", "log10", "set", "list", "tuple", "re.sub", "re.search", "re.findall", "sorted", "min", "max"] + defaultfunc = ["Print"] # TODO handle all default procedures + names ={key: 1 for key in functions + defaultfunc} + names["range"] = names["re.sub"] = 3 + names["re.search"] = names["re.findall"] = 2 if len(subnames) > 0: globs = "global %s" % ", ".join(subnames) @@ -1810,66 +1896,56 @@ def __compil__(s): if len(procedures) > 0: s = re.sub(r"(?<!__def__)(?<![-+=*/])(?<!%s)(?:^|[ \t]+)(" % ")(?<!".join(functions) + "|".join(procedures) + ")(?!\w)", r"\n\1", s) - # compile native Logo + # substitute LibreLogo functions and specifiers with their Python equivalents for i in __comp__[_.lng]: s = re.sub(u"(?u)" + i[0], i[1], s) - indent = 0 + + indent = 0 # Python indentation level result = "" func = re.compile("(?iu)(def (\w+))(\(.*\):)") - expr = r"""(?iu)(?<!def[ ])(?<![:\w])%(name)s(?!\w)(?!\()(?![ ]\() - ( - ([ ]+\[*([-+]|\([ ]?)*((%(functions)s)\b[ ]*\(*)* - (?:0x[0-9a-f]+|[0-9]+([,.][0-9]+)?|:?\w+(?:[.]\w+[\(]?[\)]?)?]*|\[])]*[\)]* - ( - (?:[ ]*([+*/,<>]|//|==|<=|>=|<>|!=)[ ]*|[ ]*-[ ]+|-|[ ]*[*][*][ ]*) # operators, eg. "**", " - ", "-", "- " - \[*([-+]|\([ ]?)* # minus sign, parenthesis - ((%(functions)s)\b[ ]*\(*)*(0x[0-9a-f]+|[0-9]+([.,][0-9]+)?|:?\w+(?:[.]\w+[\(]?[\)]?)?)]* - ([ ]?\))*)* - [\)]*){,%(repeat)s} - ) -""" - chargsp = re.compile(r"(?<![\(,])(?<!%s) (?!\)|,)" % ")(?<!".join(functions)) # compile to Python - joinfunc = "|".join(functions) - funcnames = {} + subroutines = re.compile(r"(?iu)(?<!def )(?<![_\w])\b(%s)\b(?![\w(])" % "|".join(subnames + functions + defaultfunc)) + operators = re.compile(r"(?iu)(%s)" % "(?:[ ]*([+*/<>]|//|==|<=|>=|<>|!=)[ ]*|[ ]*-[ ]+|(?<! )-[ ]*|[ ]*[*][*][ ]*)") # operators, eg. " - ", "-", "- " + atoms = re.compile(r"(?iu)(%s)" % "[0-9]+([.,][0-9]+)?|\w+([.]\w)?") for i in s.split("\n"): i = i.strip() + # store argument numbers of subroutines in names if i[0:4] == 'def ': s = func.search(i) if s.group(3) == '():': - names[s.group(2)] = (0, "") + names[s.group(2)] = 0 else: s2 = len(chsp.findall(s.group(3))) + 1 i = s.group(1) + chsp.sub(", ", s.group(3)) - names[s.group(2)] = (s2, re.compile(expr % {"name": s.group(2), "functions": joinfunc, "repeat": s2}, re.X)) - for j in functions: - if j in i: - if not j in funcnames: - funcnames[j] = (1, re.compile(expr % {"name": j, "functions": joinfunc, "repeat": 1 + 2 * int(j == 'range')}, re.X)) - r = funcnames[j][1].search(i) - while r: - i = i[:r.start()] + j + '(' + chargsp.sub(", ", rmsp.sub(lambda l: l.group(1).strip(), r.group(1).strip())) + ')' + i[r.end():] - i = parenfix.sub("\\1)]", i) - r = funcnames[j][1].search(i) - for j in names: - if j in i: - if names[j][0] == 0: - if not j in functions: - i = re.sub(r"(?iu)(?<!def )(?<![_\w])\b%s\b(?!\w)" %j, j+'()', i) - else: - r = names[j][1].search(i) - if r: - i = i[:r.start()] + j + '(' + chargsp.sub(", ", rmsp.sub(lambda l: l.group(1).strip(), r.group(1).strip())) + ')' + i[r.end():] - i = parenfix.sub("\\1)]", i) + names[s.group(2)] = s2 + + # convert Logo expressions to Python ones using regex based tokenization + # tokens: {startpos: endpos} dictionaries for subroutine names, operators and other tokens + + # sub: subroutine tokens = positions of Logo subroutine names + # (without explicite parentheses, for example: "f x" or "f (x*2)", but not "f(x)") + sub = {key: value for (key, value) in [j.span() for j in list(subroutines.finditer(i))]} + if sub != {}: + # op: operator tokens + op = {key: value for (key, value) in [j.span() for j in list(operators.finditer(i))]} + # atom: other tokens (variable names, numbers and function names) + atom = {key: value for (key, value) in [j.span() for j in list(atoms.finditer(i))]} + par = {"pos": 0, "out": "", "sub": sub, "op": op, "atom": atom, "names": names} + __l2p__(i, par, False, False) + i = par["out"] + # starting program block if i[0:1] == '[': i = i[1:] indent += 1 + # check program stop, for example, in every execution of a loop result = result + "\n" + " " * indent + "__checkhalt__()\n" + # fix position of ending parenthesis if i[0:1] == ')': i = i[1:] + ')' result = result + "\n" + " " * indent + i + # ending program block if i[0:1] == ']': result = result[:-1] indent -= 1 |