From 0f9faead23a7116e1ce771531c5904e02145eb2b Mon Sep 17 00:00:00 2001 From: Deepanjan Bhattacharyya Date: Tue, 18 Feb 2020 15:16:16 -0500 Subject: [PATCH 1/7] Add scoping rules for variables Variable scope 1. Global - defined in init.gyro 2. File - defined in any .gyro file 3. For Inline - defined as part of a @for loop innitilizer 4. For Body - defined in the body of a @for loop Scoping rules 1. Global - accessible from every where, cannot be redefined any where 2. File - accessible only from the file, cannot be redefined any where in the file 3. For Inline - accessible from inside the body of the for, cannot be redefined anywhere in the body 4. For Body - accessible in the body of the for loop, cannot be redefined anywhere in the body --- cli/src/main/java/gyro/cli/Gyro.java | 2 +- .../core/control/ForDirectiveProcessor.java | 73 +++++++++++++++++++ .../java/gyro/core/scope/NodeEvaluator.java | 16 ++++ .../main/java/gyro/core/scope/RootScope.java | 30 +++++++- .../src/main/java/gyro/lang/ast/PairNode.java | 16 ++++ .../java/gyro/lang/ast/block/BlockNode.java | 24 ++++++ 6 files changed, 156 insertions(+), 5 deletions(-) diff --git a/cli/src/main/java/gyro/cli/Gyro.java b/cli/src/main/java/gyro/cli/Gyro.java index db82630ed..d3575e78f 100644 --- a/cli/src/main/java/gyro/cli/Gyro.java +++ b/cli/src/main/java/gyro/cli/Gyro.java @@ -70,7 +70,7 @@ public static void main(String[] arguments) { try { Optional.ofNullable(GyroCore.getRootDirectory()) .map(d -> new RootScope(GyroCore.INIT_FILE, new LocalFileBackend(d), null, null)) - .ifPresent(RootScope::load); + .ifPresent(rs -> rs.load(true, true)); gyro.init(Arrays.asList(arguments)); gyro.run(); diff --git a/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java b/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java index 6657fef87..edae88d4d 100644 --- a/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java +++ b/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java @@ -16,15 +16,21 @@ package gyro.core.control; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import gyro.core.GyroException; import gyro.core.Type; import gyro.core.directive.DirectiveProcessor; +import gyro.core.scope.Defer; +import gyro.core.scope.RootScope; import gyro.core.scope.Scope; import gyro.lang.ast.Node; +import gyro.lang.ast.PairNode; +import gyro.lang.ast.block.BlockNode; import gyro.lang.ast.block.DirectiveNode; import gyro.util.CascadingMap; @@ -37,6 +43,8 @@ public void process(Scope scope, DirectiveNode node) { List variables = getArguments(scope, node, String.class); List inArguments = validateOptionArguments(node, "in", 1, 1); + validateScopedVariables(scope, node, variables); + Node inNode = inArguments.get(0); Object in = scope.getRootScope().getEvaluator().visit(inNode, scope); @@ -102,4 +110,69 @@ private void processBody(DirectiveNode node, Scope scope, Map va new Scope(scope, new CascadingMap<>(scope, values))); } + private void validateScopedVariables(Scope scope, DirectiveNode node, List variables) { + RootScope rootScope = scope.getRootScope(); + Set globalScopedVariables = !scope.equals(rootScope) ? new HashSet<>(PairNode.getNodeVariables(scope.getRootScope().getNodes())) : new HashSet<>(); + Set fileScopedVariables = scope.keySet(); + + // variable check + validateVariables(node, variables, fileScopedVariables, globalScopedVariables); + + // body check + validateBody(node, variables, fileScopedVariables, globalScopedVariables); + } + + private void validateVariables(DirectiveNode node, List variables, Set fileScopedVariables, Set globalScopedVariables) { + // duplicate inline variable + String duplicate = BlockNode.validateLocalImmutability(variables); + + if (duplicate != null) { + throw new Defer(node, String.format("duplicate inline variable '%s'!", duplicate)); + } + + validateGlobalAndFileScope(node, variables, fileScopedVariables, globalScopedVariables, false); + } + + private void validateBody(DirectiveNode node, List variables, Set fileScopedVariables, Set globalScopedVariables) { + // duplicate body variable + String duplicate = BlockNode.validateLocalImmutability(node); + + if (duplicate != null) { + throw new Defer(PairNode.getKeyNode(node.getBody(), duplicate), String.format("duplicate for body variable '%s'!", duplicate)); + } + + List bodyVariables = PairNode.getNodeVariables(node.getBody()); + + // inline scoped variable defined as body variable + duplicate = bodyVariables.stream().filter(variables::contains).findFirst().orElse(null); + + if (duplicate != null) { + throw new Defer(PairNode.getKeyNode(node.getBody(), duplicate), String.format("'%s' is already defined inline and cannot be reused!", duplicate)); + } + + validateGlobalAndFileScope(node, bodyVariables, fileScopedVariables, globalScopedVariables, true); + } + + private void validateGlobalAndFileScope( + DirectiveNode node, + List variables, + Set fileScopedVariables, + Set globalScopedVariables, + boolean isBody) { + + // file scoped variable defined as inline/body variable + String duplicate = variables.stream().filter(fileScopedVariables::contains).findFirst().orElse(null); + + if (duplicate != null) { + throw new Defer(isBody ? PairNode.getKeyNode(node.getBody(), duplicate) : node, String.format("'%s' is already defined in the file scope and cannot be reused!", duplicate)); + } + + // global scoped variable defined as inline/body variable + duplicate = variables.stream().filter(globalScopedVariables::contains).findFirst().orElse(null); + + if (duplicate != null) { + throw new Defer(isBody ? PairNode.getKeyNode(node.getBody(), duplicate) : node, String.format("'%s' is already defined in the global scope and cannot be reused!", duplicate)); + } + } + } diff --git a/core/src/main/java/gyro/core/scope/NodeEvaluator.java b/core/src/main/java/gyro/core/scope/NodeEvaluator.java index 7ad73818b..51cdf5924 100644 --- a/core/src/main/java/gyro/core/scope/NodeEvaluator.java +++ b/core/src/main/java/gyro/core/scope/NodeEvaluator.java @@ -421,6 +421,22 @@ public Object visitFile(FileNode node, Scope scope) { fileScopes.add(fileScope); } + String duplicate = BlockNode.validateGlobalImmutability(node, rootScope.getNodes()); + + if (duplicate != null) { + throw new Defer( + PairNode.getKeyNode(node.getBody(), duplicate), + String.format("'%s' is already defined as a global variable and cannot be reused!", duplicate)); + } + + duplicate = BlockNode.validateLocalImmutability(node); + + if (duplicate != null) { + throw new Defer( + PairNode.getKeyNode(node.getBody(), duplicate), + String.format("'%s' is already defined and cannot be reused!", duplicate)); + } + evaluateBody(node.getBody(), fileScope); removeTypeNode(node); return null; diff --git a/core/src/main/java/gyro/core/scope/RootScope.java b/core/src/main/java/gyro/core/scope/RootScope.java index db053e0a6..358a87936 100644 --- a/core/src/main/java/gyro/core/scope/RootScope.java +++ b/core/src/main/java/gyro/core/scope/RootScope.java @@ -84,6 +84,8 @@ import gyro.core.workflow.RestoreRootProcessor; import gyro.core.workflow.UpdateDirectiveProcessor; import gyro.lang.ast.Node; +import gyro.lang.ast.PairNode; +import gyro.lang.ast.block.BlockNode; import gyro.lang.ast.block.FileNode; import gyro.parser.antlr4.GyroParser; import gyro.util.Bug; @@ -275,20 +277,40 @@ public T findResourceById(Class resourceClass, Object id } public List load() { + return load(true, false); + } + + public List load(boolean evaluateBody, boolean validate) { List nodes = new ArrayList<>(); evaluateFile(getFile(), node -> nodes.addAll(node.getBody())); - try { - evaluator.evaluateBody(nodes, this); + if (validate) { + String duplicate = BlockNode.validateLocalImmutability(PairNode.getNodeVariables(nodes)); - } catch (Defer error) { - // Ignore for now since this is reevaluated later. + if (duplicate != null) { + throw new Defer( + PairNode.getKeyNode(nodes, duplicate), + String.format("'%s' is already defined and cannot be reused!", duplicate)); + } + } + + if (evaluateBody) { + try { + evaluator.evaluateBody(nodes, this); + + } catch (Defer error) { + // Ignore for now since this is reevaluated later. + } } return nodes; } + public List getNodes() { + return load(false, false); + } + public void evaluate() { List nodes = load(); Set existingFiles; diff --git a/lang/src/main/java/gyro/lang/ast/PairNode.java b/lang/src/main/java/gyro/lang/ast/PairNode.java index 6a33222c7..a2fcd9950 100644 --- a/lang/src/main/java/gyro/lang/ast/PairNode.java +++ b/lang/src/main/java/gyro/lang/ast/PairNode.java @@ -16,7 +16,11 @@ package gyro.lang.ast; +import java.util.List; +import java.util.stream.Collectors; + import com.google.common.base.Preconditions; +import gyro.lang.ast.value.ValueNode; import gyro.parser.antlr4.GyroParser; public class PairNode extends Node { @@ -51,4 +55,16 @@ public R accept(NodeVisitor visitor, C cont return visitor.visitPair(this, context); } + public static List getNodeVariables(List nodes) { + return nodes.stream() + .filter(o -> o instanceof PairNode) + .map(o -> (String) ((ValueNode) ((PairNode) o).getKey()).getValue()) + .collect(Collectors.toList()); + } + + public static Node getKeyNode(List nodes, String key) { + return nodes.stream().filter(o -> o instanceof PairNode).filter(o -> ((ValueNode) ((PairNode) o).getKey()).getValue().equals( + key)).findFirst().orElse(null); + } + } diff --git a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java index e81cd7ed1..bfd97e51a 100644 --- a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java +++ b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java @@ -16,11 +16,15 @@ package gyro.lang.ast.block; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import gyro.lang.ast.Node; +import gyro.lang.ast.PairNode; import org.antlr.v4.runtime.ParserRuleContext; public abstract class BlockNode extends Node { @@ -37,4 +41,24 @@ public List getBody() { return body; } + public static String validateLocalImmutability(BlockNode blockNode) { + return validateLocalImmutability(PairNode.getNodeVariables(blockNode.getBody())); + } + + public static String validateLocalImmutability(List nodeVariables) { + return nodeVariables.stream() + .filter(e -> Collections.frequency(nodeVariables, e) > 1) + .findFirst().orElse(null); + } + + public static String validateGlobalImmutability(BlockNode blockNode, List globalNode) { + List nodeVariables = PairNode.getNodeVariables(blockNode.getBody()); + + Set globalKeys = new HashSet<>(PairNode.getNodeVariables(globalNode)); + + return nodeVariables.stream() + .filter(globalKeys::contains) + .findFirst().orElse(null); + } + } From aa7f3f9258f2578208c7af74bfac08139685dc2f Mon Sep 17 00:00:00 2001 From: Deepanjan Bhattacharyya Date: Thu, 19 Mar 2020 16:50:03 -0400 Subject: [PATCH 2/7] Generalize code to be part of Node --- .../java/gyro/core/scope/NodeEvaluator.java | 2 +- lang/src/main/java/gyro/lang/ast/Node.java | 19 +++++++++++++++++++ .../src/main/java/gyro/lang/ast/PairNode.java | 17 ----------------- .../java/gyro/lang/ast/block/BlockNode.java | 11 ----------- .../gyro/lang/ast/block/DirectiveNode.java | 3 +++ 5 files changed, 23 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/gyro/core/scope/NodeEvaluator.java b/core/src/main/java/gyro/core/scope/NodeEvaluator.java index 63e7fcd11..47a6680e4 100644 --- a/core/src/main/java/gyro/core/scope/NodeEvaluator.java +++ b/core/src/main/java/gyro/core/scope/NodeEvaluator.java @@ -429,7 +429,7 @@ public Object visitFile(FileNode node, Scope scope) { String.format("'%s' is already defined as a global variable and cannot be reused!", duplicate)); } - duplicate = BlockNode.validateLocalImmutability(node); + duplicate = DirectiveNode.validateLocalImmutability(PairNode.getNodeVariables(node.getBody())); if (duplicate != null) { throw new Defer( diff --git a/lang/src/main/java/gyro/lang/ast/Node.java b/lang/src/main/java/gyro/lang/ast/Node.java index cf8f7d408..94e52c950 100644 --- a/lang/src/main/java/gyro/lang/ast/Node.java +++ b/lang/src/main/java/gyro/lang/ast/Node.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; +import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -195,4 +196,22 @@ public String toString() { return NodePrinter.toString(this); } + public static List getNodeVariables(List nodes) { + return nodes.stream() + .filter(o -> o instanceof PairNode) + .map(o -> (String) ((ValueNode) ((PairNode) o).getKey()).getValue()) + .collect(Collectors.toList()); + } + + public static Node getKeyNode(List nodes, String key) { + return nodes.stream().filter(o -> o instanceof PairNode).filter(o -> ((ValueNode) ((PairNode) o).getKey()).getValue().equals( + key)).findFirst().orElse(null); + } + + public static String validateLocalImmutability(List nodeVariables) { + return nodeVariables.stream() + .filter(e -> Collections.frequency(nodeVariables, e) > 1) + .findFirst().orElse(null); + } + } diff --git a/lang/src/main/java/gyro/lang/ast/PairNode.java b/lang/src/main/java/gyro/lang/ast/PairNode.java index a2fcd9950..5cfc140a2 100644 --- a/lang/src/main/java/gyro/lang/ast/PairNode.java +++ b/lang/src/main/java/gyro/lang/ast/PairNode.java @@ -16,11 +16,7 @@ package gyro.lang.ast; -import java.util.List; -import java.util.stream.Collectors; - import com.google.common.base.Preconditions; -import gyro.lang.ast.value.ValueNode; import gyro.parser.antlr4.GyroParser; public class PairNode extends Node { @@ -54,17 +50,4 @@ public Node getValue() { public R accept(NodeVisitor visitor, C context) throws X { return visitor.visitPair(this, context); } - - public static List getNodeVariables(List nodes) { - return nodes.stream() - .filter(o -> o instanceof PairNode) - .map(o -> (String) ((ValueNode) ((PairNode) o).getKey()).getValue()) - .collect(Collectors.toList()); - } - - public static Node getKeyNode(List nodes, String key) { - return nodes.stream().filter(o -> o instanceof PairNode).filter(o -> ((ValueNode) ((PairNode) o).getKey()).getValue().equals( - key)).findFirst().orElse(null); - } - } diff --git a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java index bfd97e51a..3f15c4f89 100644 --- a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java +++ b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java @@ -16,7 +16,6 @@ package gyro.lang.ast.block; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -41,16 +40,6 @@ public List getBody() { return body; } - public static String validateLocalImmutability(BlockNode blockNode) { - return validateLocalImmutability(PairNode.getNodeVariables(blockNode.getBody())); - } - - public static String validateLocalImmutability(List nodeVariables) { - return nodeVariables.stream() - .filter(e -> Collections.frequency(nodeVariables, e) > 1) - .findFirst().orElse(null); - } - public static String validateGlobalImmutability(BlockNode blockNode, List globalNode) { List nodeVariables = PairNode.getNodeVariables(blockNode.getBody()); diff --git a/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java b/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java index 9cd9d7b71..378f77162 100644 --- a/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java +++ b/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java @@ -77,4 +77,7 @@ public R accept(NodeVisitor visitor, C cont return visitor.visitDirective(this, context); } + public static String validateLocalImmutability(DirectiveNode node) { + return validateLocalImmutability(Node.getNodeVariables(node.getBody())); + } } From ab6f668ab4c54c8698510a43ff8a302ca4af0241 Mon Sep 17 00:00:00 2001 From: Deepanjan Bhattacharyya Date: Fri, 20 Mar 2020 17:12:57 -0400 Subject: [PATCH 3/7] Refactor validating scoping to a separate file --- .../core/control/ForDirectiveProcessor.java | 78 +---------- .../java/gyro/core/scope/NodeEvaluator.java | 14 +- .../main/java/gyro/core/scope/RootScope.java | 5 +- .../java/gyro/core/scope/ScopingPolice.java | 122 ++++++++++++++++++ lang/src/main/java/gyro/lang/ast/Node.java | 20 --- .../java/gyro/lang/ast/block/BlockNode.java | 14 -- .../gyro/lang/ast/block/DirectiveNode.java | 4 - 7 files changed, 134 insertions(+), 123 deletions(-) create mode 100644 core/src/main/java/gyro/core/scope/ScopingPolice.java diff --git a/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java b/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java index ee814fcf2..19a3e35a5 100644 --- a/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java +++ b/core/src/main/java/gyro/core/control/ForDirectiveProcessor.java @@ -26,11 +26,10 @@ import gyro.core.GyroException; import gyro.core.Type; import gyro.core.directive.DirectiveProcessor; -import gyro.core.scope.Defer; import gyro.core.scope.RootScope; import gyro.core.scope.Scope; +import gyro.core.scope.ScopingPolice; import gyro.lang.ast.Node; -import gyro.lang.ast.PairNode; import gyro.lang.ast.block.DirectiveNode; import gyro.util.CascadingMap; @@ -113,84 +112,15 @@ private void processBody(DirectiveNode node, Scope scope, Map va private void validateScopedVariables(Scope scope, DirectiveNode node, List variables) { RootScope rootScope = scope.getRootScope(); Set globalScopedVariables = !scope.equals(rootScope) - ? new HashSet<>(PairNode.getNodeVariables(scope.getRootScope().getNodes())) + ? new HashSet<>(ScopingPolice.getNodeVariables(scope.getRootScope().getNodes())) : new HashSet<>(); Set fileScopedVariables = scope.keySet(); // variable check - validateVariables(node, variables, fileScopedVariables, globalScopedVariables); + ScopingPolice.validateVariables(node, node.getBody(), variables, fileScopedVariables, globalScopedVariables); // body check - validateBody(node, variables, fileScopedVariables, globalScopedVariables); - } - - private void validateVariables( - DirectiveNode node, - List variables, - Set fileScopedVariables, - Set globalScopedVariables) { - // duplicate inline variable - String duplicate = DirectiveNode.validateLocalImmutability(variables); - - if (duplicate != null) { - throw new Defer(node, String.format("duplicate inline variable '%s'!", duplicate)); - } - - validateGlobalAndFileScope(node, variables, fileScopedVariables, globalScopedVariables, false); - } - - private void validateBody( - DirectiveNode node, - List variables, - Set fileScopedVariables, - Set globalScopedVariables) { - // duplicate body variable - String duplicate = DirectiveNode.validateLocalImmutability(node); - - if (duplicate != null) { - throw new Defer( - PairNode.getKeyNode(node.getBody(), duplicate), - String.format("duplicate for body variable '%s'!", duplicate)); - } - - List bodyVariables = PairNode.getNodeVariables(node.getBody()); - - // inline scoped variable defined as body variable - duplicate = bodyVariables.stream().filter(variables::contains).findFirst().orElse(null); - - if (duplicate != null) { - throw new Defer( - PairNode.getKeyNode(node.getBody(), duplicate), - String.format("'%s' is already defined inline and cannot be reused!", duplicate)); - } - - validateGlobalAndFileScope(node, bodyVariables, fileScopedVariables, globalScopedVariables, true); - } - - private void validateGlobalAndFileScope( - DirectiveNode node, - List variables, - Set fileScopedVariables, - Set globalScopedVariables, - boolean isBody) { - - // file scoped variable defined as inline/body variable - String duplicate = variables.stream().filter(fileScopedVariables::contains).findFirst().orElse(null); - - if (duplicate != null) { - throw new Defer( - isBody ? PairNode.getKeyNode(node.getBody(), duplicate) : node, - String.format("'%s' is already defined in the file scope and cannot be reused!", duplicate)); - } - - // global scoped variable defined as inline/body variable - duplicate = variables.stream().filter(globalScopedVariables::contains).findFirst().orElse(null); - - if (duplicate != null) { - throw new Defer( - isBody ? PairNode.getKeyNode(node.getBody(), duplicate) : node, - String.format("'%s' is already defined in the global scope and cannot be reused!", duplicate)); - } + ScopingPolice.validateBody(node, node.getBody(), variables, fileScopedVariables, globalScopedVariables); } } diff --git a/core/src/main/java/gyro/core/scope/NodeEvaluator.java b/core/src/main/java/gyro/core/scope/NodeEvaluator.java index 47a6680e4..71778490e 100644 --- a/core/src/main/java/gyro/core/scope/NodeEvaluator.java +++ b/core/src/main/java/gyro/core/scope/NodeEvaluator.java @@ -80,9 +80,6 @@ public class NodeEvaluator implements NodeVisitor { - private Map> typeNodes; - private List body; - private static final LoadingCache, Class> DIRECTIVE_PROCESSOR_SCOPE_CLASSES = CacheBuilder .newBuilder() .build(new CacheLoader, Class>() { @@ -94,7 +91,6 @@ public Class load(Class directive .getInferredGenericTypeArgumentClass(DirectiveProcessor.class, 0); } }); - private static final Map> BINARY_FUNCTIONS = ImmutableMap.>builder() .put("*", (l, r) -> doArithmetic(l, r, (ld, rd) -> ld * rd, (ll, rl) -> ll * rl)) .put("/", (l, r) -> doArithmetic(l, r, (ld, rd) -> ld / rd, (ll, rl) -> ll / rl)) @@ -110,6 +106,8 @@ public Class load(Class directive .put("and", (l, r) -> test(l) && test(r)) .put("or", (l, r) -> test(l) && test(r)) .build(); + private Map> typeNodes; + private List body; private static Object doArithmetic( Object left, @@ -421,19 +419,19 @@ public Object visitFile(FileNode node, Scope scope) { fileScopes.add(fileScope); } - String duplicate = BlockNode.validateGlobalImmutability(node, rootScope.getNodes()); + String duplicate = ScopingPolice.validateGlobalImmutability(node.getBody(), rootScope.getNodes()); if (duplicate != null) { throw new Defer( - PairNode.getKeyNode(node.getBody(), duplicate), + ScopingPolice.getKeyNode(node.getBody(), duplicate), String.format("'%s' is already defined as a global variable and cannot be reused!", duplicate)); } - duplicate = DirectiveNode.validateLocalImmutability(PairNode.getNodeVariables(node.getBody())); + duplicate = ScopingPolice.validateLocalImmutability(node.getBody()); if (duplicate != null) { throw new Defer( - PairNode.getKeyNode(node.getBody(), duplicate), + ScopingPolice.getKeyNode(node.getBody(), duplicate), String.format("'%s' is already defined and cannot be reused!", duplicate)); } diff --git a/core/src/main/java/gyro/core/scope/RootScope.java b/core/src/main/java/gyro/core/scope/RootScope.java index 8f0c1d0fd..4aa53e720 100644 --- a/core/src/main/java/gyro/core/scope/RootScope.java +++ b/core/src/main/java/gyro/core/scope/RootScope.java @@ -90,7 +90,6 @@ import gyro.core.workflow.RestoreRootProcessor; import gyro.core.workflow.UpdateDirectiveProcessor; import gyro.lang.ast.Node; -import gyro.lang.ast.PairNode; import gyro.lang.ast.block.FileNode; import gyro.parser.antlr4.GyroParser; import gyro.util.Bug; @@ -300,11 +299,11 @@ public List load(boolean evaluateBody, boolean validate) { evaluateFile(getFile(), node -> nodes.addAll(node.getBody())); if (validate) { - String duplicate = Node.validateLocalImmutability(PairNode.getNodeVariables(nodes)); + String duplicate = ScopingPolice.validateLocalImmutability(nodes); if (duplicate != null) { throw new Defer( - PairNode.getKeyNode(nodes, duplicate), + ScopingPolice.getKeyNode(nodes, duplicate), String.format("'%s' is already defined and cannot be reused!", duplicate)); } diff --git a/core/src/main/java/gyro/core/scope/ScopingPolice.java b/core/src/main/java/gyro/core/scope/ScopingPolice.java new file mode 100644 index 000000000..d533074d5 --- /dev/null +++ b/core/src/main/java/gyro/core/scope/ScopingPolice.java @@ -0,0 +1,122 @@ +package gyro.core.scope; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import gyro.lang.ast.Node; +import gyro.lang.ast.PairNode; +import gyro.lang.ast.value.ValueNode; + +public class ScopingPolice { + + public static List getNodeVariables(List nodes) { + return nodes.stream() + .filter(o -> o instanceof PairNode) + .map(o -> (String) ((ValueNode) ((PairNode) o).getKey()).getValue()) + .collect(Collectors.toList()); + } + + public static Node getKeyNode(List nodes, String key) { + return nodes.stream() + .filter(o -> o instanceof PairNode) + .filter(o -> ((ValueNode) ((PairNode) o).getKey()).getValue().equals( + key)) + .findFirst() + .orElse(null); + } + + public static String validateLocalImmutabilityVariables(List nodeVariables) { + return nodeVariables.stream() + .filter(e -> Collections.frequency(nodeVariables, e) > 1) + .findFirst().orElse(null); + } + + public static String validateLocalImmutability(List nodes) { + return validateLocalImmutabilityVariables(getNodeVariables(nodes)); + } + + public static String validateGlobalImmutability(List localNodes, List globalNodes) { + List nodeVariables = getNodeVariables(localNodes); + + Set globalKeys = new HashSet<>(getNodeVariables(globalNodes)); + + return nodeVariables.stream() + .filter(globalKeys::contains) + .findFirst().orElse(null); + } + + public static void validateVariables( + Node node, + List body, + List variables, + Set fileScopedVariables, + Set globalScopedVariables) { + // duplicate inline variable + String duplicate = ScopingPolice.validateLocalImmutabilityVariables(variables); + + if (duplicate != null) { + throw new Defer(node, String.format("duplicate inline variable '%s'!", duplicate)); + } + + validateGlobalAndFileScope(node, body, variables, fileScopedVariables, globalScopedVariables, false); + } + + public static void validateBody( + Node node, + List body, + List variables, + Set fileScopedVariables, + Set globalScopedVariables) { + // duplicate body variable + String duplicate = ScopingPolice.validateLocalImmutability(body); + + if (duplicate != null) { + throw new Defer( + ScopingPolice.getKeyNode(body, duplicate), + String.format("duplicate for body variable '%s'!", duplicate)); + } + + List bodyVariables = ScopingPolice.getNodeVariables(body); + + // inline scoped variable defined as body variable + duplicate = bodyVariables.stream().filter(variables::contains).findFirst().orElse(null); + + if (duplicate != null) { + throw new Defer( + ScopingPolice.getKeyNode(body, duplicate), + String.format("'%s' is already defined inline and cannot be reused!", duplicate)); + } + + validateGlobalAndFileScope(node, body, bodyVariables, fileScopedVariables, globalScopedVariables, true); + } + + public static void validateGlobalAndFileScope( + Node node, + List body, + List variables, + Set fileScopedVariables, + Set globalScopedVariables, + boolean isBody) { + + // file scoped variable defined as inline/body variable + String duplicate = variables.stream().filter(fileScopedVariables::contains).findFirst().orElse(null); + + if (duplicate != null) { + throw new Defer( + isBody ? ScopingPolice.getKeyNode(body, duplicate) : node, + String.format("'%s' is already defined in the file scope and cannot be reused!", duplicate)); + } + + // global scoped variable defined as inline/body variable + duplicate = variables.stream().filter(globalScopedVariables::contains).findFirst().orElse(null); + + if (duplicate != null) { + throw new Defer( + isBody ? ScopingPolice.getKeyNode(body, duplicate) : node, + String.format("'%s' is already defined in the global scope and cannot be reused!", duplicate)); + } + } +} diff --git a/lang/src/main/java/gyro/lang/ast/Node.java b/lang/src/main/java/gyro/lang/ast/Node.java index 94e52c950..79b492d0b 100644 --- a/lang/src/main/java/gyro/lang/ast/Node.java +++ b/lang/src/main/java/gyro/lang/ast/Node.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; -import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -195,23 +194,4 @@ public Node(ParserRuleContext context) { public String toString() { return NodePrinter.toString(this); } - - public static List getNodeVariables(List nodes) { - return nodes.stream() - .filter(o -> o instanceof PairNode) - .map(o -> (String) ((ValueNode) ((PairNode) o).getKey()).getValue()) - .collect(Collectors.toList()); - } - - public static Node getKeyNode(List nodes, String key) { - return nodes.stream().filter(o -> o instanceof PairNode).filter(o -> ((ValueNode) ((PairNode) o).getKey()).getValue().equals( - key)).findFirst().orElse(null); - } - - public static String validateLocalImmutability(List nodeVariables) { - return nodeVariables.stream() - .filter(e -> Collections.frequency(nodeVariables, e) > 1) - .findFirst().orElse(null); - } - } diff --git a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java index 3f15c4f89..2daa05cf2 100644 --- a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java +++ b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java @@ -16,14 +16,11 @@ package gyro.lang.ast.block; -import java.util.HashSet; import java.util.List; -import java.util.Set; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import gyro.lang.ast.Node; -import gyro.lang.ast.PairNode; import org.antlr.v4.runtime.ParserRuleContext; public abstract class BlockNode extends Node { @@ -39,15 +36,4 @@ public BlockNode(ParserRuleContext context, List body) { public List getBody() { return body; } - - public static String validateGlobalImmutability(BlockNode blockNode, List globalNode) { - List nodeVariables = PairNode.getNodeVariables(blockNode.getBody()); - - Set globalKeys = new HashSet<>(PairNode.getNodeVariables(globalNode)); - - return nodeVariables.stream() - .filter(globalKeys::contains) - .findFirst().orElse(null); - } - } diff --git a/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java b/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java index 378f77162..e1abf215c 100644 --- a/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java +++ b/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java @@ -76,8 +76,4 @@ public List getBody() { public R accept(NodeVisitor visitor, C context) throws X { return visitor.visitDirective(this, context); } - - public static String validateLocalImmutability(DirectiveNode node) { - return validateLocalImmutability(Node.getNodeVariables(node.getBody())); - } } From 38cbd24f89b146e8f1cb8a8e701e79c4e2b572d7 Mon Sep 17 00:00:00 2001 From: Deepanjan Bhattacharyya Date: Fri, 20 Mar 2020 17:16:24 -0400 Subject: [PATCH 4/7] Reset files to original --- lang/src/main/java/gyro/lang/ast/Node.java | 1 + lang/src/main/java/gyro/lang/ast/PairNode.java | 1 + lang/src/main/java/gyro/lang/ast/block/BlockNode.java | 1 + lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java | 1 + 4 files changed, 4 insertions(+) diff --git a/lang/src/main/java/gyro/lang/ast/Node.java b/lang/src/main/java/gyro/lang/ast/Node.java index 79b492d0b..cf8f7d408 100644 --- a/lang/src/main/java/gyro/lang/ast/Node.java +++ b/lang/src/main/java/gyro/lang/ast/Node.java @@ -194,4 +194,5 @@ public Node(ParserRuleContext context) { public String toString() { return NodePrinter.toString(this); } + } diff --git a/lang/src/main/java/gyro/lang/ast/PairNode.java b/lang/src/main/java/gyro/lang/ast/PairNode.java index 5cfc140a2..6a33222c7 100644 --- a/lang/src/main/java/gyro/lang/ast/PairNode.java +++ b/lang/src/main/java/gyro/lang/ast/PairNode.java @@ -50,4 +50,5 @@ public Node getValue() { public R accept(NodeVisitor visitor, C context) throws X { return visitor.visitPair(this, context); } + } diff --git a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java index 2daa05cf2..e81cd7ed1 100644 --- a/lang/src/main/java/gyro/lang/ast/block/BlockNode.java +++ b/lang/src/main/java/gyro/lang/ast/block/BlockNode.java @@ -36,4 +36,5 @@ public BlockNode(ParserRuleContext context, List body) { public List getBody() { return body; } + } diff --git a/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java b/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java index e1abf215c..9cd9d7b71 100644 --- a/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java +++ b/lang/src/main/java/gyro/lang/ast/block/DirectiveNode.java @@ -76,4 +76,5 @@ public List getBody() { public R accept(NodeVisitor visitor, C context) throws X { return visitor.visitDirective(this, context); } + } From 7cd133632f861a1035ff3ae40d21a9cf54d974a7 Mon Sep 17 00:00:00 2001 From: Deepanjan Bhattacharyya Date: Mon, 23 Mar 2020 14:52:58 -0400 Subject: [PATCH 5/7] Reactor scope validation methods to throw exceptions Code reusability --- .../java/gyro/core/scope/NodeEvaluator.java | 16 +------ .../main/java/gyro/core/scope/RootScope.java | 9 +--- .../java/gyro/core/scope/ScopingPolice.java | 46 ++++++++++++------- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/gyro/core/scope/NodeEvaluator.java b/core/src/main/java/gyro/core/scope/NodeEvaluator.java index 71778490e..f1839a829 100644 --- a/core/src/main/java/gyro/core/scope/NodeEvaluator.java +++ b/core/src/main/java/gyro/core/scope/NodeEvaluator.java @@ -419,21 +419,9 @@ public Object visitFile(FileNode node, Scope scope) { fileScopes.add(fileScope); } - String duplicate = ScopingPolice.validateGlobalImmutability(node.getBody(), rootScope.getNodes()); + ScopingPolice.validateGlobalImmutability(node.getBody(), rootScope.getNodes()); - if (duplicate != null) { - throw new Defer( - ScopingPolice.getKeyNode(node.getBody(), duplicate), - String.format("'%s' is already defined as a global variable and cannot be reused!", duplicate)); - } - - duplicate = ScopingPolice.validateLocalImmutability(node.getBody()); - - if (duplicate != null) { - throw new Defer( - ScopingPolice.getKeyNode(node.getBody(), duplicate), - String.format("'%s' is already defined and cannot be reused!", duplicate)); - } + ScopingPolice.validateLocalImmutability(node.getBody()); evaluateBody(node.getBody(), fileScope); removeTypeNode(node); diff --git a/core/src/main/java/gyro/core/scope/RootScope.java b/core/src/main/java/gyro/core/scope/RootScope.java index 4aa53e720..fd0f76462 100644 --- a/core/src/main/java/gyro/core/scope/RootScope.java +++ b/core/src/main/java/gyro/core/scope/RootScope.java @@ -299,14 +299,7 @@ public List load(boolean evaluateBody, boolean validate) { evaluateFile(getFile(), node -> nodes.addAll(node.getBody())); if (validate) { - String duplicate = ScopingPolice.validateLocalImmutability(nodes); - - if (duplicate != null) { - throw new Defer( - ScopingPolice.getKeyNode(nodes, duplicate), - String.format("'%s' is already defined and cannot be reused!", duplicate)); - } - + ScopingPolice.validateLocalImmutability(nodes); } List finalNodes = nodes; diff --git a/core/src/main/java/gyro/core/scope/ScopingPolice.java b/core/src/main/java/gyro/core/scope/ScopingPolice.java index d533074d5..6d3be64a4 100644 --- a/core/src/main/java/gyro/core/scope/ScopingPolice.java +++ b/core/src/main/java/gyro/core/scope/ScopingPolice.java @@ -19,13 +19,20 @@ public static List getNodeVariables(List nodes) { .collect(Collectors.toList()); } - public static Node getKeyNode(List nodes, String key) { + public static List getKeyNodes(List nodes, String key) { return nodes.stream() .filter(o -> o instanceof PairNode) .filter(o -> ((ValueNode) ((PairNode) o).getKey()).getValue().equals( - key)) - .findFirst() - .orElse(null); + key)).collect(Collectors.toList()); + } + + public static Node getKeyNode(List nodes, String key) { + return getKeyNode(nodes, key, 0); + } + + public static Node getKeyNode(List nodes, String key, int index) { + List keyNodes = getKeyNodes(nodes, key); + return keyNodes.size() > index ? keyNodes.get(index) : null; } public static String validateLocalImmutabilityVariables(List nodeVariables) { @@ -34,18 +41,30 @@ public static String validateLocalImmutabilityVariables(List nodeVariabl .findFirst().orElse(null); } - public static String validateLocalImmutability(List nodes) { - return validateLocalImmutabilityVariables(getNodeVariables(nodes)); + public static void validateLocalImmutability(List nodes) { + String duplicate = validateLocalImmutabilityVariables(getNodeVariables(nodes)); + + if (duplicate != null) { + throw new Defer( + ScopingPolice.getKeyNode(nodes, duplicate, 1), + String.format("'%s' is already defined and cannot be reused!", duplicate)); + } } - public static String validateGlobalImmutability(List localNodes, List globalNodes) { + public static void validateGlobalImmutability(List localNodes, List globalNodes) { List nodeVariables = getNodeVariables(localNodes); Set globalKeys = new HashSet<>(getNodeVariables(globalNodes)); - return nodeVariables.stream() + String duplicate = nodeVariables.stream() .filter(globalKeys::contains) .findFirst().orElse(null); + + if (duplicate != null) { + throw new Defer( + ScopingPolice.getKeyNode(localNodes, duplicate), + String.format("'%s' is already defined as a global variable and cannot be reused!", duplicate)); + } } public static void validateVariables( @@ -70,19 +89,14 @@ public static void validateBody( List variables, Set fileScopedVariables, Set globalScopedVariables) { - // duplicate body variable - String duplicate = ScopingPolice.validateLocalImmutability(body); - if (duplicate != null) { - throw new Defer( - ScopingPolice.getKeyNode(body, duplicate), - String.format("duplicate for body variable '%s'!", duplicate)); - } + // duplicate body variable + ScopingPolice.validateLocalImmutability(body); List bodyVariables = ScopingPolice.getNodeVariables(body); // inline scoped variable defined as body variable - duplicate = bodyVariables.stream().filter(variables::contains).findFirst().orElse(null); + String duplicate = bodyVariables.stream().filter(variables::contains).findFirst().orElse(null); if (duplicate != null) { throw new Defer( From d41a841c6ba1b328dc34bec957cea28a7d1ed8b3 Mon Sep 17 00:00:00 2001 From: Deepanjan Bhattacharyya Date: Mon, 23 Mar 2020 15:03:28 -0400 Subject: [PATCH 6/7] Hide rootscope loading logic from cli --- cli/src/main/java/gyro/cli/Gyro.java | 2 +- core/src/main/java/gyro/core/scope/RootScope.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/gyro/cli/Gyro.java b/cli/src/main/java/gyro/cli/Gyro.java index b50cc18a2..5364a0799 100644 --- a/cli/src/main/java/gyro/cli/Gyro.java +++ b/cli/src/main/java/gyro/cli/Gyro.java @@ -70,7 +70,7 @@ public static void main(String[] arguments) { try { Optional.ofNullable(GyroCore.getRootDirectory()) .map(d -> new RootScope(GyroCore.INIT_FILE, new LocalFileBackend(d), null, null)) - .ifPresent(rs -> rs.load(true, true)); + .ifPresent(RootScope::load); gyro.init(Arrays.asList(arguments)); gyro.run(); diff --git a/core/src/main/java/gyro/core/scope/RootScope.java b/core/src/main/java/gyro/core/scope/RootScope.java index fd0f76462..b52d195fb 100644 --- a/core/src/main/java/gyro/core/scope/RootScope.java +++ b/core/src/main/java/gyro/core/scope/RootScope.java @@ -290,7 +290,7 @@ public T findResourceById(Class resourceClass, Object id } public List load() { - return load(true, false); + return load(true, true); } public List load(boolean evaluateBody, boolean validate) { @@ -325,7 +325,7 @@ public List getNodes() { } public void evaluate() { - List nodes = load(); + List nodes = load(true, false); Set existingFiles; try (Stream s = list()) { From a2bcc649122cd82c9ae3c0a95944a5b664723001 Mon Sep 17 00:00:00 2001 From: Deepanjan Bhattacharyya Date: Mon, 23 Mar 2020 16:27:08 -0400 Subject: [PATCH 7/7] genralize error message genration --- .../java/gyro/core/scope/ScopingPolice.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/gyro/core/scope/ScopingPolice.java b/core/src/main/java/gyro/core/scope/ScopingPolice.java index 6d3be64a4..2a9f182ae 100644 --- a/core/src/main/java/gyro/core/scope/ScopingPolice.java +++ b/core/src/main/java/gyro/core/scope/ScopingPolice.java @@ -12,6 +12,22 @@ public class ScopingPolice { + private enum SCOPETYPE { + LOCAL, + GLOBAL, + INLINE; + + @Override + public String toString() { + switch (this) { + case LOCAL: return "local"; + case GLOBAL: return "global"; + case INLINE: return "inline"; + default: return super.toString(); + } + } + } + public static List getNodeVariables(List nodes) { return nodes.stream() .filter(o -> o instanceof PairNode) @@ -47,7 +63,7 @@ public static void validateLocalImmutability(List nodes) { if (duplicate != null) { throw new Defer( ScopingPolice.getKeyNode(nodes, duplicate, 1), - String.format("'%s' is already defined and cannot be reused!", duplicate)); + getMessage(SCOPETYPE.LOCAL, duplicate)); } } @@ -63,7 +79,7 @@ public static void validateGlobalImmutability(List localNodes, List if (duplicate != null) { throw new Defer( ScopingPolice.getKeyNode(localNodes, duplicate), - String.format("'%s' is already defined as a global variable and cannot be reused!", duplicate)); + getMessage(SCOPETYPE.GLOBAL, duplicate)); } } @@ -77,7 +93,7 @@ public static void validateVariables( String duplicate = ScopingPolice.validateLocalImmutabilityVariables(variables); if (duplicate != null) { - throw new Defer(node, String.format("duplicate inline variable '%s'!", duplicate)); + throw new Defer(node, getMessage(SCOPETYPE.INLINE, duplicate)); } validateGlobalAndFileScope(node, body, variables, fileScopedVariables, globalScopedVariables, false); @@ -101,7 +117,7 @@ public static void validateBody( if (duplicate != null) { throw new Defer( ScopingPolice.getKeyNode(body, duplicate), - String.format("'%s' is already defined inline and cannot be reused!", duplicate)); + getMessage(SCOPETYPE.INLINE, duplicate)); } validateGlobalAndFileScope(node, body, bodyVariables, fileScopedVariables, globalScopedVariables, true); @@ -121,7 +137,7 @@ public static void validateGlobalAndFileScope( if (duplicate != null) { throw new Defer( isBody ? ScopingPolice.getKeyNode(body, duplicate) : node, - String.format("'%s' is already defined in the file scope and cannot be reused!", duplicate)); + getMessage(SCOPETYPE.LOCAL, duplicate)); } // global scoped variable defined as inline/body variable @@ -130,7 +146,11 @@ public static void validateGlobalAndFileScope( if (duplicate != null) { throw new Defer( isBody ? ScopingPolice.getKeyNode(body, duplicate) : node, - String.format("'%s' is already defined in the global scope and cannot be reused!", duplicate)); + getMessage(SCOPETYPE.GLOBAL, duplicate)); } } + + private static String getMessage(SCOPETYPE scopeType, String variable) { + return String.format("'%s' is already defined as a @|bold %s|@ variable and cannot be reused!", variable, scopeType); + } }