Repository: hedyorg/hedy

Issue:
[BUG] Sample code on level 13 fails
**Describe the bug**
Locally or on alpha, navigate to level 13 (lang en) and execute the sample code. The error "Unexpected end-of-input. Expected one of: * _SPACE * _SPACE" is returned, which is not expected.

**Paste the Hedy code & level**
Level 13
Code:
```
name = ask 'what is your name?'
leeftijd = ask 'what is your age?'
if name is 'Hedy' and age is 2
    print 'You are the real Hedy!'
```

**Add a screenshot (optional)**
![Screenshot 2022-01-03 at 21 09 38](https://user-images.githubusercontent.com/3010723/147970121-905c7498-3dd1-4320-bce1-777cf7ff7f2d.png)


**Expected behavior**
Sample code should not fail. It should work as it does on production.




Discussion:
Hmmmm so weird, the tests should have caught this! I will investigate.
Ha @TiBiBa is the culprit here :D 

The code in fact parses, but something goes wrong in the achievement verifier. I think I can fix this up though.

Resolving patch:
diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -487,8 +487,11 @@ def parse():
         except:
             pass
 
-        if username and ACHIEVEMENTS.verify_run_achievements(username, code, level, response):
-            response['achievements'] = ACHIEVEMENTS.get_earned_achievements()
+        try:
+            if username and ACHIEVEMENTS.verify_run_achievements(username, code, level, response):
+                response['achievements'] = ACHIEVEMENTS.get_earned_achievements()
+        except Exception as E:
+            print(f"error determining achievements for {code} with {E}")
 
     except hedy.exceptions.HedyException as ex:
         traceback.print_exc()
diff --git a/hedy.py b/hedy.py
--- a/hedy.py
+++ b/hedy.py
@@ -837,7 +837,40 @@ def all_commands(input_string, level, lang='en'):
 
     return AllCommands(level).transform(program_root)
 
+class AllPrintArguments(Transformer):
+    def __init__(self, level):
+        self.level = level
+
+    def __default__(self, args, children, meta):
+        leaves = flatten_list_of_lists_to_list(children)
+
+        if args == 'print':
+            return children
+        else:
+            return leaves # 'pop up' the children
+
+    def program(self, args):
+        return flatten_list_of_lists_to_list(args)
+
+    # somehow tokens are not picked up by the default rule so they need their own rule
+    def INT(self, args):
+        return []
+
+    def NAME(self, args):
+        return []
+
+    def NUMBER(self, args):
+        return []
+
+    def text(self, args):
+        return ''.join(args)
+
+
+def all_print_arguments(input_string, level, lang='en'):
+    input_string = process_input_string(input_string, level)
+    program_root = parse_input(input_string, level, lang)
 
+    return AllPrintArguments(level).transform(program_root)
 
 
 @v_args(meta=True)
diff --git a/website/achievements.py b/website/achievements.py
--- a/website/achievements.py
+++ b/website/achievements.py
@@ -174,8 +174,9 @@ def check_programs_submitted(self, amount):
             self.new_achieved.append("deadline_daredevil_III")
 
     def check_code_achievements(self, code, level):
+        commands_in_code = hedy.all_commands(code, level, self.lang)
         if 'trying_is_key' not in self.achieved:
-            for command in set(hedy.all_commands(code, level, self.lang)):
+            for command in set(commands_in_code):
                 if command not in self.commands:
                     self.new_commands.append(command)
         if set(self.commands) == set(hedy.commands_per_level.get(hedy.HEDY_MAX_LEVEL)):
@@ -187,12 +188,12 @@ def check_code_achievements(self, code, level):
         if 'hedy_honor' not in self.achieved and "Hedy" in code:
             self.new_achieved.append("hedy_honor")
         if 'hedy-ious' not in self.achieved:
-            lines = code.splitlines()
-            for line in lines:
-                if "print" in hedy.all_commands(line, level, self.lang):
-                    if lines.count(line) >= 10:
-                        self.new_achieved.append("hedy-ious")
-                        return
+            all_print_arguments = hedy.all_print_arguments(code, level, self.lang)
+            for argument in all_print_arguments:
+                if all_print_arguments.count(argument) >= 10:
+                    self.new_achieved.append("hedy-ious")
+                    break
+
 
     def check_response_achievements(self, code, response):
         if 'ninja_turtle' not in self.achieved and 'has_turtle' in response and response['has_turtle']:


Test patch:
diff --git a/tests/test_level_01.py b/tests/test_level_01.py
--- a/tests/test_level_01.py
+++ b/tests/test_level_01.py
@@ -34,10 +34,14 @@ def test_print(self):
       expected_commands=expected_commands
     )
 
+    self.assertEqual(['Hallo welkom bij Hedy!'], hedy.all_print_arguments(code, self.level))
+
   def test_print_has_no_turtle(self):
-    result = hedy.transpile_inner("print koekoek", self.level)
+    code = "print koekoek"
+    result = hedy.transpile_inner(code, self.level)
     expected = False
     self.assertEqual(expected, result.has_turtle)
+    self.assertEqual(['koekoek'], hedy.all_print_arguments(code, self.level))
 
 
   def test_print_with_comma(self):
@@ -56,6 +60,7 @@ def test_print_multiple_lines(self):
     expected = "print('Hallo welkom bij Hedy')\nprint('Mooi hoor')"
 
     self.single_level_tester(code=code, expected=expected)
+    self.assertEqual(['Hallo welkom bij Hedy', 'Mooi hoor'], hedy.all_print_arguments(code, self.level))
 
 
   def test_print_with_quotes(self):
diff --git a/tests/test_level_13.py b/tests/test_level_13.py
--- a/tests/test_level_13.py
+++ b/tests/test_level_13.py
@@ -38,6 +38,41 @@ def test_and(self):
       expected=expected
     )
 
+  def test_equals(self):
+    code = textwrap.dedent("""\
+    name = ask 'what is your name?'
+    leeftijd = ask 'what is your age?'
+    if name is 'Hedy' and age is 2
+        print 'You are the real Hedy!'""")
+
+    expected = textwrap.dedent("""\
+      name = input(f'what is your name?')
+      try:
+        name = int(name)
+      except ValueError:
+        try:
+          name = float(name)
+        except ValueError:
+          pass
+      leeftijd = input(f'what is your age?')
+      try:
+        leeftijd = int(leeftijd)
+      except ValueError:
+        try:
+          leeftijd = float(leeftijd)
+        except ValueError:
+          pass
+      if str(name) == str('Hedy') and str('age') == str('2'):
+        print(f'You are the real Hedy!')""")
+
+    self.multi_level_tester(
+      code=code,
+      max_level=16,
+      expected=expected,
+      expected_commands=['ask', 'ask', 'if', 'and', 'print']
+    )
+
+
   def test_or(self):
     code = textwrap.dedent("""\
       if 5 is 5 or 4 is 4