-
Tag: @msujew I'm working through the Forward Reference validation portion in this book since I'll need that functionality for my implementation. For reference, the following are examples of illegal forward references that are otherwise valid. // Illegal forward reference
var i = j + 1
var j = 0 // Illegal mutual dependency
var i = j + 1
var j = i + 1 The validator requires a utility class ( Here is the reference Xtext/Xtend implementation. package org.example.expressions
import org.example.expressions.expressions.VariableRef
import org.example.expressions.expressions.Expression
import org.example.expressions.expressions.AbstractElement
import org.example.expressions.expressions.ExpressionsModel
import org.example.expressions.expressions.Variable
import static extension org.eclipse.xtext.EcoreUtil2.*
class ExpressionsModelUtil {
def isVariableDefinedBefore(VariableRef varRef) {
varRef.variablesDefinedBefore.contains(varRef.variable)
}
def variablesDefinedBefore(Expression e) {
e.getContainerOfType(AbstractElement).variablesDefinedBefore
}
def variablesDefinedBefore(AbstractElement containingElement) {
val allElements = (containingElement.eContainer as ExpressionsModel).elements
allElements.subList(0, allElements.indexOf(containingElement)).typeSelect(Variable).toSet
}
} Here is what I've got so far ... which seems to be pretty close ... but I'm somewhat stuck. Before I spend a massive amount of time trying to fix it, I just wanted to make sure this approach was even sound or idiomatic. There's a couple of assumptions regarding Xtext/ECore equivalence I made which may not be valid (i.e. AbstractElement.eContainer() is the same as AbstractElement.$container, getContainerOfType() implementation, etc.). import { AstNode } from "langium";
import { VariableRef, Expression, AbstractElement, ExpressionsModel, ExpressionsAstType } from "../language-server/generated/ast";
import { isExpression, isVariable, isAbstractElement, reflection } from "../language-server/generated/ast";
export class ExpressionsModelUtil {
public isVariableDefinedBefore(varRef: VariableRef): boolean {
this.variablesDefinedBefore(varRef).has(varRef.variable);
return true;
}
public variablesDefinedBefore(e: Expression | AbstractElement) {
// traverse the AST
if (isExpression(e)) {
this.variablesDefinedBefore(getContainerOfType(e, AbstractElement) as Expression | AbstractElement);
}
// collect the variables
else if (isAbstractElement(e)) {
const allElements = (e.$container as ExpressionsModel).elements;
return new Set(allElements
.slice(0, allElements.indexOf(e))
.filter(e => isVariable(e)));
}
}
}
function getContainerOfType(ele: AstNode, type: ExpressionsAstType ): any {
for (let e = ele; e !== null; e = e.$container as AstNode) {
if (reflection.isInstance(e, type)) {
return e;
}
return null;
}
} Grammar (partial)grammar Expressions
entry ExpressionsModel:
elements += AbstractElement*
;
AbstractElement:
Variable | EvalExpression
;
Variable:
'var' name=ID '=' expression=Expression
;
EvalExpression:
'eval' expression=Expression
;
... |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 11 replies
-
Hey @snarkipus, multiple thoughts on this:
const abstractElement = getContainerOfType(e, isAbstractElement); // Through typescript magic, `abstractElement` is automatically typed as `AbstractElement | undefined`
export function isDefinedBefore(from: AstNode, to: AstNode): boolean {
const fromDocument = getDocument(from);
const toDocument = getDocument(to);
// In case a variable from another document is referenced (might be illegal?)
if (fromDocument !== toDocument) {
return false;
}
if (!from.$cstNode || !to.$cstNode) {
return false;
}
const fromPosition = from.$cstNode.offset;
const toPosition = to.$cstNode.offset;
return fromPosition < toPosition;
} You can call that function using your expression in which the reference appears as |
Beta Was this translation helpful? Give feedback.
Hey @snarkipus,
multiple thoughts on this:
getContainerOfType
function in Langium, which is typed a bit better than yours. You can use it like this:getContainerOfType
function actually usesnull
. We don't usenull
usually in Langium, butundefined
instead. They have different semantic meanings (usually), and also behave differently in equality checks ortypeof
checks. See also this.