Skip to content

Commit

Permalink
Merge pull request #3 from ballerina-platform/master
Browse files Browse the repository at this point in the history
Sync `master`
  • Loading branch information
dulajdilshan authored Mar 20, 2024
2 parents bb1624a + 6ef939c commit 448bd14
Show file tree
Hide file tree
Showing 20 changed files with 564 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void addAllDiagnostics(Collection<Diagnostic> diagnostics) {
*
* @return All the collected diagnostics.
*/
public Collection<Diagnostic> diagnostics() {
public List<Diagnostic> diagnostics() {
return this.diagnostics;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,20 @@
*/
public class ParserConstants {

public static final String WRAPPER_PREFIX = "__shell_wrapper__";

public static final Set<String> RESTRICTED_FUNCTION_NAMES = Set.of("main", "init", "__java_recall",
"__java_memorize", "__recall_any", "__recall_any_error", "__memorize", "__stmts", "__run");

/**
* Checks if the given function name is restricted.
*
* @param functionName function name to check
* @return <code>true</code> if the function name is restricted. <code>false</code> otherwise
*/
public static boolean isFunctionNameRestricted(String functionName) {
return RESTRICTED_FUNCTION_NAMES.contains(functionName) || functionName.startsWith(WRAPPER_PREFIX);
}

private ParserConstants() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
* Parses the source code line using a trial based method.
Expand All @@ -50,7 +49,7 @@
* @since 2.0.0
*/
public class SerialTreeParser extends TrialTreeParser {
private static final Set<String> RESTRICTED_FUNCTION_NAMES = ParserConstants.RESTRICTED_FUNCTION_NAMES;

private static final String COMMAND_PREFIX = "/";
private final List<TreeParserTrial> nodeParserTrials;

Expand Down Expand Up @@ -103,7 +102,7 @@ public Collection<Node> parseDeclarations(String source) throws TreeParserExcept
ModulePartTrial modulePartTrial = new ModulePartTrial(this);
Collection<Node> nodes = modulePartTrial.parse(source);
List<Node> declarationNodes = new ArrayList<>();
for (Node node:nodes) {
for (Node node : nodes) {
ModulePartNode modulePartNode = (ModulePartNode) node;
modulePartNode.imports().forEach(declarationNodes::add);
modulePartNode.members().stream().filter(this::isModuleDeclarationAllowed)
Expand All @@ -123,7 +122,7 @@ public Collection<Node> parseDeclarations(String source) throws TreeParserExcept
private boolean isModuleDeclarationAllowed(ModuleMemberDeclarationNode declarationNode) {
if (declarationNode instanceof FunctionDefinitionNode) {
String functionName = ((FunctionDefinitionNode) declarationNode).functionName().text();
if (RESTRICTED_FUNCTION_NAMES.contains(functionName)) {
if (ParserConstants.isFunctionNameRestricted(functionName)) {
addWarnDiagnostic("Found '" + functionName + "' function in the declarations.\n" +
"Discarded '" + functionName + "' function without loading.");
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
* Attempts to capture a module member declaration.
Expand All @@ -45,8 +44,6 @@
*/
public class ModuleMemberTrial extends TreeParserTrial {

private static final Set<String> RESTRICTED_FUNCTION_NAMES = ParserConstants.RESTRICTED_FUNCTION_NAMES;

public ModuleMemberTrial(TrialTreeParser parentParser) {
super(parentParser);
}
Expand Down Expand Up @@ -80,7 +77,7 @@ public Collection<Node> parse(String source) throws ParserTrialFailedException {
private void validateModuleDeclaration(ModuleMemberDeclarationNode declarationNode) {
if (declarationNode instanceof FunctionDefinitionNode) {
String functionName = ((FunctionDefinitionNode) declarationNode).functionName().text();
if (RESTRICTED_FUNCTION_NAMES.contains(functionName)) {
if (ParserConstants.isFunctionNameRestricted(functionName)) {
String message = "Function name " + "'" + functionName + "'" + " not allowed in Ballerina Shell.\n";
throw new InvalidMethodException(message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import io.ballerina.compiler.syntax.tree.WhileStatementNode;
import io.ballerina.compiler.syntax.tree.XMLNamespaceDeclarationNode;
import io.ballerina.shell.exceptions.SnippetException;
import io.ballerina.shell.parser.ParserConstants;
import io.ballerina.shell.snippet.SnippetSubKind;
import io.ballerina.shell.snippet.types.ExpressionSnippet;
import io.ballerina.shell.snippet.types.ImportDeclarationSnippet;
Expand All @@ -75,6 +76,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* A factory that will create snippets from given nodes.
Expand Down Expand Up @@ -153,19 +155,38 @@ public List<VariableDeclarationSnippet> createVariableDeclarationSnippets(Node n
qualifiers = NodeFactory.createNodeList(varNode.finalKeyword().get());
}

if (((VariableDeclarationNode) node).initializer().get().kind() == SyntaxKind.QUERY_ACTION) {
Optional<ExpressionNode> optVarInitNode = varNode.initializer();
if (optVarInitNode.isEmpty()) {
assert false : "This line is unreachable as the error is captured before.";
addErrorDiagnostic("Variable declaration without an initializer is not allowed");
return null;
}

ExpressionNode varInitNode = optVarInitNode.get();
SyntaxKind nodeKind = varInitNode.kind();
if (isSupportedAction(nodeKind)) {
TypedBindingPatternNode typedBindingPatternNode = varNode.typedBindingPattern();
TypeDescriptorNode typeDescriptorNode = typedBindingPatternNode.typeDescriptor();
BindingPatternNode bindingPatternNode = typedBindingPatternNode.bindingPattern();
String functionPart = "function() returns ";
String functionBody = varNode.initializer().get().toString();
String functionString = "var " + "f_" + varFunctionCount + " = " + functionPart
+ typeDescriptorNode.toString() + "{" + typeDescriptorNode + bindingPatternNode.toString()
+ " = " + functionBody + ";" + "return " + bindingPatternNode + ";};";
varNode = (VariableDeclarationNode) NodeParser.parseStatement(functionString);
newNode = (VariableDeclarationNode) NodeParser
.parseStatement(typeDescriptorNode + " " + bindingPatternNode
+ " = f_" + varFunctionCount + "();");

// Check if the type descriptor of the variable is 'var' as it is not yet supported.
if (typeDescriptorNode.kind() == SyntaxKind.VAR_TYPE_DESC) {
addErrorDiagnostic("'var' type is not yet supported for actions. Please specify the exact type.");
return null;
}

boolean isCheckAction = nodeKind == SyntaxKind.CHECK_ACTION;
String initAction = varInitNode.toSourceCode();
String functionTypeDesc = (isCheckAction ? "function() returns error|" :
"function() returns ") + typeDescriptorNode;
String functionName = ParserConstants.WRAPPER_PREFIX + varFunctionCount;
String functionVarDecl = String.format("%s %s = %s {%s %s = %s; return %s;};", functionTypeDesc,
functionName, functionTypeDesc, typeDescriptorNode, bindingPatternNode, initAction,
bindingPatternNode);
varNode = (VariableDeclarationNode) NodeParser.parseStatement(functionVarDecl);
newNode = (VariableDeclarationNode) NodeParser.parseStatement(
String.format("%s %s = %s %s();", typeDescriptorNode, bindingPatternNode,
(isCheckAction ? "check " : ""), functionName));
}

varFunctionCount += 1;
Expand Down Expand Up @@ -266,4 +287,25 @@ private boolean containsIsolated(Node node) {

return false;
}

private boolean isSupportedAction(SyntaxKind nodeKind) {
switch (nodeKind) {
case REMOTE_METHOD_CALL_ACTION:
case BRACED_ACTION:
case CHECK_ACTION:
case START_ACTION:
case TRAP_ACTION:
case FLUSH_ACTION:
case ASYNC_SEND_ACTION:
case SYNC_SEND_ACTION:
case RECEIVE_ACTION:
case WAIT_ACTION:
case QUERY_ACTION:
case COMMIT_ACTION:
case CLIENT_RESOURCE_ACCESS_ACTION:
return true;
default:
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ protected void testEvaluate(String fileName) throws BallerinaShellException {

ShellCompilation shellCompilation = evaluator.getCompilation(testCase.getCode());
Optional<PackageCompilation> compilation = shellCompilation.getPackageCompilation();

if (compilation.isEmpty() && !testCase.getStderr().isEmpty()) {
for (int i = 0; i < testCase.getStderr().size(); i++) {
Assert.assertEquals(evaluator.diagnostics().get(i).toString(), testCase.getStderr().get(i));
}
continue;
}

String expr = evaluator.getValue(compilation).get().getResult();
Assert.assertEquals(invoker.getStdOut(), testCase.getStdout(), testCase.getDescription());
Assert.assertEquals(expr, testCase.getExpr(), testCase.getDescription());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2024, WSO2 LLC. (http://wso2.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.ballerina.shell.test.evaluator;

import io.ballerina.shell.exceptions.BallerinaShellException;
import org.testng.annotations.Test;

/**
* Test simple snippets with action invocations.
*
* @since 2201.9.0
*/
public class ActionEvaluatorTest extends AbstractEvaluatorTest {
private static final String START_EVALUATOR_TESTCASE = "testcases/evaluator/actions.start.json";
private static final String REMOTE_EVALUATOR_TESTCASE = "testcases/evaluator/actions.remote.json";
private static final String RESOURCE_EVALUATOR_TESTCASE = "testcases/evaluator/actions.resource.json";
private static final String VAR_EVALUATOR_TESTCASE = "testcases/evaluator/actions.var.json";

@Test
public void testEvaluateStart() throws BallerinaShellException {
testEvaluate(START_EVALUATOR_TESTCASE);
}

@Test
public void testEvaluateRemote() throws BallerinaShellException {
testEvaluate(REMOTE_EVALUATOR_TESTCASE);
}

@Test
public void testEvaluateResource() throws BallerinaShellException {
testEvaluate(RESOURCE_EVALUATOR_TESTCASE);
}

@Test
public void testEvaluateVar() throws BallerinaShellException {
testEvaluate(VAR_EVALUATOR_TESTCASE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

package io.ballerina.shell.test.evaluator.base;

import java.util.ArrayList;
import java.util.List;

/**
* One statement in a evaluator test case.
*
Expand All @@ -29,6 +32,7 @@ public class TestCase {
private String expr;
private String stdout = "";
private String error;
private List<String> stderr = new ArrayList<>();

public String getDescription() {
return description;
Expand Down Expand Up @@ -69,4 +73,12 @@ public String getError() {
public void setError(String error) {
this.error = error;
}

public List<String> getStderr() {
return stderr;
}

public void setStderr(List<String> stderr) {
this.stderr = stderr;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{
"description": "Define a client class",
"code": "client class MyClient { remote function invoke(string param) returns string { return string `remote method invoked with ${param}`; } }"
},
{
"description": "Initialize a client class",
"code": "MyClient myClient = new;"
},
{
"description": "Invoke a client remote method as an expression",
"code": "myClient->invoke(\"input\");",
"expr": "\"remote method invoked with input\""
},
{
"description": "Invoke a client remote method with a variable",
"code": "string returnValue = myClient->invoke(\"input\");"
},
{
"description": "Check the return value",
"code": "returnValue",
"expr": "\"remote method invoked with input\""
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{
"description": "Define a client class",
"code": "client class MyClient { resource function get root/[string path](string param) returns string { return string `${path} -> ${param}`; } }"
},
{
"description": "Initialize a client class",
"code": "MyClient myClient = new;"
},
{
"description": "Invoke a client resource method as an expression",
"code": "myClient ->/root/name(\"input\");",
"expr": "\"name -> input\""
},
{
"description": "Invoke a client resource method with a variable",
"code": "string returnValue = myClient ->/root/name(\"input\");"
},
{
"description": "Check the return value",
"code": "returnValue",
"expr": "\"name -> input\""
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"description": "Initialize the function",
"code": "function name() returns int { int x = 3; x += 3 * 2; return x; }"
},
{
"description": "Execute the function with start as an expression",
"code": "start name()",
"expr": "future {isDone:true,result:9}"
},
{
"description": "Execute the function with start with a variable assignment",
"code": "future<int> futureResult = start name()"
},
{
"description": "Check the return value",
"code": "futureResult",
"expr": "future {isDone:true,result:9}"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"description": "Initialize the function",
"code": "function name() returns int { int x = 3; x += 3 * 2; return x; }"
},
{
"description": "Execute the function with start with a variable assignment",
"code": "var futureResult = start name()",
"stderr": [
"'var' type is not yet supported for actions. Please specify the exact type.",
"Compilation aborted due to invalid input."
]
}
]
Loading

0 comments on commit 448bd14

Please sign in to comment.