Browse Source

feat: Adding pretty printer testing

Matthias Ladkau 3 years ago
parent
commit
834f5fffaa

+ 5 - 5
lang/ecal/parser/helper.go

@@ -53,15 +53,15 @@ func (n *ASTNode) instance(p *parser, t *LexToken) *ASTNode {
 Equal checks if this AST data equals another AST data. Returns also a message describing
 Equal checks if this AST data equals another AST data. Returns also a message describing
 what is the found difference.
 what is the found difference.
 */
 */
-func (n *ASTNode) Equals(other *ASTNode) (bool, string) {
-	return n.equalsPath(n.Name, other)
+func (n *ASTNode) Equals(other *ASTNode, ignoreTokenPosition bool) (bool, string) {
+	return n.equalsPath(n.Name, other, ignoreTokenPosition)
 }
 }
 
 
 /*
 /*
 equalsPath checks if this AST data equals another AST data while preserving the search path.
 equalsPath checks if this AST data equals another AST data while preserving the search path.
 Returns also a message describing what is the found difference.
 Returns also a message describing what is the found difference.
 */
 */
-func (n *ASTNode) equalsPath(path string, other *ASTNode) (bool, string) {
+func (n *ASTNode) equalsPath(path string, other *ASTNode, ignoreTokenPosition bool) (bool, string) {
 	var res = true
 	var res = true
 	var msg = ""
 	var msg = ""
 
 
@@ -70,7 +70,7 @@ func (n *ASTNode) equalsPath(path string, other *ASTNode) (bool, string) {
 		msg = fmt.Sprintf("Name is different %v vs %v\n", n.Name, other.Name)
 		msg = fmt.Sprintf("Name is different %v vs %v\n", n.Name, other.Name)
 	}
 	}
 
 
-	if ok, tokenMSG := n.Token.Equals(*other.Token); !ok {
+	if ok, tokenMSG := n.Token.Equals(*other.Token, ignoreTokenPosition); !ok {
 		res = false
 		res = false
 		msg += fmt.Sprintf("Token is different:\n%v\n", tokenMSG)
 		msg += fmt.Sprintf("Token is different:\n%v\n", tokenMSG)
 	}
 	}
@@ -85,7 +85,7 @@ func (n *ASTNode) equalsPath(path string, other *ASTNode) (bool, string) {
 			// Check for different in children
 			// Check for different in children
 
 
 			if ok, childMSG := child.equalsPath(fmt.Sprintf("%v > %v", path, child.Name),
 			if ok, childMSG := child.equalsPath(fmt.Sprintf("%v > %v", path, child.Name),
-				other.Children[i]); !ok {
+				other.Children[i], ignoreTokenPosition); !ok {
 				return ok, childMSG
 				return ok, childMSG
 			}
 			}
 		}
 		}

+ 3 - 3
lang/ecal/parser/helper_test.go

@@ -27,7 +27,7 @@ func TestASTNode(t *testing.T) {
 		return
 		return
 	}
 	}
 
 
-	if ok, msg := n.Equals(n2); ok || msg != `Path to difference: minus > number
+	if ok, msg := n.Equals(n2, false); ok || msg != `Path to difference: minus > number
 
 
 Token is different:
 Token is different:
 Pos is different 2 vs 1
 Pos is different 2 vs 1
@@ -72,7 +72,7 @@ number: 2
 		return
 		return
 	}
 	}
 
 
-	if ok, msg := n.Equals(n2); ok || msg != `Path to difference: minus > number
+	if ok, msg := n.Equals(n2, true); ok || msg != `Path to difference: minus > number
 
 
 Name is different number vs identifier
 Name is different number vs identifier
 Token is different:
 Token is different:
@@ -118,7 +118,7 @@ identifier: a
 		return
 		return
 	}
 	}
 
 
-	if ok, msg := n.Equals(n2); ok || msg != `Path to difference: minus
+	if ok, msg := n.Equals(n2, false); ok || msg != `Path to difference: minus
 
 
 Number of children is different 1 vs 2
 Number of children is different 1 vs 2
 
 

+ 4 - 4
lang/ecal/parser/lexer.go

