Repository: hedyorg/hedy

Issue:
Better error messages for forgetting arguments
`echo` or `print` without arguments now fail hard, we should add them to the grammar with higher prio to give a Hedy instead of Python error message


Discussion:
Fixed by recent grammar changes for levels 1 to 3, still needs to be improved for higher levels

Resolving patch:
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -402,14 +402,18 @@ def valid_echo(ast):
     return no_echo or ('echo' in command_names and 'ask' in command_names) and command_names.index('echo') > command_names.index('ask')
 
 
-
 class IsComplete(Filter):
+    def __init__(self, level):
+        self.level = level
     # print, ask an echo can miss arguments and then are not complete
     # used to generate more informative error messages
     # tree is transformed to a node of [True] or [False, args, line_number]
 
     def ask(self, args):
-        return args != [], 'ask'
+        # in level 1 ask without arguments means args == []
+        # in level 2 and up, ask without arguments is a list of 1, namely the var name
+        incomplete = (args == [] and self.level==1) or (len(args) == 1 and self.level >= 2)
+        return not incomplete, 'ask'
     def print(self, args):
         return args != [], 'print'
     def input(self, args):
@@ -1293,7 +1297,7 @@ def transpile_inner(input_string, level, sub=0):
                 raise HedyException('Parse', level=level, location=["?", "?"], keyword_found=invalid_command)
             raise HedyException('Invalid', invalid_command=invalid_command, level=level, guessed_command=closest)
 
-    is_complete = IsComplete().transform(program_root)
+    is_complete = IsComplete(level).transform(program_root)
     if not is_complete[0]:
         incomplete_command = is_complete[1][0]
         line = is_complete[2]


Test patch:
diff --git a/tests/tests_level_01.py b/tests/tests_level_01.py
--- a/tests/tests_level_01.py
+++ b/tests/tests_level_01.py
@@ -33,11 +33,14 @@ def test_transpile_other(self):
       result = hedy.transpile("abc felienne 123", self.level)
     self.assertEqual('Invalid', str(context.exception))
 
-  def test_transpile_incomplete(self):
-    with self.assertRaises(Exception) as context:
-      result = hedy.transpile("print", self.level)
-    self.assertEqual('Incomplete', str(context.exception))
-    self.assertEqual('print', str(context.exception.arguments['incomplete_command']))
+  def test_print_without_argument_upto_22(self):
+    max_level = 22
+    for level in range(self.level, max_level + 1):
+      code = "print"
+      with self.assertRaises(Exception) as context:
+        result = hedy.transpile(code, level)
+      self.assertEqual('Incomplete', str(context.exception))
+      print(f'{self.test_name()} level {level}')
 
   def test_transpile_incomplete_on_line_2(self):
     with self.assertRaises(Exception) as context:
@@ -202,13 +205,13 @@ def test_transpile_three_lines(self):
     self.assertEqual(expected, result.code)
     self.assertEqual(False, result.has_turtle)
 
-
   def test_transpile_echo_without_argument(self):
     result = hedy.transpile("ask wat?\necho", self.level)
     expected = "answer = input('wat?')\nprint(answer)"
     self.assertEqual(expected, result.code)
     self.assertEqual(False, result.has_turtle)
 
+
   def test_use_quotes_in_print_allowed(self):
     code = "print 'Welcome to OceanView!'"
     result = hedy.transpile(code, self.level)
diff --git a/tests/tests_level_02.py b/tests/tests_level_02.py
--- a/tests/tests_level_02.py
+++ b/tests/tests_level_02.py
@@ -4,6 +4,7 @@
 import io
 from contextlib import contextmanager
 import textwrap
+import inspect
 
 @contextmanager
 def captured_output():
@@ -24,6 +25,8 @@ def run_code(parse_result):
 
 class TestsLevel2(unittest.TestCase):
   level = 2
+  def test_name(self):
+    return inspect.stack()[1][3]
 
   # some commands should not change:
   def test_transpile_other(self):
@@ -31,6 +34,16 @@ def test_transpile_other(self):
       result = hedy.transpile("abc felienne 123", self.level)
     self.assertEqual('Invalid', str(context.exception))
 
+  def test_ask_without_argument_upto_22(self):
+    max_level = 10
+    for level in range(self.level, max_level + 1):
+      code = "name is ask"
+      with self.assertRaises(Exception) as context:
+        result = hedy.transpile(code, level)
+      self.assertEqual('Incomplete', str(context.exception))
+      print(f'{self.test_name()} level {level}')
+
+
   def test_transpile_echo_at_level_2(self):
     code = textwrap.dedent("""\
     ask what is jouw lievelingskleur?