This is in continuation with the issue mentioned in the forum:
xtext-Couldn't-resolve-reference-to.
The sample model i am using is as below
grammar org.xtext.example.testdsl.TestDsl with org.eclipse.xtext.common.Terminals
generate testDsl "http://www.test.com/test/example/TestDsl"
Model:
prog+=Program*;
Program: g=Greeting de+=DataEntry* s+=Statement*;
Greeting: 'Hello' t=ProgPara '!';
ProgPara: 'PROGRAM' pname=Progname ';';
Progname : name=ID;
DataEntry: a=INT (v=Varname| in=Indexname) ';';
Varname : name = ID;
Statement: c=CopyStmt ';';
CopyStmt: 'COPY' 'TO' qname=[IndexVarname|ID] ;
IndexVarname : (Indexname|Varname);
Indexname : '(' name = ID ')';
Named:Progname|Indexname|Varname;
I have a file which uses similar grammar and this file is pretty big with about 12000 lines of code and 2000 odd Progname or Varname and at each instance it is trying to resolve the type in the scope provider. The grammar has about 326 elements in it.
val candidates1 = EcoreUtil2.getAllContentsOfType(rootElement, Indexname);
val candidates2 = EcoreUtil2.getAllContentsOfType(rootElement, Varname);
val candidates = candidates1 + candidates2;
return Scopes.scopeFor(candidates);
Because of repeated calls to getScope(), the file is taking for ever to open in Editor. Even if it opens any small edit is way too slow. Please suggest how i could resolve this issue.
I tried adding caching support for the above code as below:
class TestDslScopeProvider extends AbstractTestDslScopeProvider {
#Inject
IResourceScopeCache cache;
override getScope(EObject context, EReference reference) {
if (context instanceof CopyStmt) {
if (reference.featureID == TestDslPackage.COPY_STMT__QNAME) {
val candidates = cache.get(
context,
reference.eResource,
[|findQNameCandidates(context, reference)]
);
return Scopes.scopeFor(candidates);
}
}
return super.getScope(context, reference);
}
def findQNameCandidates(EObject context, EReference reference) {
val rootElement = EcoreUtil2.getRootContainer(context);
val candidates1 = EcoreUtil2.getAllContentsOfType(rootElement, Indexname);
val candidates2 = EcoreUtil2.getAllContentsOfType(rootElement, Varname);
return candidates1 + candidates2;
}
}
Even now, it is still very slow. There has been no difference. If i replace the context with reference object as key for the cache, then the speed is fast but then all cross referenced links are lost and I get errors. I am not able to point where i am not doing the right thing.
Thanks,
Anitha
the following works for me
#Inject
IResourceScopeCache cache;
override getScope(EObject context, EReference reference) {
if (context instanceof CopyStmt) {
if (reference.featureID == MyDslPackage.COPY_STMT__QNAME) {
val candidates = cache.get(
"COPY_STMT__QNAME_scope",
context.eResource,
[|findQNameCandidates(context, reference)]
);
return Scopes.scopeFor(candidates);
}
}
return super.getScope(context, reference);
}
def findQNameCandidates(EObject context, EReference reference) {
val rootElement = EcoreUtil2.getRootContainer(context);
val candidates1 = EcoreUtil2.getAllContentsOfType(rootElement, IndexVarname);
return candidates1;
}
Related
fun findError(puzzle: Array<IntArray>): Boolean {
for (z in 0..8) {
val blockNums = mutableListOf<Int>()
val xNums = mutableListOf<Int>()
val yNums = mutableListOf<Int>()
for (index in 0..8) {
xNums.add(puzzle[z][index])
yNums.add(puzzle[index][z])
blockNums.add(puzzle[blocks.xy[z + 1][index]][blocks.xy[z][index]])
if (blockNums.count() != blockNums.toSet().count() ||
yNums.count() != yNums.toSet().count() ||
xNums.count() != xNums.toSet().count()) return false
}
}
return true
}
This function works as desired but the Intellij IDE gives this warning. "Boolean method 'findError' is always inverted". I kind of understand what it means and I know I could suppress it.
I can't figure out how to rewrite the code block to satisfy the error and not change the functionality. Should I just suppress it or is there a more proper way to express this? I'm a beginner that is learning.
public final data class Blocks public constructor(blockNums: kotlin.collections.MutableList<kotlin.Int>, blockNumsFinal: kotlin.collections.MutableSet<kotlin.Int>, xy: kotlin.collections.List<kotlin.collections.List<kotlin.Int>>) {
public final val blockNums: kotlin.collections.MutableList<kotlin.Int> /* compiled code */
public final val blockNumsFinal: kotlin.collections.MutableSet<kotlin.Int> /* compiled code */
public final var xy: kotlin.collections.List<kotlin.collections.List<kotlin.Int>> /* compiled code */
public final operator fun component1(): kotlin.collections.MutableList<kotlin.Int> { /* compiled code */ }
public final operator fun component2(): kotlin.collections.MutableSet<kotlin.Int> { /* compiled code */ }
public final operator fun component3(): kotlin.collections.List<kotlin.collections.List<kotlin.Int>> { /* compiled code */ }
}
I found a way to rewrite the function not using the '!' symbol. This should satisfy the intention of the warning to use positives instead of negatives. It still gives the warning. I think Bas Leijdekkers comment about the inspection may be correct.
fun findError(puzzle: Array<IntArray>): Boolean {
val blockNums = mutableListOf<Int>()
val xNums = mutableListOf<Int>()
val yNums = mutableListOf<Int>()
var counts = 0
for (z in 0..8) {
blockNums.clear()
xNums.clear()
yNums.clear()
for (index in 0..8) {
xNums.add(puzzle[z][index])
yNums.add(puzzle[index][z])
blockNums.add(puzzle[blocks.xy[z + 1][index]][blocks.xy[z][index]])
if (blockNums.count() == blockNums.toSet().count() &&
yNums.count() == yNums.toSet().count() &&
xNums.count() == xNums.toSet().count()) {
counts++
}
}
}
return counts == 81
}
Which line is the IntelliJ warning you about, is it this statement?
if (blockNums.count() != blockNums.toSet().count() ||
yNums.count() != yNums.toSet().count() ||
xNums.count() != xNums.toSet().count()) return false
if so, it is because this is complex and likely difficult for someone other than you to understand. Here's two ideas about how you might make reduce the complexity:
(1)
if (blockNums.size != blockNums.toSet().size) return false
if (yNums.size != yNums.toSet().size) return false
if (xNums.size != xNums.toSet().size) return false
or (2)
val blocksDiffer = (blockNums.size != blockNums.toSet().size)
val yDiffer = (yNums.size != yNums.toSet().size)
val xDiffer = (xNums.size != xNums.toSet().size)
if(blocksDiffer || yDiffer || xDiffer) return false
(There is a small performance penalty with (2) since the program has to compute all 3 evaluations.
I also changed count() to size which are equivalent)
I believe this is IntelliJ trying to warn you that a boolean method result is always inverted after calling. In other words, you only ever use !findError() in your code.
This is an indication that the code could be made more readable by using positive language. JetBrain's justification for this appears to be based on Robert Martin's book Clean Code:
“Negatives are just a bit harder to understand than positives. So, when possible, conditionals should be expressed as positives.”
You can use Refactor -> Invert Boolean... to perform this automatically.
I am new to Motoko and internet computer, when I am working I am having too much difficulties that might look simple, I am having difficulties doing this, posting the link to forum question here
https://forum.dfinity.org/t/how-to-traverse-inner-array-of-a-trie/12941?u=manubodhi
Please help if somebody is well versed in Motoko and Dfinity
I prepared a small code in motoko playground for you in order to see how you can traverse inner array and achieve your goal of filtering Trie. Here is as well saved project in motoko playground: https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/?tag=1150943578
Shortly to filter through inner array you can use:
let trieOfDishes = Trie.filter<DishId, Dish>(dishes, func (k, v) {
Array.find<MealTypeId>(v.mealTypeId, func(x : MealTypeId) { x == mealTypeId }) != null ;
});
Full code of canister implementation:
import Trie "mo:base/Trie";
import Array "mo:base/Array";
import Iter "mo:base/Iter";
import Nat32 "mo:base/Nat32";
actor Dishes {
type DishId = Nat32;
type DishTypeId = Nat32;
type MealTypeId = Nat32;
public type Dish = {
dishId: DishId;
dishTypeId : DishTypeId;
mealTypeId : [MealTypeId]
};
var dishes: Trie.Trie<DishId, Dish> = Trie.empty();
private func key(x : DishId) : Trie.Key<DishId> {
return { hash = x; key = x };
};
public func add_dish(dish: Dish) : async Dish {
dishes := Trie.replace(dishes, key(dish.dishId), Nat32.equal, ?dish).0;
return dish;
};
public query func getDishesByDishId (dishTypeId : DishTypeId) : async [(DishId, Dish)] {
let trieOfDishes = Trie.filter<DishId, Dish>(dishes, func (k, v) { v.dishId == dishTypeId } );
let arrayOfDishes : [(DishId, Dish)] = Iter.toArray(Trie.iter(trieOfDishes));
return arrayOfDishes;
};
public query func getDishesBymealTypeId (mealTypeId : MealTypeId) : async [(DishId, Dish)] {
let trieOfDishes = Trie.filter<DishId, Dish>(dishes, func (k, v) {
Array.find<MealTypeId>(v.mealTypeId, func(x : MealTypeId) { x == mealTypeId }) != null ;
});
let arrayOfDishes : [(DishId, Dish)] = Iter.toArray(Trie.iter(trieOfDishes));
return arrayOfDishes;
};
}
In my data model I have a very simple one-to-many relationship between challenges and it's whitelist items.
I am trying to select a challenge filtered by whitelist. Basically the challenge selection criteria is when the challenge is either does not have any entries in whitelist for itself or the whitelist matches by name.
This can be achieved with quite simple SQL query:
select c.* from challenge c, challenge_whitelist w where (c.id = w."challengeId" and w."userName" = 'testuser') or ((select count(*) where c.id = w."challengeId") = 0);
I am unable to translate it to Exposed though:
// will not compile
fun listAll(userName: String) {
ExposedChallenge.wrapRows(
ChallengeTable.innerJoin(ChallengeWhitelistTable)
.slice(ChallengeTable.columns)
.select((ChallengeWhitelistTable.userName eq userName) or (ChallengeTable.innerJoin(ChallengeWhitelistTable).selectAll().count() eq 0))
).toList()
}
The userName check works correctly but ChallengeTable.innerJoin(ChallengeWhitelistTable).selectAll().count() eq 0) is not qualified as the valid expression (will not compile).
Note that the mappings are super-simple:
object ChallengeTable : IntIdTable() {
val createdAt = datetime("createdAt")
}
class ExposedChallenge(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<ExposedChallenge>(ChallengeTable)
var createdAt by ChallengeTable.createdAt
val whitelist by ExposedChallengeWhitelist referrersOn ChallengeWhitelistTable.challenge
}
object ChallengeWhitelistTable : IntIdTable(name = "challenge_whitelist") {
var userName = varchar("userName", 50)
var challengeId = integer("challengeId")
val challenge = reference("challengeId", ChallengeTable).uniqueIndex()
}
class ExposedChallengeWhitelist(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<ExposedChallengeWhitelist>(ChallengeWhitelistTable)
val challengeId by ChallengeWhitelistTable.challengeId
val challenge by ExposedChallenge referencedOn ChallengeWhitelistTable.challenge
}
Any help would be appreciated.
Your SQL query is invalid as you use select count(*) without from part.
But it can be rewritten with Exposed DSL like:
ChallengeTable.leftJoin(ChallengeWhitelistTable).
slice(ChallengeTable.columns).
selectAll().
groupBy(ChallengeTable.id, ChallengeWhitelistTable.userName).having {
(ChallengeWhitelistTable.userName eq "testUser") or
(ChallengeWhitelistTable.id.count() eq 0)
}
Another way is to use just left join:
ChallengeTable.leftJoin(ChallengeWhitelistTable).
slice(ChallengeTable.columns).
select {
(ChallengeWhitelistTable.userName eq "testUser") or
(ChallengeWhitelistTable.id.isNull())
}
I am trying to serialize Guava Range objects to JSON using Gson, however the default serialization fails, and I'm unsure how to correctly implement a TypeAdapter for this generic type.
Gson gson = new Gson();
Range<Integer> range = Range.closed(10, 20);
String json = gson.toJson(range);
System.out.println(json);
Range<Integer> range2 = gson.fromJson(json,
new TypeToken<Range<Integer>>(){}.getType());
System.out.println(range2);
assertEquals(range2, range);
This fails like so:
{"lowerBound":{"endpoint":10},"upperBound":{"endpoint":20}}
PASSED: typeTokenInterface
FAILED: range
java.lang.RuntimeException: Unable to invoke no-args constructor for
com.google.common.collect.Cut<java.lang.Integer>. Register an
InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$12.construct(
ConstructorConstructor.java:210)
...
Note that the default serialization actually loses information - it fails to report whether the endpoints are open or closed. I would prefer to see it serialized similar to its toString(), e.g. [10‥20] however simply calling toString() won't work with generic Range instances, as the elements of the range may not be primitives (Joda-Time LocalDate instances, for example). For the same reason, implementing a custom TypeAdapter seems difficult, as we don't know how to deserialize the endpoints.
I've implemented most of a TypeAdaptorFactory based on the template provided for Multimap which ought to work, but now I'm stuck on the generics. Here's what I have so far:
public class RangeTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Range.class
|| !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = (TypeAdapter<?>)gson.getAdapter(TypeToken.get(elementType));
// Bound mismatch: The generic method newRangeAdapter(TypeAdapter<E>) of type
// GsonUtils.RangeTypeAdapterFactory is not applicable for the arguments
// (TypeAdapter<capture#4-of ?>). The inferred type capture#4-of ? is not a valid
// substitute for the bounded parameter <E extends Comparable<?>>
return (TypeAdapter<T>) newRangeAdapter(elementAdapter);
}
private <E extends Comparable<?>> TypeAdapter<Range<E>> newRangeAdapter(final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Range<E>>() {
#Override
public void write(JsonWriter out, Range<E> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String repr = (value.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(value.hasLowerBound() ? elementAdapter.toJson(value.lowerEndpoint()) : "-\u221e") +
'\u2025' +
(value.hasLowerBound() ? elementAdapter.toJson(value.upperEndpoint()) : "+\u221e") +
(value.upperBoundType() == BoundType.CLOSED ? "]" : ")");
out.value(repr);
}
public Range<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String[] endpoints = in.nextString().split("\u2025");
E lower = elementAdapter.fromJson(endpoints[0].substring(1));
E upper = elementAdapter.fromJson(endpoints[1].substring(0,endpoints[1].length()-1));
return Range.range(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN,
upper, endpoints[1].charAt(endpoints[1].length()-1) == '[' ? BoundType.CLOSED : BoundType.OPEN);
}
};
}
}
However the return (TypeAdapter<T>) newRangeAdapter(elementAdapter); line has a compilation error and I'm now at a loss.
What's the best way to resolve this error? Is there a better way to serialize Range objects that I'm missing? What about if I want to serialize RangeSets?
Rather frustrating that the Google utility library and Google serialization library seem to require so much glue to work together :(
This feels somewhat like reinventing the wheel, but it was a lot quicker to put together and test than the time spent trying to get Gson to behave, so at least presently I'll be using the following Converters to serialize Range and RangeSet*, rather than Gson.
/**
* Converter between Range instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<Range<T>, String> rangeConverter(final Converter<T, String> elementConverter) {
final String NEG_INFINITY = "-\u221e";
final String POS_INFINITY = "+\u221e";
final String DOTDOT = "\u2025";
return new Converter<Range<T>, String>() {
#Override
protected String doForward(Range<T> range) {
return (range.hasLowerBound() && range.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(range.hasLowerBound() ? elementConverter.convert(range.lowerEndpoint()) : NEG_INFINITY) +
DOTDOT +
(range.hasUpperBound() ? elementConverter.convert(range.upperEndpoint()) : POS_INFINITY) +
(range.hasUpperBound() && range.upperBoundType() == BoundType.CLOSED ? "]" : ")");
}
#Override
protected Range<T> doBackward(String range) {
String[] endpoints = range.split(DOTDOT);
Range<T> ret = Range.all();
if(!endpoints[0].substring(1).equals(NEG_INFINITY)) {
T lower = elementConverter.reverse().convert(endpoints[0].substring(1));
ret = ret.intersection(Range.downTo(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN));
}
if(!endpoints[1].substring(0,endpoints[1].length()-1).equals(POS_INFINITY)) {
T upper = elementConverter.reverse().convert(endpoints[1].substring(0,endpoints[1].length()-1));
ret = ret.intersection(Range.upTo(upper, endpoints[1].charAt(endpoints[1].length()-1) == ']' ? BoundType.CLOSED : BoundType.OPEN));
}
return ret;
}
};
}
/**
* Converter between RangeSet instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<RangeSet<T>, String> rangeSetConverter(final Converter<T, String> elementConverter) {
return new Converter<RangeSet<T>, String>() {
private final Converter<Range<T>, String> rangeConverter = rangeConverter(elementConverter);
#Override
protected String doForward(RangeSet<T> rs) {
ArrayList<String> ls = new ArrayList<>();
for(Range<T> range : rs.asRanges()) {
ls.add(rangeConverter.convert(range));
}
return Joiner.on(", ").join(ls);
}
#Override
protected RangeSet<T> doBackward(String rs) {
Iterable<String> parts = Splitter.on(",").trimResults().split(rs);
ImmutableRangeSet.Builder<T> build = ImmutableRangeSet.builder();
for(String range : parts) {
build.add(rangeConverter.reverse().convert(range));
}
return build.build();
}
};
}
*For inter-process communication, Java serialization would likely work just fine, as both classes implement Serializable. However I'm serializing to disk for more permanent storage, meaning I need a format I can trust won't change over time. Guava's serialization doesn't provide that guarantee.
Here is a Gson JsonSerializer and JsonDeserializer that generically supports a Range: https://github.com/jamespedwards42/Fava/wiki/Range-Marshaller
#Override
public JsonElement serialize(final Range src, final Type typeOfSrc, final JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
if ( src.hasLowerBound() ) {
jsonObject.add( "lowerBoundType", context.serialize( src.lowerBoundType() ) );
jsonObject.add( "lowerBound", context.serialize( src.lowerEndpoint() ) );
} else
jsonObject.add( "lowerBoundType", context.serialize( BoundType.OPEN ) );
if ( src.hasUpperBound() ) {
jsonObject.add( "upperBoundType", context.serialize( src.upperBoundType() ) );
jsonObject.add( "upperBound", context.serialize( src.upperEndpoint() ) );
} else
jsonObject.add( "upperBoundType", context.serialize( BoundType.OPEN ) );
return jsonObject;
}
#Override
public Range<? extends Comparable<?>> deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
if ( !( typeOfT instanceof ParameterizedType ) )
throw new IllegalStateException( "typeOfT must be a parameterized Range." );
final JsonObject jsonObject = json.getAsJsonObject();
final JsonElement lowerBoundTypeJsonElement = jsonObject.get( "lowerBoundType" );
final JsonElement upperBoundTypeJsonElement = jsonObject.get( "upperBoundType" );
if ( lowerBoundTypeJsonElement == null || upperBoundTypeJsonElement == null )
throw new IllegalStateException( "Range " + json
+ "was not serialized with this serializer! The default serialization does not store the boundary types, therfore we can not deserialize." );
final Type type = ( ( ParameterizedType ) typeOfT ).getActualTypeArguments()[0];
final BoundType lowerBoundType = context.deserialize( lowerBoundTypeJsonElement, BoundType.class );
final JsonElement lowerBoundJsonElement = jsonObject.get( "lowerBound" );
final Comparable<?> lowerBound = lowerBoundJsonElement == null ? null : context.deserialize( lowerBoundJsonElement, type );
final BoundType upperBoundType = context.deserialize( upperBoundTypeJsonElement, BoundType.class );
final JsonElement upperBoundJsonElement = jsonObject.get( "upperBound" );
final Comparable<?> upperBound = upperBoundJsonElement == null ? null : context.deserialize( upperBoundJsonElement, type );
if ( lowerBound == null && upperBound != null )
return Range.upTo( upperBound, upperBoundType );
else if ( lowerBound != null && upperBound == null )
return Range.downTo( lowerBound, lowerBoundType );
else if ( lowerBound == null && upperBound == null )
return Range.all();
return Range.range( lowerBound, lowerBoundType, upperBound, upperBoundType );
}
Here is a straight forward solution. Works very well
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import com.google.gson.*;
import java.lang.reflect.Type;
public class GoogleRangeAdapter implements JsonSerializer, JsonDeserializer {
public static String TK_hasLowerBound = "hasLowerBound";
public static String TK_hasUpperBound = "hasUpperBound";
public static String TK_lowerBoundType = "lowerBoundType";
public static String TK_upperBoundType = "upperBoundType";
public static String TK_lowerBound = "lowerBound";
public static String TK_upperBound = "upperBound";
#Override
public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = (JsonObject)json;
boolean hasLowerBound = jsonObject.get(TK_hasLowerBound).getAsBoolean();
boolean hasUpperBound = jsonObject.get(TK_hasUpperBound).getAsBoolean();
if (!hasLowerBound && !hasUpperBound) {
return Range.all();
}
else if (!hasLowerBound && hasUpperBound){
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
if (upperBoundType == BoundType.OPEN)
return Range.lessThan(upperBound);
else
return Range.atMost(upperBound);
}
else if (hasLowerBound && !hasUpperBound){
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN)
return Range.greaterThan(lowerBound);
else
return Range.atLeast(lowerBound);
}
else {
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.OPEN)
return Range.open(lowerBound, upperBound);
else if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.CLOSED)
return Range.openClosed(lowerBound, upperBound);
else if (lowerBoundType == BoundType.CLOSED && upperBoundType == BoundType.OPEN)
return Range.closedOpen(lowerBound, upperBound);
else
return Range.closed(lowerBound, upperBound);
}
}
#Override
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
Range<Double> range = (Range<Double>)src;
boolean hasLowerBound = range.hasLowerBound();
boolean hasUpperBound = range.hasUpperBound();
jsonObject.addProperty(TK_hasLowerBound, hasLowerBound);
jsonObject.addProperty(TK_hasUpperBound, hasUpperBound);
if (hasLowerBound) {
jsonObject.addProperty(TK_lowerBound, range.lowerEndpoint());
jsonObject.addProperty(TK_lowerBoundType, range.lowerBoundType().name());
}
if (hasUpperBound) {
jsonObject.addProperty(TK_upperBound, range.upperEndpoint());
jsonObject.addProperty(TK_upperBoundType, range.upperBoundType().name());
}
return jsonObject;
}
}
it's my first question here :)
I'd like to build an heterogeneous AST with ANTLR for a simple grammar. There are different Interfaces to represent the AST nodes, e. g. IInfiExp, IVariableDecl. ANTLR comes up with CommonTree to hold all the information of the source code (line number, character position etc.) and I want to use this as a base for the implementations of the AST interfacese IInfixExp ...
In order to get an AST as output with CommonTree as node types, I set:
options {
language = Java;
k = 1;
output = AST;
ASTLabelType = CommonTree;
}
The IInifxExp is:
package toylanguage;
public interface IInfixExp extends IExpression {
public enum Operator {
PLUS, MINUS, TIMES, DIVIDE;
}
public Operator getOperator();
public IExpression getLeftHandSide();
public IExpression getRightHandSide();
}
and the implementation InfixExp is:
package toylanguage;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
// IInitializable has only void initialize()
public class InfixExp extends CommonTree implements IInfixExp, IInitializable {
private Operator operator;
private IExpression leftHandSide;
private IExpression rightHandSide;
InfixExp(Token token) {
super(token);
}
#Override
public Operator getOperator() {
return operator;
}
#Override
public IExpression getLeftHandSide() {
return leftHandSide;
}
#Override
public IExpression getRightHandSide() {
return rightHandSide;
}
// from IInitializable. get called from ToyTreeAdaptor.rulePostProcessing
#Override
public void initialize() {
// term ((PLUS|MINUS) term)+
// atom ((TIMES|DIIDE) atom)+
// exact 2 children
assert getChildCount() == 2;
// left and right child are IExpressions
assert getChild(0) instanceof IExpression
&& getChild(1) instanceof IExpression;
// operator
switch (token.getType()) {
case ToyLanguageParser.PLUS:
operator = Operator.PLUS;
break;
case ToyLanguageParser.MINUS:
operator = Operator.MINUS;
break;
case ToyLanguageParser.TIMES:
operator = Operator.TIMES;
break;
case ToyLanguageParser.DIVIDE:
operator = Operator.DIVIDE;
break;
default:
assert false;
}
// left and right operands
leftHandSide = (IExpression) getChild(0);
rightHandSide = (IExpression) getChild(1);
}
}
The corresponding rules are:
exp // e.g. a+b
: term ((PLUS<InfixExp>^|MINUS<InfixExp>^) term)*
;
term // e.g. a*b
: atom ((TIMES<InfixExp>^|DIVIDE<InfixExp>^) atom)*
;
This works fine, becouse PLUS, MINUS etc. are "real" tokens.
But now comes to the imaginary token:
tokens {
PROGRAM;
}
The corresponding rule is:
program // e.g. var a, b; a + b
: varDecl* exp
-> ^(PROGRAM<Program> varDecl* exp)
;
With this, ANTLR doesn't create a tree with PROGRAM as root node.
In the parser, the following code creates the Program instance:
root_1 = (CommonTree)adaptor.becomeRoot(new Program(PROGRAM), root_1);
Unlike InfixExp not the Program(Token) constructor but Program(int) is invoked.
Program is:
package toylanguage;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
class Program extends CommonTree implements IProgram, IInitializable {
private final LinkedList<IVariableDecl> variableDeclarations = new LinkedList<IVariableDecl>();
private IExpression expression = null;
Program(Token token) {
super(token);
}
public Program(int tokeType) {
// What to do?
super();
}
#Override
public List<IVariableDecl> getVariableDeclarations() {
// don't allow to change the list
return Collections.unmodifiableList(variableDeclarations);
}
#Override
public IExpression getExpression() {
return expression;
}
#Override
public void initialize() {
// program: varDecl* exp;
// at least one child
assert getChildCount() > 0;
// the last one is a IExpression
assert getChild(getChildCount() - 1) instanceof IExpression;
// iterate over varDecl*
int i = 0;
while (getChild(i) instanceof IVariableDecl) {
variableDeclarations.add((IVariableDecl) getChild(i));
i++;
}
// exp
expression = (IExpression) getChild(i);
}
}
you can see the constructor:
public Program(int tokeType) {
// What to do?
super();
}
as a result of it, with super() a CommonTree ist build without a token. So CommonTreeAdaptor.rulePostProcessing see a flat list, not a tree with a Token as root.
My TreeAdaptor looks like:
package toylanguage;
import org.antlr.runtime.tree.CommonTreeAdaptor;
public class ToyTreeAdaptor extends CommonTreeAdaptor {
public Object rulePostProcessing(Object root) {
Object result = super.rulePostProcessing(root);
// check if needs initialising
if (result instanceof IInitializable) {
IInitializable initializable = (IInitializable) result;
initializable.initialize();
}
return result;
};
}
And to test anything I use:
package toylanguage;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.tree.CommonTree;
import toylanguage.ToyLanguageParser.program_return;
public class Processor {
public static void main(String[] args) {
String input = "var a, b; a + b + 123"; // sample input
ANTLRStringStream stream = new ANTLRStringStream(input);
ToyLanguageLexer lexer = new ToyLanguageLexer(stream);
TokenStream tokens = new CommonTokenStream(lexer);
ToyLanguageParser parser = new ToyLanguageParser(tokens);
ToyTreeAdaptor treeAdaptor = new ToyTreeAdaptor();
parser.setTreeAdaptor(treeAdaptor);
try {
// test with: var a, b; a + b
program_return program = parser.program();
CommonTree root = program.tree;
// prints 'a b (+ a b)'
System.out.println(root.toStringTree());
// get (+ a b), the third child of root
CommonTree third = (CommonTree) root.getChild(2);
// prints '(+ a b)'
System.out.println(third.toStringTree());
// prints 'true'
System.out.println(third instanceof IInfixExp);
// prints 'false'
System.out.println(root instanceof IProgram);
} catch (RecognitionException e) {
e.printStackTrace();
}
}
}
For completeness, here is the full grammar:
grammar ToyLanguage;
options {
language = Java;
k = 1;
output = AST;
ASTLabelType = CommonTree;
}
tokens {
PROGRAM;
}
#header {
package toylanguage;
}
#lexer::header {
package toylanguage;
}
program // e.g. var a, b; a + b
: varDecl* exp
-> ^(PROGRAM<Program> varDecl* exp)
;
varDecl // e.g. var a, b;
: 'var'! ID<VariableDecl> (','! ID<VariableDecl>)* ';'!
;
exp // e.g. a+b
: term ((PLUS<InfixExp>^|MINUS<InfixExp>^) term)*
;
term // e.g. a*b
: atom ((TIMES<InfixExp>^|DIVIDE<InfixExp>^) atom)*
;
atom
: INT<IntegerLiteralExp> // e.g. 123
| ID<VariableExp> // e.g. a
| '(' exp ')' -> exp // e.g. (a+b)
;
INT : ('0'..'9')+ ;
ID : ('a'..'z')+ ;
PLUS : '+' ;
MINUS : '-' ;
TIMES : '*' ;
DIVIDE : '/' ;
WS : ('\t' | '\n' | '\r' | ' ')+ { $channel = HIDDEN; } ;
OK, the final question is how to get from
program // e.g. var a, b; a + b
: varDecl* exp
-> ^(PROGRAM<Program> varDecl* exp)
;
a tree with PROGRAM as root
^(PROGRAM varDecl* exp)
and not a flat list with
(varDecl* exp) ?
(Sorry for this numerous code fragments)
Ciao Vertex
Try creating the following constructor:
public Program(int tokenType) {
super(new CommonToken(tokenType, "PROGRAM"));
}