Merge pull request #1 from test262-utils/parsing-fixes

Parsing fixes
diff --git a/src/_monkeyYaml.py b/src/_monkeyYaml.py
index 00c18ca..bc6d128 100644
--- a/src/_monkeyYaml.py
+++ b/src/_monkeyYaml.py
@@ -15,12 +15,17 @@
 mYamlStringValue = re.compile(r"^('|\").*\1$")
 
 def load(str):
+    return myReadDict(str.splitlines())[1]
+
+def myReadDict(lines, indent=""):
     dict = None
     key = None
     emptyLines = 0
 
-    lines = str.splitlines()
     while lines:
+        if not lines[0].startswith(indent):
+            break
+
         line = lines.pop(0)
         if myIsAllSpaces(line):
             emptyLines += 1
@@ -32,7 +37,7 @@
                 dict = {}
             key = result.group(1).strip()
             value = result.group(2).strip()
-            (lines, value) = myReadValue(lines, value)
+            (lines, value) = myReadValue(lines, value, indent)
             dict[key] = value
         else:
             if dict and key and key in dict:
@@ -41,17 +46,22 @@
             else:
                 raise Exception("monkeyYaml is confused at " + line)
         emptyLines = 0
-    return dict
+    return lines, dict
 
-def myReadValue(lines, value):
-    if value == ">":
-        (lines, value) = myMultiline(lines, value)
+def myReadValue(lines, value, indent):
+    if value == ">" or value == "|":
+        (lines, value) = myMultiline(lines, value == "|")
         value = value + "\n"
         return (lines, value)
-    if lines and not value and myMaybeList(lines[0]):
-        return myMultilineList(lines, value)
-    else:
-        return lines, myReadOneLine(value)
+    if lines and not value:
+        if myMaybeList(lines[0]):
+            return myMultilineList(lines, value)
+        indentMatch = re.match("(" + indent + r"\s+)", lines[0])
+        if indentMatch:
+            if ":" in lines[0]:
+                return myReadDict(lines, indentMatch.group(1))
+            return myMultiline(lines, False)
+    return lines, myReadOneLine(value)
 
 def myMaybeList(value):
     return mYamlMultilineList.match(value)
@@ -99,20 +109,35 @@
     values = result.group(1).split(",")
     return [myReadOneLine(v.strip()) for v in values]
 
-def myMultiline(lines, value):
+def myMultiline(lines, preserveNewlines=False):
     # assume no explcit indentor (otherwise have to parse value)
-    value = []
+    value = ""
     indent = myLeadingSpaces(lines[0])
+    wasEmpty = None
+
     while lines:
         line = lines.pop(0)
-        if myIsAllSpaces(line):
-            value += ["\n"]
+        isEmpty = myIsAllSpaces(line)
+
+        if isEmpty:
+            if preserveNewlines:
+                value += "\n"
         elif myLeadingSpaces(line) < indent:
             lines.insert(0, line)
             break;
         else:
-            value += [line[(indent):]]
-    value = " ".join(value)
+            if preserveNewlines:
+                if wasEmpty != None:
+                    value += "\n"
+            else:
+                if wasEmpty == False:
+                    value += " "
+                elif wasEmpty == True:
+                    value += "\n"
+            value += line[(indent):]
+
+        wasEmpty = isEmpty
+
     return (lines, value)
 
 def myIsAllSpaces(line):
diff --git a/src/test262.py b/src/test262.py
index d5a5aa1..c92e5bf 100755
--- a/src/test262.py
+++ b/src/test262.py
@@ -146,7 +146,7 @@
     if self.HasUnexpectedOutcome():
       if self.case.IsNegative():
         print "=== %s was expected to fail in %s, but didn't ===" % (name, mode)
-        print "--- expected error: %s ---\n" % self.case.GetNegative()
+        print "--- expected error: %s ---\n" % self.case.GetNegativeType()
       else:
         if long_format:
           print "=== %s failed in %s ===" % (name, mode)
@@ -246,11 +246,21 @@
     self.validate()
 
   def NegativeMatch(self, stderr):
-    neg = re.compile(self.GetNegative())
+    neg = re.compile(self.GetNegativeType())
     return re.search(neg, stderr)
 
   def GetNegative(self):
-    return self.testRecord['negative']
+    if not self.IsNegative():
+        return None
+    return self.testRecord["negative"]
+
+  def GetNegativeType(self):
+    negative = self.GetNegative()
+    return negative and negative["type"]
+
+  def GetNegativePhase(self):
+    negative = self.GetNegative()
+    return negative and negative["phase"]
 
   def GetName(self):
     return path.join(*self.name)
@@ -304,6 +314,10 @@
         self.GetAdditionalIncludes() + \
         self.test + '\n'
 
