summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--librelogo/source/LibreLogo/LibreLogo.py33
-rw-r--r--sw/qa/uitest/librelogo/compile.py4
-rw-r--r--sw/qa/uitest/librelogo/run.py29
3 files changed, 54 insertions, 12 deletions
diff --git a/librelogo/source/LibreLogo/LibreLogo.py b/librelogo/source/LibreLogo/LibreLogo.py
index 93c51d636312..06ccca3a6377 100644
--- a/librelogo/source/LibreLogo/LibreLogo.py
+++ b/librelogo/source/LibreLogo/LibreLogo.py
@@ -1874,9 +1874,6 @@ def __compil__(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)
@@ -1909,17 +1906,29 @@ def __compil__(s):
operators = re.compile(r"(?iu)(%s)" % "(?:[ ]*([+*/<>]|//|==|<=|>=|<>|!=)[ ]*|[ ]*-[ ]+|(?<! )-[ ]*|[ ]*[*][*][ ]*)") # operators, eg. " - ", "-", "- "
atoms = re.compile(r"(?iu)(%s)" % "[0-9]+([.,][0-9]+)?|\w+([.]\w)?")
+ # store argument numbers of all subroutines in dictionary "names"
+ names = {key: 1 for key in functions + defaultfunc}
+ names["range"] = names["re.sub"] = 3
+ names["re.search"] = names["re.findall"] = 2
+
+ # match a function header
+ search_funcdef = re.compile(r"(^|\n) *(def (\w+))(\([^\n]*\):) *(?=\n)")
+
+ # "multiline" lambda function to process function headers: add commas to argument list and
+ # add {"subroutine_name": argument_count} into names using a temporary array
+ # (instead of using global variable "names" and a new global function to process the matching patterns)
+ # for example: "def f(x y z):" -> "def f(x,y,z):" and names = {"f": 3}
+ process_funcdef = lambda r: r.group(1) + r.group(2) + \
+ [chsp.sub(", ", r.group(4)), names.update({r.group(3): len(re.findall(r"\w+", r.group(4)))})][0]
+ # process all function headers calling process_funcdef for every matching
+ # (before parsing Logo expressions line by line, we need to know about all functions,
+ # because functions can be defined in any order, ie. their calls can be before
+ # their definitions)
+ s = search_funcdef.sub(process_funcdef, s)
+
+ # process line by line
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
- else:
- s2 = len(chsp.findall(s.group(3))) + 1
- i = s.group(1) + chsp.sub(", ", s.group(3))
- 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
diff --git a/sw/qa/uitest/librelogo/compile.py b/sw/qa/uitest/librelogo/compile.py
index 73c2e8fbaaa6..79a297ef1593 100644
--- a/sw/qa/uitest/librelogo/compile.py
+++ b/sw/qa/uitest/librelogo/compile.py
@@ -98,6 +98,8 @@ class LibreLogoCompileTest(UITestCase):
("a=(SIN 102) + (COS 102)", "a=(sin(102)) + (cos(102))"),
("a=SIN 103 + COS 103 - SQRT 103", "a=sin(103 + cos(103 - sqrt(103)))"),
("a=(SIN 104 + COS 104) - SQRT 104", "a=(sin(104 + cos(104))) - sqrt(104)"),
+ # SIN(x) is Python-like, SIN (x) is Logo-like syntax
+ ("a=SIN(105) + COS (105) - SQRT 105", "a=sin(105) + cos((105) - sqrt(105))"),
("a=COUNT [1, 2, 3]", "a=len([1, 2, 3])"),
("PRINT COUNT [1, 2, 3]", "Print(len([1, 2, 3]))"),
("PRINT 'TEXT: ' + 'CHAR'[0] + ' TEXT2: ' + variable[-1]", "Print(u'TEXT: ' + u'CHAR'[0] + u' TEXT2: ' + variable[-1])"),
@@ -119,6 +121,8 @@ class LibreLogoCompileTest(UITestCase):
("TO f x y z\nLABEL x+y+z\nEND\nf len [1, cos 2, [65]] sqrt len [1, 2, 3, 4] sin 90 * cos 270", "global f\ndef f(x, y, z):\n __checkhalt__()\n %s\n label(x+y+z)\n %s\n%s\nf(len([1, cos(2), [65]]), sqrt(len([1, 2, 3, 4])), sin(90 * cos(270)))" % (((self.LS),)*3)),
("TO f x y z\nLABEL x+y+z\nEND\nf len([1, cos 2, [65]]) sqrt(len [1, 2, 3, 4]) sin(90) * cos 270", "global f\ndef f(x, y, z):\n __checkhalt__()\n %s\n label(x+y+z)\n %s\n%s\nf(len([1, cos(2), [65]]), sqrt(len([1, 2, 3, 4])), sin(90) * cos(270))" % (((self.LS),)*3)),
("TO f x y z\nLABEL x+y+z\nEND\nf (len [1, cos 2, [65]]) (sqrt len [1, 2, 3, 4]) (sin 90) * (cos 270)", "global f\ndef f(x, y, z):\n __checkhalt__()\n %s\n label(x+y+z)\n %s\n%s\nf((len([1, cos(2), [65]])), (sqrt(len([1, 2, 3, 4]))), (sin(90)) * (cos(270)))" % (((self.LS),)*3)),
+ # arbitrary order of function definitions and calls
+ ("f 1 1 f 2 2\nTO f x y\nPRINT x + y\nEND", "global f\nf(1, 1)\nf(2, 2)\n%s\ndef f(x, y):\n __checkhalt__()\n %s\n Print(x + y)\n %s" % (((self.LS),)*3)),
):
compiled = xCompile.invoke((test[0],), (), ())[0]
self.assertEqual(test[1], re.sub(r'(\n| +\n)+', '\n', re.sub(r'\( ', '(', compiled)).strip())
diff --git a/sw/qa/uitest/librelogo/run.py b/sw/qa/uitest/librelogo/run.py
index 5d36c5d14e7b..5d64e1bea152 100644
--- a/sw/qa/uitest/librelogo/run.py
+++ b/sw/qa/uitest/librelogo/run.py
@@ -69,6 +69,35 @@ class LibreLogoTest(UITestCase):
# first paragraph is empty (for working page break)
self.assertEqual(document.Text.createEnumeration().nextElement().String, "")
+ # function definitions and calls can be in arbitrary order
+ document.Text.String = """
+; dragon curve
+TO x n
+IF n = 0 [ STOP ]
+x n-1
+RIGHT 90
+y n-1 ; it worked only as "y(n-1)"
+FORWARD 10
+END
+
+TO y n
+IF n = 0 [ STOP ]
+FORWARD 10
+x n-1
+LEFT 90
+y n-1
+END
+
+PICTURE ; start new line draw
+x 3 ; draw only a few levels
+"""
+ self.logo("run")
+ # wait for LibreLogo program termination
+ while xIsAlive.invoke((), (), ())[0]:
+ pass
+ # new shape + previous two ones = 3
+ self.assertEqual(document.DrawPage.getCount(), 3)
+
self.ui_test.close_doc()
# vim: set shiftwidth=4 softtabstop=4 expandtab: