Skip to content

Commit 6c68428

Browse files
authored
Merge pull request #43993 from MaryamZi/natural-programming
Add semantic analysis and negative tests for natural expressions
2 parents c092499 + 7af41c9 commit 6c68428

File tree

38 files changed

+720
-19
lines changed

38 files changed

+720
-19
lines changed

compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BaseVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation;
103103
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard;
104104
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression;
105+
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNaturalExpression;
105106
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral;
106107
import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression;
107108
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction;
@@ -1111,4 +1112,8 @@ public void visit(BLangGroupingKey groupingKeyClause) {
11111112
@Override
11121113
public void visit(BLangReCapturingGroups reCapturingGroups) {
11131114
}
1115+
1116+
@Override
1117+
public void visit(BLangNaturalExpression naturalExpression) {
1118+
}
11141119
}

compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/NodeFinder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard;
102102
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive;
103103
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression;
104+
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNaturalExpression;
104105
import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression;
105106
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction;
106107
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryExpr;
@@ -1578,6 +1579,13 @@ public void visit(BLangCollectContextInvocation invocation) {
15781579
lookupNode(invocation.invocation);
15791580
}
15801581

1582+
@Override
1583+
public void visit(BLangNaturalExpression naturalExpression) {
1584+
lookupNodes(naturalExpression.arguments);
1585+
lookupNodes(naturalExpression.strings);
1586+
lookupNodes(naturalExpression.insertions);
1587+
}
1588+
15811589
private boolean setEnclosingNode(BLangNode node, Location pos) {
15821590
if (PositionUtil.isRangeWithinNode(this.range, pos)
15831591
&& (this.enclosingNode == null

compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/ReferenceFinder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard;
110110
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive;
111111
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression;
112+
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNaturalExpression;
112113
import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression;
113114
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction;
114115
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryExpr;
@@ -1480,6 +1481,13 @@ public void visit(BLangReFlagExpression reFlagExpression) {
14801481
find(reFlagExpression.colon);
14811482
}
14821483

1484+
@Override
1485+
public void visit(BLangNaturalExpression naturalExpression) {
1486+
find(naturalExpression.arguments);
1487+
find(naturalExpression.insertions);
1488+
find(naturalExpression.strings);
1489+
}
1490+
14831491
// Private methods
14841492

14851493
private void findRefsInResourceAccessPathSegments(BLangInvocation.BLangResourceAccessInvocation invocation) {

compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFinder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard;
119119
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive;
120120
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression;
121+
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNaturalExpression;
121122
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral;
122123
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction;
123124
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryExpr;
@@ -1795,6 +1796,13 @@ public void visit(BLangReFlagExpression reFlagExpression) {
17951796
lookupNode(reFlagExpression.colon);
17961797
}
17971798

1799+
@Override
1800+
public void visit(BLangNaturalExpression naturalExpression) {
1801+
lookupNodes(naturalExpression.arguments);
1802+
lookupNodes(naturalExpression.strings);
1803+
lookupNodes(naturalExpression.insertions);
1804+
}
1805+
17981806
private void lookupResourceAccessPathSegments(BLangInvocation.BLangResourceAccessInvocation resourceInvocation) {
17991807
if (resourceInvocation.targetResourceFunc == null) {
18001808
// Return if target-function is not set.

compiler/ballerina-lang/src/main/java/io/ballerina/projects/DocumentContext.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.ballerina.compiler.syntax.tree.SeparatedNodeList;
2424
import io.ballerina.compiler.syntax.tree.SyntaxTree;
2525
import io.ballerina.projects.environment.ModuleLoadRequest;
26+
import io.ballerina.projects.internal.NaturalProgrammingImportAnalyzer;
2627
import io.ballerina.projects.internal.TransactionImportValidator;
2728
import io.ballerina.tools.diagnostics.Diagnostic;
2829
import io.ballerina.tools.text.TextDocument;
@@ -139,19 +140,45 @@ private Set<ModuleLoadRequest> getModuleLoadRequests(ModuleDescriptor currentMod
139140
moduleLoadRequestSet.add(getModuleLoadRequest(importDcl, scope));
140141
}
141142

143+
addTransactionModuleImportIfRequired(currentModuleDesc, scope, modulePartNode, moduleLoadRequestSet);
144+
addNaturalProgrammingModuleImportIfRequired(currentModuleDesc, scope, modulePartNode, moduleLoadRequestSet);
145+
return moduleLoadRequestSet;
146+
}
147+
148+
private static void addTransactionModuleImportIfRequired(ModuleDescriptor currentModuleDesc,
149+
PackageDependencyScope scope,
150+
ModulePartNode modulePartNode,
151+
Set<ModuleLoadRequest> moduleLoadRequestSet) {
142152
// TODO This is a temporary solution for SLP6 release
143153
// TODO Traverse the syntax tree to see whether to import the ballerinai/transaction package or not
144154
TransactionImportValidator trxImportValidator = new TransactionImportValidator();
155+
if (!trxImportValidator.shouldImportTransactionPackage(modulePartNode)) {
156+
return;
157+
}
158+
addModuleLoadRequest(currentModuleDesc, scope, moduleLoadRequestSet, Names.BALLERINA_INTERNAL_ORG.value,
159+
Names.TRANSACTION.value, DependencyResolutionType.PLATFORM_PROVIDED);
160+
}
145161

146-
if (trxImportValidator.shouldImportTransactionPackage(modulePartNode) &&
147-
!currentModuleDesc.name().toString().equals(Names.TRANSACTION.value)) {
148-
String moduleName = Names.TRANSACTION.value;
149-
ModuleLoadRequest ballerinaiLoadReq = new ModuleLoadRequest(
150-
PackageOrg.from(Names.BALLERINA_INTERNAL_ORG.value),
151-
moduleName, scope, DependencyResolutionType.PLATFORM_PROVIDED);
152-
moduleLoadRequestSet.add(ballerinaiLoadReq);
162+
private static void addNaturalProgrammingModuleImportIfRequired(ModuleDescriptor currentModuleDesc,
163+
PackageDependencyScope scope,
164+
ModulePartNode modulePartNode,
165+
Set<ModuleLoadRequest> moduleLoadRequestSet) {
166+
NaturalProgrammingImportAnalyzer naturalProgrammingImportAnalyzer = new NaturalProgrammingImportAnalyzer();
167+
if (!naturalProgrammingImportAnalyzer.shouldImportNaturalProgrammingModule(modulePartNode)) {
168+
return;
169+
}
170+
addModuleLoadRequest(currentModuleDesc, scope, moduleLoadRequestSet, Names.BALLERINA_ORG.value,
171+
Names.NATURAL_PROGRAMMING.value, DependencyResolutionType.PLATFORM_PROVIDED);
172+
}
173+
174+
private static void addModuleLoadRequest(ModuleDescriptor currentModuleDesc, PackageDependencyScope scope,
175+
Set<ModuleLoadRequest> moduleLoadRequestSet, String orgName,
176+
String moduleName, DependencyResolutionType dependencyResolutionType) {
177+
if (!currentModuleDesc.name().toString().equals(moduleName)) {
178+
ModuleLoadRequest moduleLoadRequest = new ModuleLoadRequest(
179+
PackageOrg.from(orgName), moduleName, scope, dependencyResolutionType);
180+
moduleLoadRequestSet.add(moduleLoadRequest);
153181
}
154-
return moduleLoadRequestSet;
155182
}
156183

157184
private ModuleLoadRequest getModuleLoadRequest(ImportDeclarationNode importDcl, PackageDependencyScope scope) {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org).
3+
*
4+
* WSO2 Inc. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package io.ballerina.projects.internal;
20+
21+
import io.ballerina.compiler.syntax.tree.ExternalFunctionBodyNode;
22+
import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
23+
import io.ballerina.compiler.syntax.tree.ModulePartNode;
24+
import io.ballerina.compiler.syntax.tree.NaturalExpressionNode;
25+
import io.ballerina.compiler.syntax.tree.Node;
26+
import io.ballerina.compiler.syntax.tree.NodeVisitor;
27+
import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode;
28+
29+
/**
30+
* Check and add an import for the natural programming module if there is a natural expression.
31+
*
32+
* @since 2201.13.0
33+
*/
34+
public class NaturalProgrammingImportAnalyzer extends NodeVisitor {
35+
36+
private boolean shouldImportNaturalProgrammingModule;
37+
private static final String CODE_ANNOTATION = "code";
38+
39+
public boolean shouldImportNaturalProgrammingModule(ModulePartNode modulePartNode) {
40+
modulePartNode.accept(this);
41+
return this.shouldImportNaturalProgrammingModule;
42+
}
43+
44+
@Override
45+
public void visit(NaturalExpressionNode naturalExpressionNode) {
46+
this.shouldImportNaturalProgrammingModule = true;
47+
}
48+
49+
@Override
50+
public void visit(FunctionDefinitionNode functionDefinitionNode) {
51+
if (this.shouldImportNaturalProgrammingModule) {
52+
return;
53+
}
54+
55+
if (isExternalFunctionWithCodeAnnotation(functionDefinitionNode)) {
56+
this.shouldImportNaturalProgrammingModule = true;
57+
return;
58+
}
59+
60+
super.visit(functionDefinitionNode);
61+
}
62+
63+
private boolean isExternalFunctionWithCodeAnnotation(FunctionDefinitionNode functionDefinitionNode) {
64+
if (!(functionDefinitionNode.functionBody() instanceof ExternalFunctionBodyNode externalFunctionBodyNode)) {
65+
return false;
66+
}
67+
68+
return externalFunctionBodyNode.annotations().stream()
69+
.anyMatch(annotation ->
70+
annotation.annotReference() instanceof SimpleNameReferenceNode annotReference &&
71+
CODE_ANNOTATION.equals(annotReference.name().text()));
72+
}
73+
74+
@Override
75+
protected void visitSyntaxNode(Node node) {
76+
if (this.shouldImportNaturalProgrammingModule) {
77+
return;
78+
}
79+
super.visitSyntaxNode(node);
80+
}
81+
}

compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import org.ballerinalang.model.tree.expressions.MarkdownDocumentationReturnParameterAttributeNode;
9696
import org.ballerinalang.model.tree.expressions.MarkdownDocumentationTextAttributeNode;
9797
import org.ballerinalang.model.tree.expressions.NamedArgNode;
98+
import org.ballerinalang.model.tree.expressions.NaturalExpressionNode;
9899
import org.ballerinalang.model.tree.expressions.QueryExpressionNode;
99100
import org.ballerinalang.model.tree.expressions.RawTemplateLiteralNode;
100101
import org.ballerinalang.model.tree.expressions.ReAssertionNode;
@@ -275,6 +276,7 @@
275276
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard;
276277
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive;
277278
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression;
279+
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNaturalExpression;
278280
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral;
279281
import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression;
280282
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction;
@@ -1191,4 +1193,8 @@ public static InferredTypedescDefaultNode createInferTypedescExpressionNode() {
11911193
public static BLangInvocation.BLangResourceAccessInvocation createResourceAccessInvocation() {
11921194
return new BLangInvocation.BLangResourceAccessInvocation();
11931195
}
1196+
1197+
public static NaturalExpressionNode createNaturalExpressionNode() {
1198+
return new BLangNaturalExpression();
1199+
}
11941200
}

compiler/ballerina-lang/src/main/java/org/ballerinalang/model/tree/NodeKind.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ public enum NodeKind {
151151
REG_EXP_CAPTURING_GROUP,
152152
REG_EXP_FLAG_EXPR,
153153
REG_EXP_FLAGS_ON_OFF,
154+
NATURAL_EXPR,
154155

155156
/* Statements */
156157
ABORT,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org).
3+
*
4+
* WSO2 Inc. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package org.ballerinalang.model.tree.expressions;
20+
21+
import java.util.List;
22+
23+
/**
24+
* Represents a natural expression node.
25+
*
26+
* @since 2201.13.0
27+
*/
28+
public interface NaturalExpressionNode extends ExpressionNode {
29+
30+
List<? extends ExpressionNode> getInsertions();
31+
32+
void addInsertion(ExpressionNode expression);
33+
34+
List<? extends ExpressionNode> getStrings();
35+
36+
void addString(ExpressionNode stringLiteral);
37+
}

compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,17 @@ public enum DiagnosticErrorCode implements DiagnosticCode {
817817
EXPRESSION_OF_FUTURE_TYPE_EXPECTED("BCE4057", "future.expression.expected"),
818818
INSTANTIATION_ERROR("BCE4058", "instantiation.error"),
819819
INVALID_BINDING_PATTERN_IN_ON_FAIL("BCE4059", "invalid.binding.pattern.in.on.fail"),
820-
INVALID_USAGE_OF_CHECK_IN_PARAMETER_DEFAULT("BCE4060", "invalid.usage.of.check.in.parameter.default")
820+
INVALID_USAGE_OF_CHECK_IN_PARAMETER_DEFAULT("BCE4060", "invalid.usage.of.check.in.parameter.default"),
821+
822+
EXPECTED_TYPE_FOR_NATURAL_EXPR_MUST_CONTAIN_ERROR("BCE4070", "expected.type.for.natural.expr.must.contain.error"),
823+
EXPECTED_TYPE_FOR_NATURAL_EXPR_MUST_CONTAIN_A_UNION_OF_NON_ERROR_AND_ERROR(
824+
"BCE4071", "expected.type.for.natural.expr.must.contain.a.union.of.non.error.and.error"),
825+
EXPECTED_TYPE_FOR_NATURAL_EXPR_MUST_BE_A_SUBTYPE_OF_ANYDATA_OR_ERROR(
826+
"BCE4072", "expected.type.for.natural.expr.must.be.a.subtype.of.anydata.or.error"),
827+
CONST_NATURAL_EXPR_CAN_HAVE_ONLY_CONST_EXPR_INSERTION(
828+
"BCE4073", "const.natural.expr.can.have.only.const.expr.insertion"),
829+
EXPECTED_TYPE_FOR_CONST_NATURAL_EXPR_MUST_BE_A_SUBTYPE_OF_ANYDATA(
830+
"BCE4074", "expected.type.for.const.natural.expr.must.be.a.subtype.of.anydata")
821831
;
822832

823833
private final String diagnosticId;

0 commit comments

Comments
 (0)