+    if self.GetNegativePhase() == "early":
+        source = ("throw 'Expected an early error, but code was executed.';\n" +
+            source)
+
     if self.strict_mode:
       source = '"use strict";\nvar strict_mode = true;\n' + source
     else:
@@ -363,6 +377,10 @@
 
   def validate(self):
     flags = self.testRecord.get("flags")
+    phase = self.GetNegativePhase()
+
+    if phase not in [None, "early", "runtime"]:
+        raise TypeError("Invalid value for negative phase: " + phase)
 
     if not flags:
         return
@@ -578,7 +596,7 @@
     if result.HasUnexpectedOutcome():
       if result.case.IsNegative():
           self.logf.write("=== %s was expected to fail in %s, but didn't === \n" % (name, mode))
-          self.logf.write("--- expected error: %s ---\n" % result.case.GetNegative())
+          self.logf.write("--- expected error: %s ---\n" % result.case.GetNegativeType())
           result.WriteOutput(self.logf)
       else:
           self.logf.write("=== %s failed in %s === \n" % (name, mode))
diff --git a/test/fixtures/negative.js b/test/fixtures/negative.js
new file mode 100644
index 0000000..f772b2e
--- /dev/null
+++ b/test/fixtures/negative.js
@@ -0,0 +1,11 @@
+// fake copyright comment
+/*---
+info: >
+    Sample test info
+description: Sample test description
+negative:
+  phase: early
+  type: SyntaxError
+---*/
+
+???
diff --git a/test/test_monkeyYaml.py b/test/test_monkeyYaml.py
index 94b9b99..428e45b 100644
--- a/test/test_monkeyYaml.py
+++ b/test/test_monkeyYaml.py
@@ -49,24 +49,21 @@
 
     def test_Multiline_2(self):
         lines = [" foo", " bar"]
-        value = ">"
-        y = "\n".join([value] + lines)
-        (lines, value) = monkeyYaml.myMultiline(lines, value)
+        y = "\n".join([">"] + lines)
+        (lines, value) = monkeyYaml.myMultiline(lines)
         self.assertEqual(lines, [])
         self.assertEqual(value, yaml.load(y))
 
     def test_Multiline_3(self):
         lines = ["  foo", "  bar"]
-        value = ">"
-        y = "\n".join([value] + lines)
-        (lines, value) = monkeyYaml.myMultiline(lines, value)
+        y = "\n".join([">"] + lines)
+        (lines, value) = monkeyYaml.myMultiline(lines)
         self.assertEqual(lines, [])
         self.assertEqual(value, yaml.load(y))
 
     def test_Multiline_4(self):
         lines = ["    foo", "    bar", "  other: 42"]
-        value = ">"
-        (lines, value) = monkeyYaml.myMultiline(lines, value)
+        (lines, value) = monkeyYaml.myMultiline(lines)
         self.assertEqual(lines, ["  other: 42"])
         self.assertEqual(value, "foo bar")
 
@@ -169,6 +166,45 @@
 """
         self.assertEqual(monkeyYaml.load(y), yaml.load(y))
 
+    def test_no_folding(self):
+        y = """
+description: |
+  This is text that, naively parsed, would appear
+
+  to: have
+  nested: data
+"""
+        self.assertEqual(monkeyYaml.load(y), yaml.load(y))
+
+    def test_value_multiline(self):
+        y = """
+description:
+  This is a multi-line value
+
+  whose trailing newline should be stripped
+"""
+        self.assertEqual(monkeyYaml.load(y), yaml.load(y))
+
+    def test_nested_1(self):
+        y = """
+es61d: 19.1.2.1
+negative:
+    stage: early
+    type: ReferenceError
+description: foo
+"""
+        self.assertEqual(monkeyYaml.load(y), yaml.load(y))
+
+    def test_nested_2(self):
+        y = """
+es61d: 19.1.2.1
+first:
+    second_a:
+        third: 1
+    second_b: 3
+description: foo
+"""
+        self.assertEqual(monkeyYaml.load(y), yaml.load(y))
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/test/test_parseTestRecord.py b/test/test_parseTestRecord.py
index 422b5af..36576b5 100644
--- a/test/test_parseTestRecord.py
+++ b/test/test_parseTestRecord.py
@@ -171,7 +171,13 @@
 
 """, record['test'])
 
+    def test_negative(self):
+        name = 'fixtures/negative.js'
+        contents = slurpFile(name)
+        record = parseTestRecord(contents, name)
 
+        self.assertEqual('early', record['negative']['phase'])
+        self.assertEqual('SyntaxError', record['negative']['type'])
 
 if __name__ == '__main__':
     unittest.main()