@@ -39,7 +39,7 @@ type LexToken struct {
 Equal checks if this LexToken equals another LexToken. Returns also a message describing
 Equal checks if this LexToken equals another LexToken. Returns also a message describing
 what is the found difference.
 what is the found difference.
 */
 */
-func (n LexToken) Equals(other LexToken) (bool, string) {
+func (n LexToken) Equals(other LexToken, ignorePosition bool) (bool, string) {
 	var res = true
 	var res = true
 	var msg = ""
 	var msg = ""
 
 
@@ -48,7 +48,7 @@ func (n LexToken) Equals(other LexToken) (bool, string) {
 		msg += fmt.Sprintf("ID is different %v vs %v\n", n.ID, other.ID)
 		msg += fmt.Sprintf("ID is different %v vs %v\n", n.ID, other.ID)
 	}
 	}
 
 
-	if n.Pos != other.Pos {
+	if !ignorePosition && n.Pos != other.Pos {
 		res = false
 		res = false
 		msg += fmt.Sprintf("Pos is different %v vs %v\n", n.Pos, other.Pos)
 		msg += fmt.Sprintf("Pos is different %v vs %v\n", n.Pos, other.Pos)
 	}
 	}
@@ -63,12 +63,12 @@ func (n LexToken) Equals(other LexToken) (bool, string) {
 		msg += fmt.Sprintf("Identifier is different %v vs %v\n", n.Identifier, other.Identifier)
 		msg += fmt.Sprintf("Identifier is different %v vs %v\n", n.Identifier, other.Identifier)
 	}
 	}
 
 
-	if n.Lline != other.Lline {
+	if !ignorePosition && n.Lline != other.Lline {
 		res = false
 		res = false
 		msg += fmt.Sprintf("Lline is different %v vs %v\n", n.Lline, other.Lline)
 		msg += fmt.Sprintf("Lline is different %v vs %v\n", n.Lline, other.Lline)
 	}
 	}
 
 
-	if n.Lpos != other.Lpos {
+	if !ignorePosition && n.Lpos != other.Lpos {
 		res = false
 		res = false
 		msg += fmt.Sprintf("Lpos is different %v vs %v\n", n.Lpos, other.Lpos)
 		msg += fmt.Sprintf("Lpos is different %v vs %v\n", n.Lpos, other.Lpos)
 	}
 	}

+ 1 - 1
lang/ecal/parser/lexer_test.go

@@ -62,7 +62,7 @@ func TestNextItem(t *testing.T) {
 func TestEquals(t *testing.T) {
 func TestEquals(t *testing.T) {
 	l := LexToList("mytest", "not\n test")
 	l := LexToList("mytest", "not\n test")
 
 
-	if ok, msg := l[0].Equals(l[1]); ok || msg != `ID is different 46 vs 6
+	if ok, msg := l[0].Equals(l[1], false); ok || msg != `ID is different 46 vs 6
 Pos is different 0 vs 5
 Pos is different 0 vs 5
 Val is different not vs test
 Val is different not vs test
 Identifier is different false vs true
 Identifier is different false vs true

+ 58 - 23
lang/ecal/parser/main_test.go

@@ -52,9 +52,11 @@ var usedNodes = map[string]bool{
 }
 }
 
 
 func UnitTestParse(name string, input string) (*ASTNode, error) {
 func UnitTestParse(name string, input string) (*ASTNode, error) {
-	n, err := ParseWithRuntime(name, input, &DummyRuntimeProvider{})
+	return UnitTestParseWithPPResult(name, input, "")
+}
 
 
-	// TODO Test pretty printing
+func UnitTestParseWithPPResult(name string, input string, expectedPPRes string) (*ASTNode, error) {
+	n, err := ParseWithRuntime(name, input, &DummyRuntimeProvider{})
 
 
 	// Test AST serialization
 	// Test AST serialization
 
 
@@ -75,14 +77,49 @@ func UnitTestParse(name string, input string) (*ASTNode, error) {
 			return nil, fmt.Errorf("Could not create AST from unmarshaled JSON object: %v", err)
 			return nil, fmt.Errorf("Could not create AST from unmarshaled JSON object: %v", err)
 		}
 		}
 
 
-		// String compare the ASTs
-		if ok, msg := n.Equals(unmarshaledAST); !ok {
+		// Compare the ASTs
+
+		if ok, msg := n.Equals(unmarshaledAST, false); !ok {
 			return nil, fmt.Errorf(
 			return nil, fmt.Errorf(
 				"Parsed AST is different from the unmarshaled AST.\n%v\n",
 				"Parsed AST is different from the unmarshaled AST.\n%v\n",
 				msg)
 				msg)
 		}
 		}
 	}
 	}
 
 
+	// Test Pretty printing
+
+	if err == nil {
+
+		ppres, err := PrettyPrint(n)
+		if err != nil {
+			return nil, fmt.Errorf("Error while pretty printing: %v (input: %v)", err, input)
+		}
+
+		if expectedPPRes == "" {
+
+			n2, err := ParseWithRuntime(name, ppres, &DummyRuntimeProvider{})
+			if err != nil {
+				return nil, fmt.Errorf("Error while parsing pretty print result: %v (result: %v)", err, ppres)
+			}
+
+			// Compare the ASTs
+
+			if ok, msg := n.Equals(n2, true); !ok {
+				return nil, fmt.Errorf(
+					"Parsed AST from pretty printer is different from the originally parsed AST."+
+						"\nOriginal input: %v\nPretty printed: %v\nPretty AST: %v\n%v\n",
+					input, ppres, n2, msg)
+			}
+
+		} else if ppres != expectedPPRes {
+
+			return nil, fmt.Errorf("Expected pretty printer result is different:\nExpected "+
+				"result: %v\nActual result: %v\n", expectedPPRes, ppres)
+		}
+
+		markASTNodesAsPrettyPrinted(n)
+	}
+
 	return n, err
 	return n, err
 }
 }
 
 
