Skip to content

Commit

Permalink
feat(codegen): Ensured the VariableLiterals are able to provide their…
Browse files Browse the repository at this point in the history
… type.
  • Loading branch information
chrisdutz committed Feb 22, 2022
1 parent e376eec commit 0a99ac6
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.plc4x.plugins.codegenerator.language.mspec;

import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;

import java.util.function.Consumer;

public interface LazyTypeDefinitionConsumer {

void setOrScheduleTypeDefinitionConsumer(String typeRefName, Consumer<TypeDefinition> setTypeDefinition);

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,39 @@
*/
package org.apache.plc4x.plugins.codegenerator.language.mspec.expression;

import org.apache.plc4x.plugins.codegenerator.language.mspec.LazyTypeDefinitionConsumer;
import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatListener;
import org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition;
import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
import org.apache.plc4x.plugins.codegenerator.types.fields.PropertyField;
import org.apache.plc4x.plugins.codegenerator.types.references.TypeReference;
import org.apache.plc4x.plugins.codegenerator.types.terms.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class ExpressionStringListener extends ExpressionBaseListener {

private static final Logger LOGGER = LoggerFactory.getLogger(ExpressionStringListener.class);

private final LazyTypeDefinitionConsumer lazyTypeDefinitionConsumer;

private final String rootTypeName;

private Stack<List<Term>> parserContexts;

private Stack<CompletableFuture<TypeReference>> futureStack;

private Term root;

public ExpressionStringListener(LazyTypeDefinitionConsumer lazyTypeDefinitionConsumer, String rootTypeName) {
this.lazyTypeDefinitionConsumer = lazyTypeDefinitionConsumer;
this.rootTypeName = rootTypeName;
}

public Term getRoot() {
return root;
}
Expand Down Expand Up @@ -88,9 +110,48 @@ public void exitStringExpression(ExpressionParser.StringExpressionContext ctx) {

@Override
public void enterIdentifierSegment(ExpressionParser.IdentifierSegmentContext ctx) {
String propertyName = ctx.name.getText();

CompletableFuture<TypeReference> typeReferenceFuture = new CompletableFuture<>();
if (futureStack == null) {
schedulePropertyResolution(propertyName, typeReferenceFuture, rootTypeName);
futureStack = new Stack<>();
} else {
futureStack.peek().whenComplete((typeReference, throwable) -> {
if (throwable != null) {
LOGGER.debug("Error processing variables", throwable);
return;
}
String typeName = typeReference.asComplexTypeReference().orElseThrow().getName();
schedulePropertyResolution(propertyName, typeReferenceFuture, typeName);
});
}
futureStack.push(typeReferenceFuture);
parserContexts.push(new LinkedList<>());
}

private void schedulePropertyResolution(String propertyName, CompletableFuture<TypeReference> typeReferenceFuture, String typeName) {
lazyTypeDefinitionConsumer.setOrScheduleTypeDefinitionConsumer(typeName, (TypeDefinition typeDefinition)->{
if (!typeDefinition.isComplexTypeDefinition()) {
typeReferenceFuture.completeExceptionally(new RuntimeException("is not a complex type"));
return;
}
final ComplexTypeDefinition complexTypeDefinition = typeDefinition
.asComplexTypeDefinition()
.orElseThrow();
final Optional<PropertyField> propertyFieldByName = complexTypeDefinition
.getPropertyFieldByName(propertyName);
if (propertyFieldByName.isEmpty()) {
typeReferenceFuture.completeExceptionally(new RuntimeException("Field with name " + propertyName + " not found on "+typeName));
return;
}
TypeReference propertyTypeReference = propertyFieldByName
.orElseThrow()
.getType();
typeReferenceFuture.complete(propertyTypeReference);
});
}

@Override
public void exitIdentifierSegment(ExpressionParser.IdentifierSegmentContext ctx) {
List<Term> args = parserContexts.pop();
Expand All @@ -108,16 +169,30 @@ public void exitIdentifierSegment(ExpressionParser.IdentifierSegmentContext ctx)
}

String name = ctx.name.getText();
// TODO: Based on the current context type-definition, get the type of the property with name ctx.name.getText()

int index = VariableLiteral.NO_INDEX;
if (indexContext != null) {
// TODO: Add a check, that the field providing the property is an "array" or "manualArray" field.
index = indexContext.getFirst().getNumber().intValue();
}
VariableLiteral rest = null;
if (restContext != null) {
// TODO: Add a check, that the field providing the property references a complex type (or uses one of the built-ins)
rest = restContext.getFirst();
}
parserContexts.peek().add(new DefaultVariableLiteral(name, argsContext, index, rest));

final DefaultVariableLiteral variableLiteral = new DefaultVariableLiteral(name, argsContext, index, rest);
futureStack.pop().whenComplete((typeReference, throwable) -> {
// TODO: check throwable
String typeName= typeReference.asComplexTypeReference().orElseThrow().getName();
lazyTypeDefinitionConsumer.setOrScheduleTypeDefinitionConsumer(typeName, variableLiteral::setTypeDefinition);
});
if (futureStack.empty()) {
futureStack = null;
}

parserContexts.peek().add(variableLiteral);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,31 @@
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.apache.plc4x.plugins.codegenerator.language.mspec.LazyTypeDefinitionConsumer;
import org.apache.plc4x.plugins.codegenerator.types.terms.Term;

import java.io.IOException;
import java.io.InputStream;

public class ExpressionStringParser {

private final LazyTypeDefinitionConsumer lazyTypeDefinitionConsumer;

private final String rootTypeName;

public ExpressionStringParser(LazyTypeDefinitionConsumer lazyTypeDefinitionConsumer, String rootTypeName) {
this.lazyTypeDefinitionConsumer = lazyTypeDefinitionConsumer;
this.rootTypeName = rootTypeName;
}

public Term parse(InputStream source) {
ExpressionLexer lexer;
try {
lexer = new ExpressionLexer(CharStreams.fromStream(source));
} catch (IOException e) {
throw new RuntimeException(e);
}
ExpressionStringListener listener = new ExpressionStringListener();
ExpressionStringListener listener = new ExpressionStringListener(lazyTypeDefinitionConsumer, rootTypeName);
new ParseTreeWalker().walk(listener, new ExpressionParser(new CommonTokenStream(lexer)).expressionString());
return listener.getRoot();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import org.antlr.v4.runtime.RuleContext;
import org.apache.commons.io.IOUtils;
import org.apache.plc4x.plugins.codegenerator.language.mspec.LazyTypeDefinitionConsumer;
import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecBaseListener;
import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecParser;
import org.apache.plc4x.plugins.codegenerator.language.mspec.expression.ExpressionStringParser;
Expand All @@ -45,7 +46,7 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class MessageFormatListener extends MSpecBaseListener {
public class MessageFormatListener extends MSpecBaseListener implements LazyTypeDefinitionConsumer {

private static final Logger LOGGER = LoggerFactory.getLogger(MessageFormatListener.class);

Expand All @@ -67,6 +68,8 @@ public Deque<List<EnumValue>> getEnumContexts() {
return enumContexts;
}

private String currentTypeName;

@Override
public void enterFile(MSpecParser.FileContext ctx) {
parserContexts = new LinkedList<>();
Expand All @@ -76,6 +79,7 @@ public void enterFile(MSpecParser.FileContext ctx) {

@Override
public void enterComplexType(MSpecParser.ComplexTypeContext ctx) {
currentTypeName = getIdString(ctx.name);
// Set a map of attributes that should be set for all fields.
Map<String, Term> curBatchSetAttributes = new HashMap<>();
// Add all attributes defined in the current batchSet field.
Expand Down Expand Up @@ -491,7 +495,9 @@ private Term getExpressionTerm(MSpecParser.ExpressionContext expressionContext)
String expressionString = getExprString(expressionContext);
Objects.requireNonNull(expressionString, "Expression string should not be null");
InputStream inputStream = IOUtils.toInputStream(expressionString, Charset.defaultCharset());
ExpressionStringParser parser = new ExpressionStringParser();

Objects.requireNonNull(currentTypeName, "expression term can only occur within a type");
ExpressionStringParser parser = new ExpressionStringParser(this, currentTypeName);
try {
return parser.parse(inputStream);
} catch (Exception e) {
Expand All @@ -504,7 +510,7 @@ private VariableLiteral getVariableLiteral(MSpecParser.VariableLiteralContext va
// TODO: make nullsafe
final String variableLiteral = variableLiteralContext.getText();
InputStream inputStream = IOUtils.toInputStream(variableLiteral, Charset.defaultCharset());
ExpressionStringParser parser = new ExpressionStringParser();
ExpressionStringParser parser = new ExpressionStringParser(this, currentTypeName);
try {
// As this come from a VariableLiteralContext we know that it is a VariableLiteral
return (VariableLiteral) parser.parse(inputStream);
Expand All @@ -518,7 +524,7 @@ private Literal getValueLiteral(MSpecParser.ValueLiteralContext valueLiteralCont
// TODO: make nullsafe
final String valueLiteralContextText = valueLiteralContext.getText();
InputStream inputStream = IOUtils.toInputStream(valueLiteralContextText, Charset.defaultCharset());
ExpressionStringParser parser = new ExpressionStringParser();
ExpressionStringParser parser = new ExpressionStringParser(this, currentTypeName);
try {
// As this come from a ValueLiteralContext we know that it is a Literal
return (Literal) parser.parse(inputStream);
Expand Down Expand Up @@ -607,7 +613,7 @@ private List<Term> getParams(MSpecParser.MultipleExpressionsContext params) {

private Term parseExpression(String expressionString) {
InputStream inputStream = IOUtils.toInputStream(expressionString, Charset.defaultCharset());
ExpressionStringParser parser = new ExpressionStringParser();
ExpressionStringParser parser = new ExpressionStringParser(this, currentTypeName);
try {
Term term = parser.parse(inputStream);
return term;
Expand Down Expand Up @@ -654,7 +660,7 @@ private String getExprString(MSpecParser.ExpressionContext ctx) {
return null;
}

private void dispatchType(String typeName, TypeDefinition type) {
public void dispatchType(String typeName, TypeDefinition type) {
LOGGER.debug("dispatching {}:{}", typeName, type);
List<Consumer<TypeDefinition>> waitingConsumers = typeDefinitionConsumers.getOrDefault(typeName, new LinkedList<>());
LOGGER.debug("{} waiting for {}", waitingConsumers.size(), typeName);
Expand All @@ -669,7 +675,8 @@ private void dispatchType(String typeName, TypeDefinition type) {
types.put(typeName, type);
}

private void setOrScheduleTypeDefinitionConsumer(String typeRefName, Consumer<TypeDefinition> setTypeDefinition) {
@Override
public void setOrScheduleTypeDefinitionConsumer(String typeRefName, Consumer<TypeDefinition> setTypeDefinition) {
LOGGER.debug("set or schedule {}", typeRefName);
TypeDefinition typeDefinition = types.get(typeRefName);
if (typeDefinition != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

class ExpressionStringParserTest {

ExpressionStringParser SUT = new ExpressionStringParser();
ExpressionStringParser SUT = new ExpressionStringParser((typeRefName, setTypeDefinition) -> {}, "hurz");

@Test
void parseSimple() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,10 @@ void parseSomethingElse() {
System.out.println(parse);
}

@Test
void parseNothingElse() {
TypeContext parse = SUT.parse(getClass().getResourceAsStream("/mspec.example3"));
System.out.println(parse);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

[type Root
[simple A a]
[virtual uint 8 doesIt 'a.b.c']
]

[type A
[simple B b]
]

[type B
[simple C c]
]

[type C
[simple uint 8 nothingElseMatters]
]
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public TypeContext getTypeContext() throws GenerationException {
typeDefinitionMap.putAll(typeContext.getTypeDefinitions());

if (typeContext.getUnresolvedTypeReferences().size() > 0) {
throw new GenerationException("Unresolved types left");
throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
}

return new TypeContext() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public TypeContext getTypeContext() throws GenerationException {
typeDefinitionMap.putAll(typeContext.getTypeDefinitions());

if (typeContext.getUnresolvedTypeReferences().size() > 0) {
throw new GenerationException("Unresolved types left");
throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
}

return new TypeContext() {
Expand Down

0 comments on commit 0a99ac6

Please sign in to comment.