summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLászló Németh <nemeth@numbertext.org>2018-11-05 22:50:40 +0100
committerLászló Németh <nemeth@numbertext.org>2018-11-06 08:32:56 +0100
commitb274e879bdbed85c9373e90e6998cabe22b5bd3a (patch)
tree604efbe2d00dc6bd2729fe178e453db4d6e2eb7d
parentconvert some macros to local functions (diff)
downloadcore-b274e879bdbed85c9373e90e6998cabe22b5bd3a.tar.gz
core-b274e879bdbed85c9373e90e6998cabe22b5bd3a.zip
LibreLogo: function calls and definitions can be in any order
with Logo syntax, too. Mutual recursion, for example drawing dragon curve (see in the unit test of the commit) doesn't need Python syntax any more to call the function before its definition. Change-Id: I93426a8c5be394fb4f1203e0490f7fa8d8491597 Reviewed-on: https://gerrit.libreoffice.org/62926 Tested-by: Jenkins Reviewed-by: László Németh <nemeth@numbertext.org>
-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: