
com.twineworks.tweakflow.lang.parse.builders.ExpressionBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tweakflow Show documentation
Show all versions of tweakflow Show documentation
Embeddable expression language for Java.
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Twineworks GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.twineworks.tweakflow.lang.parse.builders;
import com.twineworks.tweakflow.grammar.TweakFlowLexer;
import com.twineworks.tweakflow.grammar.TweakFlowParser;
import com.twineworks.tweakflow.grammar.TweakFlowParserBaseVisitor;
import com.twineworks.tweakflow.lang.ast.args.*;
import com.twineworks.tweakflow.lang.ast.expressions.*;
import com.twineworks.tweakflow.lang.ast.meta.ViaNode;
import com.twineworks.tweakflow.lang.ast.structure.*;
import com.twineworks.tweakflow.lang.ast.structure.match.DefaultPatternNode;
import com.twineworks.tweakflow.lang.ast.structure.match.MatchLineNode;
import com.twineworks.tweakflow.lang.ast.structure.match.MatchLines;
import com.twineworks.tweakflow.lang.errors.LangError;
import com.twineworks.tweakflow.lang.errors.LangException;
import com.twineworks.tweakflow.lang.parse.SourceInfo;
import com.twineworks.tweakflow.lang.parse.units.ParseUnit;
import com.twineworks.tweakflow.lang.types.Type;
import com.twineworks.tweakflow.lang.types.Types;
import com.twineworks.tweakflow.lang.values.DateTimeValue;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.math.BigInteger;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import static com.twineworks.tweakflow.lang.parse.util.CodeParseHelper.*;
public class ExpressionBuilder extends TweakFlowParserBaseVisitor {
private final ParseUnit parseUnit;
public ExpressionBuilder(ParseUnit parseUnit) {
this.parseUnit = parseUnit;
}
public ExpressionNode addImplicitCast(Type targetType, ExpressionNode node){
if (targetType == Types.ANY) return node;
if (targetType == node.getValueType()) return node;
if (node.getValueType().canAttemptCastTo(targetType)){
return new CastNode()
.setExpression(node)
.setTargetType(targetType)
.setSourceInfo(node.getSourceInfo());
}
else {
throw new LangException(LangError.INCOMPATIBLE_TYPES, "cannot cast "+node.getValueType().name()+" to "+targetType.name(), node.getSourceInfo());
}
}
@Override
public ExpressionNode visitNilLiteral(TweakFlowParser.NilLiteralContext ctx) {
return new NilNode().setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitStringHereDoc(TweakFlowParser.StringHereDocContext ctx) {
String text = ctx.HEREDOC_STRING().getText();
// empty heredoc?
if (text.matches("~~~\r?\n~~~")){
text = "";
}
else{
text = text.replaceFirst("\\A~~~\r?\n", "");
text = text.replaceFirst("\r?\n~~~\\z", "");
}
return new StringNode(text).setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitStringVerbatim(TweakFlowParser.StringVerbatimContext ctx) {
String text = ctx.VSTRING().getText();
// strip quotes
text = text.substring(1, text.length()-1);
// replace escape sequence
return new StringNode(text.replaceAll("''", "'")).setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitStringQuoted(TweakFlowParser.StringQuotedContext ctx) {
SourceInfo sourceInfo = (srcOf(parseUnit, ctx));
StringBuilder builder = new StringBuilder();
// skip opening '"' and closing '"'
int i = 1;
int n = ctx.getChildCount()-1;
for (;i 11) {
clockTime = str.substring(11, 19);
}
if (len > 20) {
// fractional seconds
StringBuilder fractionalSecondsBuilder = new StringBuilder();
int i = 19;
if (str.charAt(i) == '.'){
// keep collecting fractional digits
i = 20;
char c = str.charAt(i);
while(c >= '0' && c <= '9'){
fractionalSecondsBuilder.append(c);
i+=1;
if (i == len) break;
c = str.charAt(i);
}
}
fractionalSeconds = fractionalSecondsBuilder.toString();
// offset & timezone
if (i < len){
char c = str.charAt(i);
if (c == '-' || c == '+'){
// offset present
offset = str.substring(i, i+6);
i+=6;
}
else if (c == 'Z'){
offset = "Z";
i+=1;
}
// time zone given?
if (i < len && str.charAt(i) == '@'){
tz = identifier(str.substring(i+1));
}
// no timezone given, construct implicitly
else{
if (offset.equals("Z")){
tz = "UTC";
}
else{
tz = "UTC"+offset;
}
}
}
}
try {
// create a zoned time, using strict rules for all parts
LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE);
LocalTime localTime = LocalTime.parse(clockTime+"."+fractionalSeconds, DateTimeFormatter.ISO_LOCAL_TIME);
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
ZonedDateTime zonedDateTime = ZonedDateTime.ofStrict(localDateTime, ZoneOffset.of(offset), ZoneId.of(tz));
DateTimeValue dateTime = new DateTimeValue(zonedDateTime);
return new DateTimeNode(dateTime).setSourceInfo(srcOf(parseUnit, ctx));
} catch(DateTimeException e){
throw new LangException(LangError.INVALID_DATETIME, e.getMessage(), srcOf(parseUnit, ctx));
}
}
@Override
public ExpressionNode visitForExp(TweakFlowParser.ForExpContext ctx) {
ForNode forNode = new ForNode();
forNode.setSourceInfo(srcOf(parseUnit, ctx));
ForHead head = forNode.getHead();
TweakFlowParser.ForHeadContext headContext = ctx.forHead();
int childCount = headContext.getChildCount();
for(int i=0; i matchLineContexts = matchBodyContext.matchLine();
int size = matchLineContexts.size();
for (int i = 0; i < size; i++) {
TweakFlowParser.MatchLineContext matchLineContext = matchLineContexts.get(i);
MatchLineNode matchLineNode = new MatchLineBuilder(parseUnit).visit(matchLineContext);
// ensure the default pattern appears last, if present
if (matchLineNode.getPattern() instanceof DefaultPatternNode){
if (i != size-1) throw new LangException(LangError.DEFAULT_PATTERN_NOT_LAST, matchLineNode.getPattern().getSourceInfo());
}
matchLines.getElements().add(matchLineNode);
}
return matchNode;
}
@Override
public ExpressionNode visitDefaultExp(TweakFlowParser.DefaultExpContext ctx) {
return new DefaultNode()
.setExpression(visit(ctx.expression(0)))
.setDefaultExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitTypeOfExp(TweakFlowParser.TypeOfExpContext ctx) {
return new TypeOfNode()
.setExpression(visit(ctx.expression()))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitDebugExp(TweakFlowParser.DebugExpContext ctx) {
ExpressionNode debugExpression = visit(ctx.expression(0));
ExpressionNode valueExpression = ctx.expression().size() > 1 ? visit(ctx.expression(1)) : null;
return new DebugNode()
.setDebugExpression(debugExpression)
.setValueExpression(valueExpression)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitBitwiseNotExp(TweakFlowParser.BitwiseNotExpContext ctx) {
return new BitwiseNotNode()
.setExpression(visit(ctx.expression()))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitBitwiseXorExp(TweakFlowParser.BitwiseXorExpContext ctx) {
return new BitwiseXorNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitShiftLeftExp(TweakFlowParser.ShiftLeftExpContext ctx) {
return new BitwiseShiftLeftNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitBitwiseOrExp(TweakFlowParser.BitwiseOrExpContext ctx) {
return new BitwiseOrNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitZeroShiftRightExp(TweakFlowParser.ZeroShiftRightExpContext ctx) {
return new BitwiseZeroShiftRightNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitPreservingShiftRightExp(TweakFlowParser.PreservingShiftRightExpContext ctx) {
return new BitwisePreservingShiftRightNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitBitwiseAndExp(TweakFlowParser.BitwiseAndExpContext ctx) {
return new BitwiseAndNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitStringInterpolation(TweakFlowParser.StringInterpolationContext ctx) {
SourceInfo sourceInfo = (srcOf(parseUnit, ctx));
ArrayList nodes = new ArrayList<>();
// skip opening '"' and closing '"'
int i = 1;
int n = ctx.getChildCount()-1;
for (;i compacted = compactStringNodes(nodes);
// compacted to nothing is an empty string
if (nodes.size() == 0){
return new StringNode("").setSourceInfo(sourceInfo);
}
// a single node is returned directly, it might be a string or a reference
if (compacted.size() == 1){
return compacted.get(0).setSourceInfo(sourceInfo);
}
// multiple nodes go into concat nodes
ExpressionNode left = compacted.get(0);
for (int si = 1; si < compacted.size(); si++) {
ExpressionNode right = compacted.get(si);
left = new StringConcatNode()
.setLeftExpression(left)
.setRightExpression(right)
.setSourceInfo(left.getSourceInfo().copy());
}
return left.setSourceInfo(sourceInfo);
}
@Override
public ExpressionNode visitStringReferenceInterpolation(TweakFlowParser.StringReferenceInterpolationContext ctx) {
// re-parse as expression to get proper reference nodes out of interpolated text
String txt = ctx.getText();
// skip opening and ending markers in #{my.interpolated.expression}
String exp = txt.substring(2, txt.length()-1).trim();
CodePointCharStream input = CharStreams.fromString(exp);
TweakFlowLexer lexer = new TweakFlowLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
try {
// consume tokens
tokens.fill();
// parse them
TweakFlowParser parser = new TweakFlowParser(tokens);
// build AST nodes
ExpressionNode expressionNode = visit(parser.reference());
// ensure it's a reference
if (!(expressionNode instanceof ReferenceNode)){
throw new LangException(LangError.PARSE_ERROR, "Only references allowed in string interpolation");
}
// and update the source info on it to match where it was found
ReferenceNode refNode = (ReferenceNode) expressionNode;
refNode.setSourceInfo(srcOf(parseUnit, ctx));
// adjust the char within line to point to the actual reference, not the marker in #{my.ref}
return refNode;
}
catch (RecognitionException e){
throw LangException.wrap(e, LangError.PARSE_ERROR)
.put("location", srcOf(parseUnit, ctx));
}
}
private ExpressionNode splatEnabledListExpression(SourceInfo src, List children){
// phase 1: collect subLists
// [a, b, ...c, d] -> [[a, b], c, [d]]
ArrayList subLists = new ArrayList<>();
ListNode currentSubList = null;
for(ParseTree child : children){
if (child instanceof TerminalNode){
// skip ',' optionally separating entries
continue;
}
if (child instanceof TweakFlowParser.SplatContext){
// a splat closes any current sublist and enters itself as sublist
// close sublist
if (currentSubList != null){
subLists.add(currentSubList);
currentSubList = null;
}
// enter its expression as sublist
TweakFlowParser.SplatContext splatContext = (TweakFlowParser.SplatContext) child;
subLists.add(visit(splatContext.expression()));
}
else {
// a regular list item enters itself into the current sublist, creating one if necessary
if (currentSubList == null){
currentSubList = new ListNode();
currentSubList.setSourceInfo(srcOf(parseUnit, (ParserRuleContext) child));
}
currentSubList.getElements().add(visit(child));
}
}
// at the end, any open sublist is closed
if (currentSubList != null){
subLists.add(currentSubList);
}
// phase 2: generate subLists for concatenation
// [[a, b], c, [d]] -> concat(concat([a, b], [c]), [d])
// direct returns if no concat necessary
if (subLists.size() == 0){
return new ListNode().setSourceInfo(src);
}
if (subLists.size() == 1){
ExpressionNode result = subLists.get(0);
// for cosmetics, let source info point to the list definition, as opposed to the element in it
result.setSourceInfo(src);
return result;
}
// fold subLists into list concat nodes
ExpressionNode left = subLists.get(0);
for (int si = 1; si < subLists.size(); si++) {
ExpressionNode right = subLists.get(si);
left = new ListConcatNode()
.setLeftExpression(left)
.setRightExpression(right)
.setSourceInfo(left.getSourceInfo().copy());
}
return left;
}
@Override
public ExpressionNode visitListLiteral(TweakFlowParser.ListLiteralContext ctx) {
return splatEnabledListExpression(srcOf(parseUnit, ctx), ctx.children);
}
/**
* Compacts consecutive StringNode elements in the given list. All sequences of StringNode elements
* are compacted into a single StringNode element in the returned list. Source information of the first
* StringNode is preserved in the compacted StringNode.
* All StringNodes in the returned list are newly constructed objects. Original StringNodes are not
* modified. Nodes that are not StringNodes are carried over in the returned list by reference. There
* are no copies created for them.
* @param in
* @return The list of nodes where any sub-sequences of StringNodes should be compacted.
*/
List compactStringNodes(List in){
ArrayList results = new ArrayList<>(in.size());
StringNode currentBase = null;
StringBuilder builder = null;
for(int i=0;i [{:a a,:b b}, c, {:d d}]
ArrayList subMaps = new ArrayList<>();
DictNode currentSubMap = null;
List children = ctx.children;
for (int i = 0; i < children.size(); i++) {
ParseTree child = children.get(i);
if (child instanceof TerminalNode) {
// skip ',' optionally separating entries and opening/closing {}
continue;
}
if (child instanceof TweakFlowParser.SplatContext) {
// a splat closes any current sublist and enters itself as sublist
// close sublist
if (currentSubMap != null) {
subMaps.add(currentSubMap);
currentSubMap = null;
}
// enter its expression as sublist
TweakFlowParser.SplatContext splatContext = (TweakFlowParser.SplatContext) child;
subMaps.add(visit(splatContext.expression()));
} else {
// a regular map entry enters itself into the current sublist, creating one if necessary
if (currentSubMap == null) {
currentSubMap = new DictNode();
currentSubMap.setSourceInfo(srcOf(parseUnit, (ParserRuleContext) child));
}
ExpressionNode key = addImplicitCast(Types.STRING, visit(child));
ExpressionNode value = visit(ctx.children.get(++i));
DictEntryNode entry = new DictEntryNode().setSourceInfo(key.getSourceInfo().copy());
entry.setKey(key);
entry.setValue(value);
currentSubMap.getEntries().add(entry);
}
}
// at the end, any open subMap is closed
if (currentSubMap != null){
subMaps.add(currentSubMap);
}
// phase 2: generate merge call for subMaps
// [{:a a, :b b}, c, {:d d}] -> merge(merge({:a a, :b b}, c), {:d d})
// direct returns if no concat necessary
if (subMaps.size() == 0){
return new DictNode().setSourceInfo(srcOf(parseUnit, ctx));
}
if (subMaps.size() == 1){
ExpressionNode result = subMaps.get(0);
// for cosmetics, let source info point to the map definition, as opposed to the entry in it
result.setSourceInfo(srcOf(parseUnit, ctx));
return result;
}
// fold subLists into calls to $std.data.concat
ExpressionNode left = subMaps.get(0);
for (int si = 1; si < subMaps.size(); si++) {
ExpressionNode right = subMaps.get(si);
left = new DictMergeNode()
.setLeftExpression(left)
.setRightExpression(right)
.setSourceInfo(left.getSourceInfo().copy());
}
return left;
}
@Override
public BooleanNode visitBooleanLiteral(TweakFlowParser.BooleanLiteralContext ctx) {
if (ctx.TRUE() != null){
return new BooleanNode(Boolean.TRUE).setSourceInfo(srcOf(parseUnit, ctx));
}
else {
return new BooleanNode(Boolean.FALSE).setSourceInfo(srcOf(parseUnit, ctx));
}
}
@Override
public ExpressionNode visitLetExp(TweakFlowParser.LetExpContext ctx) {
BindingsNode bindings = new BindingsNode()
.setSourceInfo(srcOf(parseUnit, ctx));
Map varDefs = bindings.getVars().getMap();
for (TweakFlowParser.VarDefContext varDefContext : ctx.varDef()) {
VarDefNode varDef = new VarDefBuilder(parseUnit).visitVarDef(varDefContext);
if (varDefs.containsKey(varDef.getSymbolName())){
throw new LangException(LangError.ALREADY_DEFINED, varDef.getSymbolName()+" already defined", varDef.getSourceInfo());
}
varDefs.put(varDef.getSymbolName(), varDef);
}
bindings.getVars().cook();
ExpressionNode expression = new ExpressionBuilder(parseUnit).visit(ctx.expression());
return new LetNode()
.setBindings(bindings)
.setExpression(expression)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitNestedExp(TweakFlowParser.NestedExpContext ctx) {
return visit(ctx.expression());
}
@Override
public ExpressionNode visitFunctionLiteral(TweakFlowParser.FunctionLiteralContext ctx) {
TweakFlowParser.FunctionHeadContext head = ctx.functionHead();
Type declaredReturnType;
if (head.dataType() == null){
declaredReturnType = Types.ANY;
}
else{
declaredReturnType = Types.byName(head.dataType().getText());
}
LinkedHashMap paramMap = new LinkedHashMap<>();
for (TweakFlowParser.ParamDefContext defContext : head.paramsList().paramDef()) {
Type declaredType;
if (defContext.dataType() == null){
declaredType = Types.ANY;
}
else{
declaredType = Types.byName(defContext.dataType().getText());
}
ExpressionNode defaultValue;
if (defContext.expression() == null){
defaultValue = new NilNode().setSourceInfo(srcOf(parseUnit, defContext.identifier()));
}
else{
defaultValue = visit(defContext.expression());
}
String name = identifier(defContext.identifier().getText());
if (paramMap.containsKey(name)){
throw new LangException(LangError.ALREADY_DEFINED, name+" already defined", srcOf(parseUnit, defContext));
}
paramMap.put(name, new ParameterNode()
.setSymbolName(name)
.setIndex(paramMap.size())
.setDeclaredType(declaredType)
.setDefaultValue(defaultValue)
.setSourceInfo(srcOf(parseUnit, defContext)));
}
Parameters parameters = new Parameters()
.setSourceInfo(srcOf(parseUnit, ctx))
.setMap(paramMap);
if (ctx.expression() != null){
ExpressionNode expression = visit(ctx.expression());
return new FunctionNode()
.setExpression(addImplicitCast(declaredReturnType, expression))
.setParameters(parameters)
.setDeclaredReturnType(declaredReturnType)
.setSourceInfo(srcOf(parseUnit, ctx));
} else if (ctx.viaDec() != null){
ViaNode via = new ViaBuilder(parseUnit).visit(ctx.viaDec());
return new FunctionNode()
.setVia(via)
.setParameters(parameters)
.setDeclaredReturnType(declaredReturnType)
.setSourceInfo(srcOf(parseUnit, ctx));
}
throw new AssertionError("unknown function definition: "+ctx);
}
@Override
public ExpressionNode visitLocalReference(TweakFlowParser.LocalReferenceContext ctx) {
ArrayList elements = new ArrayList<>();
for (TweakFlowParser.IdentifierContext identifierContext : ctx.identifier()) {
elements.add(identifier(identifierContext.getText()));
}
return new ReferenceNode()
.setAnchor(ReferenceNode.Anchor.LOCAL)
.setElements(elements)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitGlobalReference(TweakFlowParser.GlobalReferenceContext ctx) {
ArrayList elements = new ArrayList<>();
for (TweakFlowParser.IdentifierContext identifierContext : ctx.identifier()) {
elements.add(identifier(identifierContext.getText()));
}
return new ReferenceNode()
.setAnchor(ReferenceNode.Anchor.GLOBAL)
.setElements(elements)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitLibraryReference(TweakFlowParser.LibraryReferenceContext ctx) {
ArrayList elements = new ArrayList<>();
for (TweakFlowParser.IdentifierContext identifierContext : ctx.identifier()) {
elements.add(identifier(identifierContext.getText()));
}
return new ReferenceNode()
.setAnchor(ReferenceNode.Anchor.LIBRARY)
.setElements(elements)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitModuleReference(TweakFlowParser.ModuleReferenceContext ctx) {
ArrayList elements = new ArrayList<>();
for (TweakFlowParser.IdentifierContext identifierContext : ctx.identifier()) {
elements.add(identifier(identifierContext.getText()));
}
return new ReferenceNode()
.setAnchor(ReferenceNode.Anchor.MODULE)
.setElements(elements)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitTryCatchExp(TweakFlowParser.TryCatchExpContext ctx) {
ExpressionNode tryExpression = visit(ctx.expression(0));
ExpressionNode catchExpression = visit(ctx.expression(1));
VarDecNode caughtException = null;
VarDecNode caughtTrace = null;
if (ctx.catchDeclaration() instanceof TweakFlowParser.CatchErrorContext){
TweakFlowParser.CatchErrorContext catchDeclaration = (TweakFlowParser.CatchErrorContext) ctx.catchDeclaration();
String exceptionName = identifier(catchDeclaration.identifier().getText());
caughtException = new VarDecNode()
.setDeclaredType(Types.ANY)
.setSymbolName(exceptionName)
.setSourceInfo(srcOf(parseUnit, catchDeclaration.identifier()));
}
else if (ctx.catchDeclaration() instanceof TweakFlowParser.CatchErrorAndTraceContext){
TweakFlowParser.CatchErrorAndTraceContext catchDeclaration = (TweakFlowParser.CatchErrorAndTraceContext) ctx.catchDeclaration();
String exceptionName = identifier(catchDeclaration.identifier().get(0).getText());
caughtException = new VarDecNode()
.setDeclaredType(Types.ANY)
.setSymbolName(exceptionName)
.setSourceInfo(srcOf(parseUnit, catchDeclaration.identifier().get(0)));
String traceName = identifier(catchDeclaration.identifier().get(1).getText());
caughtTrace = new VarDecNode()
.setDeclaredType(Types.ANY)
.setSymbolName(traceName)
.setSourceInfo(srcOf(parseUnit, catchDeclaration.identifier().get(1)));
}
return new TryCatchNode()
.setTryExpression(tryExpression)
.setCatchExpression(catchExpression)
.setCaughtException(caughtException)
.setCaughtTrace(caughtTrace)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitThrowErrorExp(TweakFlowParser.ThrowErrorExpContext ctx) {
ExpressionNode exception = visit(ctx.expression());
return new ThrowNode()
.setExceptionExpression(exception)
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitCastExp(TweakFlowParser.CastExpContext ctx) {
ExpressionNode exp = visit(ctx.expression());
Type type = type(ctx.dataType());
if (exp.getValueType().canAttemptCastTo(type)){
return new CastNode()
.setExpression(exp)
.setTargetType(type)
.setSourceInfo(srcOf(parseUnit, ctx));
}
else{
throw new LangException(LangError.INCOMPATIBLE_TYPES, "cannot cast "+exp.getValueType().name()+" to "+type.name(), srcOf(parseUnit, ctx));
}
}
@Override
public ExpressionNode visitIfExp(TweakFlowParser.IfExpContext ctx) {
return new IfNode()
.setCondition(addImplicitCast(Types.BOOLEAN, visit(ctx.expression(0))))
.setThenExpression(visit(ctx.expression(1)))
.setElseExpression(visit(ctx.expression(2)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitIsExp(TweakFlowParser.IsExpContext ctx) {
return new IsNode()
.setExpression(visit(ctx.expression()))
.setCompareType(type(ctx.dataType()))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitConcatExp(TweakFlowParser.ConcatExpContext ctx) {
return new StringConcatNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitBoolAndExp(TweakFlowParser.BoolAndExpContext ctx) {
return new AndNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitBoolOrExp(TweakFlowParser.BoolOrExpContext ctx) {
return new OrNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitEqualExp(TweakFlowParser.EqualExpContext ctx) {
return new EqualNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitValueAndTypeEqualsExp(TweakFlowParser.ValueAndTypeEqualsExpContext ctx) {
return new ValueAndTypeEqualsNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitNotValueAndTypeEqualsExp(TweakFlowParser.NotValueAndTypeEqualsExpContext ctx) {
return new NotValueAndTypeEqualsNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitLessThanExp(TweakFlowParser.LessThanExpContext ctx) {
return new LessThanNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitLessThanOrEqualToExp(TweakFlowParser.LessThanOrEqualToExpContext ctx) {
return new LessThanOrEqualNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitGreaterThanExp(TweakFlowParser.GreaterThanExpContext ctx) {
return new GreaterThanNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitGreaterThanOrEqualToExp(TweakFlowParser.GreaterThanOrEqualToExpContext ctx) {
return new GreaterThanOrEqualNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitUnaryMinusExp(TweakFlowParser.UnaryMinusExpContext ctx) {
return new NegateNode()
.setExpression(visit(ctx.expression()))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitAddExp(TweakFlowParser.AddExpContext ctx) {
return new PlusNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitMultExp(TweakFlowParser.MultExpContext ctx) {
return new MultNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitPowExp(TweakFlowParser.PowExpContext ctx) {
return new PowNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitContainerAccessExp(TweakFlowParser.ContainerAccessExpContext ctx) {
// a[:foo 0 "bar"]
TweakFlowParser.ContainerAccessKeySequenceContext seq = ctx.containerAccessKeySequence();
// make keys list from the key sequence
ExpressionNode keys = splatEnabledListExpression(srcOf(parseUnit, seq), seq.children);
return new ContainerAccessNode()
.setKeysExpression(keys)
.setContainerExpression(visit(ctx.expression()))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitNotEqualExp(TweakFlowParser.NotEqualExpContext ctx) {
return new NotEqualNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitBoolNotExp(TweakFlowParser.BoolNotExpContext ctx) {
return new NotNode()
.setExpression(visit(ctx.expression()))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitSubExp(TweakFlowParser.SubExpContext ctx) {
return new MinusNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitModExp(TweakFlowParser.ModExpContext ctx) {
return new ModNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitDivExp(TweakFlowParser.DivExpContext ctx) {
return new DivNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitIntDivExp(TweakFlowParser.IntDivExpContext ctx) {
return new IntDivNode()
.setLeftExpression(visit(ctx.expression(0)))
.setRightExpression(visit(ctx.expression(1)))
.setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitThreadExp(TweakFlowParser.ThreadExpContext ctx) {
// ->> (arg) f g h
// h(g(f(arg)))
// functions in order f, g, h
List functions = ctx.expression().stream()
.map(this::visit)
.collect(Collectors.toList());
ExpressionNode arg = visit(ctx.threadArg());
// fold into nested calls
ExpressionNode left = arg;
for (ExpressionNode right : functions) {
left = new CallNode()
.setExpression(right)
.setArguments(new Arguments()
.setSourceInfo(left.getSourceInfo().copy())
.setList(Collections.singletonList(
new PositionalArgumentNode()
.setSourceInfo(left.getSourceInfo().copy())
.setIndex(0)
.setExpression(left)
)))
.setSourceInfo(right.getSourceInfo().copy());
}
return left;
}
// arguments are not an expression
// hence visitArgs cannot participate in typed visitor pattern directly
private Arguments makeArgs(TweakFlowParser.ArgsContext ctx) {
ArrayList args = new ArrayList<>();
if (ctx.children != null){
int idx = 0;
for (ParseTree child : ctx.children) {
ArgumentNode arg;
if (child instanceof TweakFlowParser.PositionalArgContext){
TweakFlowParser.PositionalArgContext pArg = (TweakFlowParser.PositionalArgContext) child;
arg = new PositionalArgumentNode()
.setSourceInfo(srcOf(parseUnit, pArg))
.setExpression(visit(pArg.expression()))
.setIndex(idx);
}
else if (child instanceof TweakFlowParser.NamedArgContext){
TweakFlowParser.NamedArgContext nArg = (TweakFlowParser.NamedArgContext) child;
arg = new NamedArgumentNode()
.setSourceInfo(srcOf(parseUnit, nArg))
.setExpression(visit(nArg.expression()))
.setName(identifier(nArg.identifier().getText()));
}
else if (child instanceof TweakFlowParser.SplatArgContext){
TweakFlowParser.SplatArgContext sArg = (TweakFlowParser.SplatArgContext) child;
arg = new SplatArgumentNode()
.setSourceInfo(srcOf(parseUnit, sArg))
.setExpression(visit(sArg.splat().expression()))
.setIndex(idx);
}
else if (child instanceof TerminalNode && child.getText().equals(",")){ // skip ',' separators
continue;
}
else {
throw new AssertionError("unknown argument type: "+child);
}
args.add(arg);
idx += 1;
}
}
return new Arguments()
.setSourceInfo(srcOf(parseUnit, ctx))
.setList(args);
}
@Override
public ExpressionNode visitKeyLiteral(TweakFlowParser.KeyLiteralContext ctx) {
return new StringNode(key(ctx)).setSourceInfo(srcOf(parseUnit, ctx));
}
@Override
public ExpressionNode visitCallExp(TweakFlowParser.CallExpContext ctx) {
ExpressionNode expression = visit(ctx.expression());
return new CallNode()
.setExpression(expression)
.setArguments(makeArgs(ctx.args()))
.setSourceInfo(srcOf(parseUnit, ctx));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy