Skip to content

Commit

Permalink
Added support for an execution stack on the DynamicContext that track…
Browse files Browse the repository at this point in the history
…s the expressions entered and exited during Metapath evaluation. This will be useful context for debugging and error reporting.
  • Loading branch information
david-waltermire committed Jan 4, 2025
1 parent b2e2409 commit 08549d8
Show file tree
Hide file tree
Showing 84 changed files with 258 additions and 225 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@
import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
import gov.nist.secauto.metaschema.core.model.IUriResolver;
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
import gov.nist.secauto.metaschema.core.util.CollectionUtil;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.io.IOException;
import java.net.URI;
import java.time.Clock;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
Expand All @@ -35,8 +40,8 @@

// TODO: add support for in-scope namespaces
/**
* The implementation of a Metapath
* <a href="https://www.w3.org/TR/xpath-31/#eval_context">dynamic context</a>.
* The implementation of a Metapath <a href="https://www.w3.org/TR/xpath-31/#eval_context">dynamic
* context</a>.
*/
public class DynamicContext { // NOPMD - intentional data class
@NonNull
Expand Down Expand Up @@ -82,6 +87,8 @@ private static class SharedState {
private CachingLoader documentLoader;
@NonNull
private final IMutableConfiguration<MetapathEvaluationFeature<?>> configuration;
@NonNull
private final Deque<IExpression> executionStack = new ArrayDeque<>();

public SharedState(@NonNull StaticContext staticContext) {
this.staticContext = staticContext;
Expand All @@ -103,10 +110,9 @@ public SharedState(@NonNull StaticContext staticContext) {
/**
* Generate a new dynamic context that is a copy of this dynamic context.
* <p>
* This method can be used to create a new sub-context where changes can be made
* without affecting this context. This is useful for setting information that
* is only used in a limited evaluation sub-scope, such as for handling variable
* assignment.
* This method can be used to create a new sub-context where changes can be made without affecting
* this context. This is useful for setting information that is only used in a limited evaluation
* sub-scope, such as for handling variable assignment.
*
* @return a new dynamic context
*/
Expand Down Expand Up @@ -146,8 +152,7 @@ public ZonedDateTime getCurrentDateTime() {
}

/**
* Get the mapping of loaded documents from the document URI to the document
* node.
* Get the mapping of loaded documents from the document URI to the document node.
*
* @return the map of document URIs to document nodes
*/
Expand All @@ -162,8 +167,7 @@ public Map<URI, IDocumentNodeItem> getAvailableDocuments() {
*
* @return the loader
* @throws DynamicMetapathException
* with an error code
* {@link DynamicMetapathException#DYNAMIC_CONTEXT_ABSENT} if a
* with an error code {@link DynamicMetapathException#DYNAMIC_CONTEXT_ABSENT} if a
* document loader is not configured for this dynamic context
*/
@NonNull
Expand All @@ -187,12 +191,11 @@ public void setDocumentLoader(@NonNull IDocumentLoader documentLoader) {
}

/**
* Get the cached function call result for evaluating a function that has the
* property {@link FunctionProperty#DETERMINISTIC}.
* Get the cached function call result for evaluating a function that has the property
* {@link FunctionProperty#DETERMINISTIC}.
*
* @param callingContext
* the function calling context information that distinguishes the call
* from any other call
* the function calling context information that distinguishes the call from any other call
* @return the cached result sequence for the function call
*/
@Nullable
Expand All @@ -201,12 +204,10 @@ public ISequence<?> getCachedResult(@NonNull CalledContext callingContext) {
}

/**
* Cache a function call result for a that has the property
* {@link FunctionProperty#DETERMINISTIC}.
* Cache a function call result for a that has the property {@link FunctionProperty#DETERMINISTIC}.
*
* @param callingContext
* the calling context information that distinguishes the call from any
* other call
* the calling context information that distinguishes the call from any other call
* @param result
* the function call result
*/
Expand All @@ -216,12 +217,10 @@ public void cacheResult(@NonNull CalledContext callingContext, @NonNull ISequenc
}

/**
* Used to disable the evaluation of predicate expressions during Metapath
* evaluation.
* Used to disable the evaluation of predicate expressions during Metapath evaluation.
* <p>
* This can be useful for determining the potential targets identified by a
* Metapath expression as a partial evaluation, without evaluating that these
* targets match the predicate.
* This can be useful for determining the potential targets identified by a Metapath expression as a
* partial evaluation, without evaluating that these targets match the predicate.
*
* @return this dynamic context
*/
Expand All @@ -232,8 +231,7 @@ public DynamicContext disablePredicateEvaluation() {
}

/**
* Used to enable the evaluation of predicate expressions during Metapath
* evaluation.
* Used to enable the evaluation of predicate expressions during Metapath evaluation.
* <p>
* This is the default behavior if unchanged.
*
Expand All @@ -256,15 +254,13 @@ public IConfiguration<MetapathEvaluationFeature<?>> getConfiguration() {
}

/**
* Get the sequence value assigned to a let variable with the provided qualified
* name.
* Get the sequence value assigned to a let variable with the provided qualified name.
*
* @param name
* the variable qualified name
* @return the non-null variable value
* @throws MetapathException
* of the variable has not been assigned or if the variable value is
* {@code null}
* of the variable has not been assigned or if the variable value is {@code null}
*/
@NonNull
public ISequence<?> getVariableValue(@NonNull IEnhancedQName name) {
Expand All @@ -289,8 +285,8 @@ public ISequence<?> getVariableValue(@NonNull IEnhancedQName name) {
* the number of arguments in the requested function
* @return the function
* @throws StaticMetapathException
* with the code {@link StaticMetapathException#NO_FUNCTION_MATCH} if
* a matching function was not found
* with the code {@link StaticMetapathException#NO_FUNCTION_MATCH} if a matching function
* was not found
*/
@NonNull
public IFunction getFunction(@NonNull IEnhancedQName name, int arity) {
Expand All @@ -312,6 +308,37 @@ public DynamicContext bindVariableValue(@NonNull IEnhancedQName name, @NonNull I
return this;
}

/**
* Push the current expression under evaluation to the execution queue.
*
* @param expression
* the expression to push
*/
public void pushExecutionStack(@NonNull IExpression expression) {
this.sharedState.executionStack.push(expression);
}

/**
* Pop the expression that was under evaluation from the execution queue.
*
* @param expression
* the expected expression to be popped
*/
public void popExecutionStack(@NonNull IExpression expression) {
IExpression popped = this.sharedState.executionStack.pop();
assert expression.equals(popped);
}

/**
* Return a copy of the current execution stack.
*
* @return the execution stack
*/
@NonNull
public List<IExpression> getExecutionStack() {
return CollectionUtil.unmodifiableList(new ArrayList<>(this.sharedState.executionStack));
}

private class CachingLoader implements IDocumentLoader {
@NonNull
private final IDocumentLoader proxy;
Expand Down Expand Up @@ -351,8 +378,7 @@ public class ContextUriResolver implements IUriResolver {
/**
* {@inheritDoc}
* <p>
* This method first resolves the provided URI against the static context's base
* URI.
* This method first resolves the provided URI against the static context's base URI.
*/
@Override
public URI resolve(URI uri) {
Expand All @@ -370,5 +396,4 @@ public URI resolve(URI uri) {
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
* SPDX-License-Identifier: CC0-1.0
*/

package gov.nist.secauto.metaschema.core.metapath.cst;
package gov.nist.secauto.metaschema.core.metapath;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.ISequence;

Expand Down Expand Up @@ -77,7 +77,7 @@ default Class<? extends IItem> getStaticResultType() {
*/
@SuppressWarnings("null")
@NonNull
default String toASTString() {
default String toCSTString() {
return String.format("%s[]", getClass().getName());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import gov.nist.secauto.metaschema.core.metapath.antlr.ParseTreePrinter;
import gov.nist.secauto.metaschema.core.metapath.cst.BuildCSTVisitor;
import gov.nist.secauto.metaschema.core.metapath.cst.CSTPrinter;
import gov.nist.secauto.metaschema.core.metapath.cst.IExpression;
import gov.nist.secauto.metaschema.core.metapath.cst.path.ContextItem;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
Expand Down Expand Up @@ -163,12 +162,12 @@ public String getPath() {
}

/**
* Get the compiled abstract syntax tree (AST) representation of the Metapath.
* Get the compiled compact syntax tree (CST) representation of the Metapath.
*
* @return the Metapath AST
* @return the Metapath CST
*/
@NonNull
protected IExpression getASTNode() {
protected IExpression getCSTNode() {
return expression;
}

Expand All @@ -179,7 +178,7 @@ public StaticContext getStaticContext() {

@Override
public String toString() {
return CSTPrinter.toString(getASTNode());
return CSTPrinter.toString(getCSTNode());
}

@Override
Expand All @@ -188,7 +187,7 @@ public <T extends IItem> ISequence<T> evaluate(
@Nullable IItem focus,
@NonNull DynamicContext dynamicContext) {
try {
return ObjectUtils.asType(getASTNode().accept(dynamicContext, ISequence.of(focus)).reusable());
return ObjectUtils.asType(getCSTNode().accept(dynamicContext, ISequence.of(focus)).reusable());
} catch (MetapathException ex) {
throw new MetapathException(
String.format("An error occurred while evaluating the expression '%s'. %s",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.IExpression;

import java.util.List;
import java.util.Objects;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.IExpression;
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException;
import gov.nist.secauto.metaschema.core.metapath.antlr.AbstractAstVisitor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.IExpression;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.ISequence;

import edu.umd.cs.findbugs.annotations.NonNull;

/**
Expand Down Expand Up @@ -34,4 +39,28 @@ public String getText() {
public String toString() {
return CSTPrinter.toString(this);
}

@Override
public ISequence<?> accept(DynamicContext dynamicContext, ISequence<?> focus) {
dynamicContext.pushExecutionStack(this);
try {
return evaluate(dynamicContext, focus);
} finally {
dynamicContext.popExecutionStack(this);
}
}

/**
* Evaluate this expression, producing a sequence result.
*
* @param dynamicContext
* the dynamic evaluation context
* @param focus
* the outer focus of the expression
* @return the result of evaluation
*/
@NonNull
protected abstract ISequence<? extends IItem> evaluate(
@NonNull DynamicContext dynamicContext,
@NonNull ISequence<?> focus);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.IExpression;
import gov.nist.secauto.metaschema.core.metapath.cst.items.ArraySequenceConstructor;
import gov.nist.secauto.metaschema.core.metapath.cst.items.ArraySquareConstructor;
import gov.nist.secauto.metaschema.core.metapath.cst.items.DecimalLiteral;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.IExpression;

import java.util.List;
import java.util.Objects;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.IExpression;

import java.util.List;
import java.util.Objects;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.IExpression;
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
import gov.nist.secauto.metaschema.core.metapath.function.impl.AbstractFunction;
Expand Down Expand Up @@ -77,13 +78,13 @@ public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visit
}

@Override
public ISequence<?> accept(DynamicContext dynamicContext, ISequence<?> focus) {
protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
return ISequence.of(getFunction());
}

@SuppressWarnings("null")
@Override
public String toASTString() {
public String toCSTString() {
IFunction function = getFunction();
return String.format("%s[arguments=%s,return=%s]",
getClass().getName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.IExpression;
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
import gov.nist.secauto.metaschema.core.metapath.StaticMetapathException;
import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10;
Expand Down
Loading

0 comments on commit 08549d8

Please sign in to comment.