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
 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.
 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 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)
 	}
 
-	if ok, tokenMSG := n.Token.Equals(*other.Token); !ok {
+	if ok, tokenMSG := n.Token.Equals(*other.Token, ignoreTokenPosition); !ok {
 		res = false
 		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
 
 			if ok, childMSG := child.equalsPath(fmt.Sprintf("%v > %v", path, child.Name),
-				other.Children[i]); !ok {
+				other.Children[i], ignoreTokenPosition); !ok {
 				return ok, childMSG
 			}
 		}

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

@@ -27,7 +27,7 @@ func TestASTNode(t *testing.T) {
 		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:
 Pos is different 2 vs 1
@@ -72,7 +72,7 @@ number: 2
 		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
 Token is different:
@@ -118,7 +118,7 @@ identifier: a
 		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
 

+ 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
 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 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)
 	}
 
-	if n.Pos != other.Pos {
+	if !ignorePosition && n.Pos != other.Pos {
 		res = false
 		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)
 	}
 
-	if n.Lline != other.Lline {
+	if !ignorePosition && n.Lline != other.Lline {
 		res = false
 		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
 		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) {
 	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
 Val is different not vs test
 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) {
-	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
 
@@ -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)
 		}
 
-		// 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(
 				"Parsed AST is different from the unmarshaled AST.\n%v\n",
 				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
 }
 
@@ -91,31 +128,29 @@ func UnitTestParse(name string, input string) (*ASTNode, error) {
 //
 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)
 	if err != nil || ppres != ppOutput {

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

@@ -14,6 +14,21 @@ import (
 	"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) {
 
 	// Test error output
@@ -115,6 +130,8 @@ plus
 		return
 	}
 
+	// Test needless brackets
+
 	input = "(a + 1) * (5 / (6 - 2))"
 	expectedOutput = `
 times
@@ -128,7 +145,10 @@ times
       number: 2
 `[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)
 		return
 	}
@@ -156,7 +176,9 @@ or
       identifier: test
 `[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)
 		return
 	}
@@ -188,7 +210,9 @@ or
     number: 10
 `[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)
 		return
 	}

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

@@ -48,6 +48,7 @@ func init() {
 		*/
 		// Arithmetic operators
 
+		NodePLUS + "_1":   template.Must(template.New(NodeMINUS).Parse("+{{.c1}}")),
 		NodePLUS + "_2":   template.Must(template.New(NodePLUS).Parse("{{.c1}} + {{.c2}}")),
 		NodeMINUS + "_1":  template.Must(template.New(NodeMINUS).Parse("-{{.c1}}")),
 		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 {
 
-					// 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)
 				}