@@ -91,31 +128,29 @@ func UnitTestParse(name string, input string) (*ASTNode, error) {
 //
 //
 var usedPrettyPrinterNodes = map[string]bool{}
 var usedPrettyPrinterNodes = map[string]bool{}
 
 
-func UnitTestPrettyPrinting(input, astOutput, ppOutput string) error {
-	var visitAST func(*ASTNode)
+func markASTNodesAsPrettyPrinted(n *ASTNode) {
 
 
-	astres, err := ParseWithRuntime("mytest", input, &DummyRuntimeProvider{})
-	if err != nil || fmt.Sprint(astres) != astOutput {
-		return fmt.Errorf("Unexpected parser output:\n%v expected was:\n%v Error: %v", astres, astOutput, err)
-	}
-
-	visitAST = func(n *ASTNode) {
+	// Make the encountered node as used
 
 
-		// Make the encountered node as used
+	numChildren := len(n.Children)
+	if numChildren > 0 {
+		usedPrettyPrinterNodes[fmt.Sprintf("%v_%v", n.Name, numChildren)] = true
+	} else {
+		usedPrettyPrinterNodes[n.Name] = true
+	}
 
 
-		numChildren := len(n.Children)
-		if numChildren > 0 {
-			usedPrettyPrinterNodes[fmt.Sprintf("%v_%v", n.Name, numChildren)] = true
-		} else {
-			usedPrettyPrinterNodes[n.Name] = true
-		}
+	for _, c := range n.Children {
+		markASTNodesAsPrettyPrinted(c)
+	}
+}
 
 
-		for _, c := range n.Children {
-			visitAST(c)
-		}
+func UnitTestPrettyPrinting(input, astOutput, ppOutput string) error {
+	astres, err := ParseWithRuntime("mytest", input, &DummyRuntimeProvider{})
+	if err != nil || fmt.Sprint(astres) != astOutput {
+		return fmt.Errorf("Unexpected parser output:\n%v expected was:\n%v Error: %v", astres, astOutput, err)
 	}
 	}
 
 
-	visitAST(astres)
+	markASTNodesAsPrettyPrinted(astres)
 
 
 	ppres, err := PrettyPrint(astres)
 	ppres, err := PrettyPrint(astres)
 	if err != nil || ppres != ppOutput {
 	if err != nil || ppres != ppOutput {

+ 27 - 3
lang/ecal/parser/parser_main_test.go

@@ -14,6 +14,21 @@ import (
 	"testing"
 	"testing"
 )
 )
 
 
+func TestCommentParsing(t *testing.T) {
+
+	// TODO: Comment parsing
+
+	//	input := `/* This
+	//	is  a comment */ a := 1 + 1 # foo bar`
+	/*
+		if _, err := UnitTestParse("mytest", input); err.Error() !=
+			"Parse error in mytest: Lexical error (invalid syntax while parsing string) (Line:1 Pos:1)" {
+			t.Error(err)
+			return
+		}
+	*/
+}
+
 func TestSimpleExpressionParsing(t *testing.T) {
 func TestSimpleExpressionParsing(t *testing.T) {
 
 
 	// Test error output
 	// Test error output
@@ -115,6 +130,8 @@ plus
 		return
 		return
 	}
 	}
 
 
+	// Test needless brackets
+
 	input = "(a + 1) * (5 / (6 - 2))"
 	input = "(a + 1) * (5 / (6 - 2))"
 	expectedOutput = `
 	expectedOutput = `
 times
 times
@@ -128,7 +145,10 @@ times
       number: 2
       number: 2
 `[1:]
 `[1:]
 
 
-	if res, err := UnitTestParse("mytest", input); err != nil || fmt.Sprint(res) != expectedOutput {
+	// Pretty printer should get rid of the needless brackets
+
+	res, err := UnitTestParseWithPPResult("mytest", input, "(a + 1) * 5 / (6 - 2)")
+	if err != nil || fmt.Sprint(res) != expectedOutput {
 		t.Error("Unexpected parser output:\n", res, "expected was:\n", expectedOutput, "Error:", err)
 		t.Error("Unexpected parser output:\n", res, "expected was:\n", expectedOutput, "Error:", err)
 		return
 		return
 	}
 	}
@@ -156,7 +176,9 @@ or
       identifier: test
       identifier: test
 `[1:]
 `[1:]
 
 
-	if res, err := UnitTestParse("mytest", input); err != nil || fmt.Sprint(res) != expectedOutput {
+	res, err := UnitTestParseWithPPResult("mytest", input, "not (a + 1) * 5 and true == false or not 1 - 5 != test")
+
+	if err != nil || fmt.Sprint(res) != expectedOutput {
 		t.Error("Unexpected parser output:\n", res, "expected was:\n", expectedOutput, "Error:", err)
 		t.Error("Unexpected parser output:\n", res, "expected was:\n", expectedOutput, "Error:", err)
 		return
 		return
 	}
 	}
@@ -188,7 +210,9 @@ or
     number: 10
     number: 10
 `[1:]
 `[1:]
 
 
-	if res, err := UnitTestParse("mytest", input); err != nil || fmt.Sprint(res) != expectedOutput {
+	res, err = UnitTestParseWithPPResult("mytest", input, `a > b or a <= p or b hassuffix "test" or c hasprefix "test" and x < 4 or x >= 10`)
+
+	if err != nil || fmt.Sprint(res) != expectedOutput {
 		t.Error("Unexpected parser output:\n", res, "expected was:\n", expectedOutput, "Error:", err)
 		t.Error("Unexpected parser output:\n", res, "expected was:\n", expectedOutput, "Error:", err)
 		return
 		return
 	}
 	}

+ 3 - 1
lang/ecal/parser/prettyprinter.go

@@ -48,6 +48,7 @@ func init() {
 		*/
 		*/
 		// Arithmetic operators
 		// Arithmetic operators
 
 
+		NodePLUS + "_1":   template.Must(template.New(NodeMINUS).Parse("+{{.c1}}")),
 		NodePLUS + "_2":   template.Must(template.New(NodePLUS).Parse("{{.c1}} + {{.c2}}")),
 		NodePLUS + "_2":   template.Must(template.New(NodePLUS).Parse("{{.c1}} + {{.c2}}")),
 		NodeMINUS + "_1":  template.Must(template.New(NodeMINUS).Parse("-{{.c1}}")),
 		NodeMINUS + "_1":  template.Must(template.New(NodeMINUS).Parse("-{{.c1}}")),
 		NodeMINUS + "_2":  template.Must(template.New(NodeMINUS).Parse("{{.c1}} - {{.c2}}")),
 		NodeMINUS + "_2":  template.Must(template.New(NodeMINUS).Parse("{{.c1}} - {{.c2}}")),
@@ -115,7 +116,8 @@ func PrettyPrint(ast *ASTNode) (string, error) {
 
 
 				if _, ok := bracketPrecedenceMap[child.Name]; ok && ast.binding > child.binding {
 				if _, ok := bracketPrecedenceMap[child.Name]; ok && ast.binding > child.binding {
 
 
-					// Put the expression in brackets if the binding would normally order things differently
+					// Put the expression in brackets iff (if and only if) the binding would
+					// normally order things differently
 
 
 					res = fmt.Sprintf("(%v)", res)
 					res = fmt.Sprintf("(%v)", res)
 				}
 				}