Browse Source

fix: Try supports now otherwise blocks

Matthias Ladkau 6 months ago
parent
commit
4a29d46f3a

+ 5 - 1
ecal-support/syntaxes/ecal.tmLanguage.json

@@ -22,6 +22,10 @@
           "name": "keyword.control.import.ecal",
           "match": "\\b(import|as)\\b"
         },
+        {
+          "name": "keyword.control.let.ecal",
+          "match": "\\b(let)\\b"
+        },
         {
           "name": "keyword.control.sink.ecal",
           "match": "\\b(sink|kindmatch|scopematch|statematch|priority|suppresses)\\b"
@@ -56,7 +60,7 @@
         },
         {
           "name": "keyword.control.try.ecal",
-          "match": "\\b(try|except|finally)\\b"
+          "match": "\\b(try|except|otherwise|finally)\\b"
         }
       ]
     },

+ 5 - 1
ecal.md

@@ -345,12 +345,16 @@ if a == 1 {
 
 Try-except blocks
 --
-ECAL uses try-except blocks to handle error states. Errors can either happen while executing statements or explicitly by using the `raise` function:
+ECAL uses try-except blocks to handle error states. Errors can either happen while executing statements or explicitly by using the `raise` function. Code which should only be executed if no errors happened can be put into an `otherwise` block. Code which should be executed regardless can be put into a `finally` block.
 ```
 try {
     raise("MyError", "My error message", [1,2,3])
 } except "MyError" as e {
     log(e)
+} otherwise {
+    log("No error happened")
+} finally {
+    log("Try block was left")
 }
 ```
 The variable `e` has the following structure:

+ 4 - 3
interpreter/provider.go

@@ -128,9 +128,10 @@ var providerMap = map[string]ecalRuntimeNew{
 
 	// Try statement
 
-	parser.NodeTRY:     tryRuntimeInst,
-	parser.NodeEXCEPT:  voidRuntimeInst,
-	parser.NodeFINALLY: voidRuntimeInst,
+	parser.NodeTRY:       tryRuntimeInst,
+	parser.NodeEXCEPT:    voidRuntimeInst,
+	parser.NodeOTHERWISE: voidRuntimeInst,
+	parser.NodeFINALLY:   voidRuntimeInst,
 
 	// Mutex block
 

+ 12 - 0
interpreter/rt_statements.go

@@ -567,6 +567,18 @@ func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint6
 					}
 				}
 			}
+
+		} else {
+
+			// Evaluate otherwise clause
+
+			for i := 1; i < len(rt.node.Children); i++ {
+				if child := rt.node.Children[i]; child.Name == parser.NodeOTHERWISE {
+					ovs := vs.NewChild(scope.NameFromASTNode(child))
+					_, err = child.Children[0].Runtime.Eval(ovs, is, tid)
+					break
+				}
+			}
 		}
 	}
 

+ 21 - 0
interpreter/rt_statements_test.go

@@ -1097,6 +1097,27 @@ error: {
 		return
 	}
 
+	_, err = UnitTestEval(
+		`
+try {
+  x := 1
+} except e {
+  raise("usererror", "This did not work", e)
+} otherwise {
+  log("all good")
+}
+`, vs)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if testlogger.String() != `
+all good`[1:] {
+		t.Error("Unexpected result:", testlogger.String())
+		return
+	}
 }
 
 func TestMutexStatements(t *testing.T) {

+ 6 - 4
parser/const.go

@@ -184,6 +184,7 @@ const (
 
 	TokenTRY
 	TokenEXCEPT
+	TokenOTHERWISE
 	TokenFINALLY
 
 	// Mutex block
@@ -298,10 +299,11 @@ const (
 
 	// Try block
 
-	NodeTRY     = "try"
-	NodeEXCEPT  = "except"
-	NodeAS      = "as"
-	NodeFINALLY = "finally"
+	NodeTRY       = "try"
+	NodeEXCEPT    = "except"
+	NodeAS        = "as"
+	NodeOTHERWISE = "otherwise"
+	NodeFINALLY   = "finally"
 
 	// Mutex block
 

+ 4 - 3
parser/lexer.go

@@ -248,9 +248,10 @@ var KeywordMap = map[string]LexTokenID{
 
 	// Try block
 
-	"try":     TokenTRY,
-	"except":  TokenEXCEPT,
-	"finally": TokenFINALLY,
+	"try":       TokenTRY,
+	"except":    TokenEXCEPT,
+	"otherwise": TokenOTHERWISE,
+	"finally":   TokenFINALLY,
 
 	// Mutex block
 

+ 20 - 3
parser/parser.go

@@ -136,9 +136,10 @@ func init() {
 
 		// Try statement
 
-		TokenTRY:     {NodeTRY, nil, nil, nil, nil, 0, ndTry, nil},
-		TokenEXCEPT:  {NodeEXCEPT, nil, nil, nil, nil, 0, nil, nil},
-		TokenFINALLY: {NodeFINALLY, nil, nil, nil, nil, 0, nil, nil},
+		TokenTRY:       {NodeTRY, nil, nil, nil, nil, 0, ndTry, nil},
+		TokenEXCEPT:    {NodeEXCEPT, nil, nil, nil, nil, 0, nil, nil},
+		TokenOTHERWISE: {NodeOTHERWISE, nil, nil, nil, nil, 0, nil, nil},
+		TokenFINALLY:   {NodeFINALLY, nil, nil, nil, nil, 0, nil, nil},
 
 		// Mutex statement
 
@@ -815,6 +816,22 @@ func ndTry(p *parser, self *ASTNode) (*ASTNode, error) {
 		}
 	}
 
+	return ndOtherwiseFinally(p, try, err)
+}
+
+/*
+ndOtherwiseFinally is used to parse otherwise and finally blocks.
+*/
+func ndOtherwiseFinally(p *parser, try *ASTNode, err error) (*ASTNode, error) {
+
+	if err == nil && p.node.Token.ID == TokenOTHERWISE {
+		otherwise := p.node
+
+		if err = acceptChild(p, try, TokenOTHERWISE); err == nil {
+			_, err = parseInnerStatements(p, otherwise)
+		}
+	}
+
 	if err == nil && p.node.Token.ID == TokenFINALLY {
 		finally := p.node
 

+ 3 - 2
parser/parser_statement_test.go

@@ -131,8 +131,7 @@ try
 	input = `
 try {
 	raise("test", [1,2,3])
-} else {
-	
+} otherwise {
 } finally {
 }
 `
@@ -146,6 +145,8 @@ try
           number: 1
           number: 2
           number: 3
+  otherwise
+    statements
   finally
     statements
 `[1:]

+ 2 - 1
parser/prettyprinter.go

@@ -142,7 +142,8 @@ func init() {
 
 		// TokenTRY - Special case (handled in code)
 		// TokenEXCEPT - Special case (handled in code)
-		NodeFINALLY + "_1": template.Must(template.New(NodeFINALLY).Parse(" finally {\n{{.c1}}}\n")),
+		NodeOTHERWISE + "_1": template.Must(template.New(NodeOTHERWISE).Parse(" otherwise {\n{{.c1}}}")),
+		NodeFINALLY + "_1":   template.Must(template.New(NodeFINALLY).Parse(" finally {\n{{.c1}}}")),
 
 		// Mutex block