package scotch.compiler.parser; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.stream.Collectors.toList; import static org.apache.commons.lang.StringEscapeUtils.unescapeJava; import static scotch.compiler.syntax.definition.DefinitionEntry.entry; import static scotch.compiler.syntax.definition.DefinitionGraph.createGraph; import static scotch.compiler.syntax.value.Values.apply; import static scotch.compiler.syntax.value.Values.arg; import static scotch.compiler.syntax.value.Values.conditional; import static scotch.compiler.syntax.value.Values.id; import static scotch.compiler.syntax.value.Values.literal; import static scotch.compiler.text.SourceLocation.NULL_SOURCE; import static scotch.compiler.text.SourceLocation.source; import static scotch.compiler.text.SourcePoint.point; import static scotch.compiler.text.TextUtil.repeat; import static scotch.symbol.Symbol.qualified; import static scotch.symbol.Symbol.symbol; import static scotch.symbol.Symbol.unqualified; import static scotch.symbol.Value.Fixity.LEFT_INFIX; import static scotch.symbol.Value.Fixity.PREFIX; import static scotch.symbol.Value.Fixity.RIGHT_INFIX; import static scotch.compiler.syntax.type.Types.ctor; import static scotch.compiler.syntax.type.Types.fn; import static scotch.compiler.syntax.type.Types.sum; import static scotch.compiler.syntax.type.Types.var; import java.math.BigInteger; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import com.google.common.collect.ImmutableList; import lombok.AllArgsConstructor; import lombok.NonNull; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.TerminalNode; import scotch.compiler.parser.ScotchParser.CaptureArgumentContext; import scotch.compiler.parser.ScotchParser.ClassDefinitionContext; import scotch.compiler.parser.ScotchParser.ClassMembersContext; import scotch.compiler.parser.ScotchParser.ClassParameterContext; import scotch.compiler.parser.ScotchParser.ConditionalContext; import scotch.compiler.parser.ScotchParser.ContextParameterContext; import scotch.compiler.parser.ScotchParser.DataConstantContext; import scotch.compiler.parser.ScotchParser.DataConstructorContext; import scotch.compiler.parser.ScotchParser.DataDefinitionContext; import scotch.compiler.parser.ScotchParser.DataFieldContext; import scotch.compiler.parser.ScotchParser.DataRecordContext; import scotch.compiler.parser.ScotchParser.DataTupleContext; import scotch.compiler.parser.ScotchParser.DataTupleParameterContext; import scotch.compiler.parser.ScotchParser.DestructuringArgumentContext; import scotch.compiler.parser.ScotchParser.DestructuringFieldContext; import scotch.compiler.parser.ScotchParser.DoNotationContext; import scotch.compiler.parser.ScotchParser.DoStatementContext; import scotch.compiler.parser.ScotchParser.DrawFromContext; import scotch.compiler.parser.ScotchParser.ExpressionContext; import scotch.compiler.parser.ScotchParser.FixityContext; import scotch.compiler.parser.ScotchParser.FunctionLiteralContext; import scotch.compiler.parser.ScotchParser.GuardCaseContext; import scotch.compiler.parser.ScotchParser.GuardCasesContext; import scotch.compiler.parser.ScotchParser.IgnoreArgumentContext; import scotch.compiler.parser.ScotchParser.InstanceDefinitionContext; import scotch.compiler.parser.ScotchParser.ListArgumentContext; import scotch.compiler.parser.ScotchParser.ListLiteralContext; import scotch.compiler.parser.ScotchParser.ListSignatureContext; import scotch.compiler.parser.ScotchParser.LiteralArgumentContext; import scotch.compiler.parser.ScotchParser.LiteralContext; import scotch.compiler.parser.ScotchParser.ModuleContext; import scotch.compiler.parser.ScotchParser.ModuleImportContext; import scotch.compiler.parser.ScotchParser.ModuleImportsContext; import scotch.compiler.parser.ScotchParser.ModuleMemberContext; import scotch.compiler.parser.ScotchParser.ModuleMembersContext; import scotch.compiler.parser.ScotchParser.NameContext; import scotch.compiler.parser.ScotchParser.OperatorArgumentContext; import scotch.compiler.parser.ScotchParser.OperatorDefinitionContext; import scotch.compiler.parser.ScotchParser.OperatorNameContext; import scotch.compiler.parser.ScotchParser.OperatorNamesContext; import scotch.compiler.parser.ScotchParser.ParenthesizedArgumentContext; import scotch.compiler.parser.ScotchParser.ParenthesizedExpressionContext; import scotch.compiler.parser.ScotchParser.ParenthesizedTypeSignatureContext; import scotch.compiler.parser.ScotchParser.PartialOperatorContext; import scotch.compiler.parser.ScotchParser.PatternArgumentContext; import scotch.compiler.parser.ScotchParser.PatternBodyContext; import scotch.compiler.parser.ScotchParser.PatternContext; import scotch.compiler.parser.ScotchParser.PatternSignatureContext; import scotch.compiler.parser.ScotchParser.PrimaryExpressionContext; import scotch.compiler.parser.ScotchParser.PrimarySignatureContext; import scotch.compiler.parser.ScotchParser.QualifiedTypeContext; import scotch.compiler.parser.ScotchParser.ReferenceContext; import scotch.compiler.parser.ScotchParser.SingletonDataDefinitionContext; import scotch.compiler.parser.ScotchParser.StructFieldContext; import scotch.compiler.parser.ScotchParser.TupleArgumentContext; import scotch.compiler.parser.ScotchParser.TupleElementContext; import scotch.compiler.parser.ScotchParser.TupleLiteralContext; import scotch.compiler.parser.ScotchParser.TupleParameterContext; import scotch.compiler.parser.ScotchParser.TupleSignatureContext; import scotch.compiler.parser.ScotchParser.TupleSignatureElementContext; import scotch.compiler.parser.ScotchParser.TypeArgumentContext; import scotch.compiler.parser.ScotchParser.TypeContextContext; import scotch.compiler.parser.ScotchParser.TypeParameterContext; import scotch.compiler.parser.ScotchParser.TypeSignatureContext; import scotch.compiler.parser.ScotchParser.TypeVariableContext; import scotch.compiler.parser.ScotchParser.UnshuffledArgumentContext; import scotch.compiler.syntax.builder.BuilderUtil; import scotch.compiler.syntax.builder.SyntaxBuilder; import scotch.compiler.syntax.definition.ClassDefinition.ClassDefinitionBuilder; import scotch.compiler.syntax.definition.DataConstructorDefinition; import scotch.compiler.syntax.definition.DataFieldDefinition; import scotch.compiler.syntax.definition.DataTypeDefinition; import scotch.compiler.syntax.definition.Definition; import scotch.compiler.syntax.definition.DefinitionEntry; import scotch.compiler.syntax.definition.DefinitionGraph; import scotch.compiler.syntax.definition.Import; import scotch.compiler.syntax.definition.ImportBlock; import scotch.compiler.syntax.definition.ModuleDefinition; import scotch.compiler.syntax.definition.ModuleImport; import scotch.compiler.syntax.definition.OperatorDefinition; import scotch.compiler.syntax.definition.RootDefinition; import scotch.compiler.syntax.definition.ScopeDefinition; import scotch.compiler.syntax.definition.UnshuffledDefinition; import scotch.compiler.syntax.definition.ValueDefinition; import scotch.compiler.syntax.definition.ValueSignature; import scotch.compiler.syntax.pattern.CaptureMatch; import scotch.compiler.syntax.pattern.EqualMatch; import scotch.compiler.syntax.pattern.IgnorePattern; import scotch.compiler.syntax.pattern.PatternCase; import scotch.compiler.syntax.pattern.PatternMatch; import scotch.compiler.syntax.pattern.StructField; import scotch.compiler.syntax.pattern.StructMatch; import scotch.compiler.syntax.pattern.UnshuffledStructMatch; import scotch.compiler.syntax.reference.DefinitionReference; import scotch.compiler.syntax.scope.Scope; import scotch.compiler.syntax.value.Argument; import scotch.compiler.syntax.value.Conditional; import scotch.compiler.syntax.value.ConstantReference; import scotch.compiler.syntax.value.DataConstructor; import scotch.compiler.syntax.value.DefaultOperator; import scotch.compiler.syntax.value.FunctionValue; import scotch.compiler.syntax.value.Identifier; import scotch.compiler.syntax.value.Initializer; import scotch.compiler.syntax.value.InitializerField; import scotch.compiler.syntax.value.Literal; import scotch.compiler.syntax.value.PatternMatcher; import scotch.compiler.syntax.value.UnshuffledValue; import scotch.compiler.syntax.value.Value; import scotch.compiler.text.SourceLocation; import scotch.symbol.Symbol; import scotch.symbol.SymbolResolver; import scotch.symbol.Value.Fixity; import scotch.compiler.syntax.type.SumType; import scotch.compiler.syntax.type.Type; import scotch.compiler.syntax.type.Types; import scotch.compiler.syntax.util.SymbolGenerator; public class SyntaxTransformer { private static final int MAX_TUPLE_SIZE = 12; private final List<DefinitionEntry> definitions; private final Deque<Scope> scopes; private final Deque<List<String>> memberNames; private final SymbolGenerator symbolGenerator; private String currentModule; public SyntaxTransformer(SymbolResolver resolver, SymbolGenerator symbolGenerator) { this.definitions = new ArrayList<>(); this.scopes = new ArrayDeque<>(asList(Scope.scope(symbolGenerator, resolver))); this.memberNames = new ArrayDeque<>(asList(ImmutableList.of())); this.symbolGenerator = symbolGenerator; } public DefinitionGraph transform(Map<String, List<ModuleContext>> moduleMap) { definition(null, RootDefinition.builder(), builder -> moduleMap.keySet().forEach( moduleName -> builder.withModule(transformModules(moduleName, moduleMap.get(moduleName))))); return createGraph(definitions).build(); } private DefinitionReference collect(Definition definition) { definitions.add(entry(scope(), definition)); return definition.getReference(); } private DefinitionReference createConstructor(DataConstructorDefinition constructor, SumType type) { return scoped(() -> definition(null, ValueDefinition.builder(), value -> { Value body = createConstructorBody(constructor, type); value.withSymbol(constructor.getSymbol()).withBody(body); })); } private Value createConstructorBody(DataConstructorDefinition constructor, SumType type) { if (constructor.isNiladic()) { return node(null, ConstantReference.builder(), constant -> constant .withDataType(constructor.getDataType()) .withSymbol(constructor.getSymbol()) .withConstantField(constructor.getConstantField()) .withType(type)); } else { return scoped(() -> node(null, FunctionValue.builder(), function -> definition(null, ScopeDefinition.builder(), scope -> { Symbol symbol = reserveSymbol(); scope.withSymbol(symbol); function .withSymbol(symbol) .withArguments(constructor.getFields().stream() .map(DataFieldDefinition::toArgument) .collect(toList())) .withBody(node(null, DataConstructor.builder(), ctor -> { ctor.withSymbol(constructor.getSymbol()); ctor.withType(type); ctor.withArguments(constructor.getFields().stream() .map(DataFieldDefinition::toValue) .collect(toList())); })); }))); } } private <D extends Definition, B extends SyntaxBuilder<D>> DefinitionReference definition(ParserRuleContext context, B supplier, Consumer<B> consumer) { return collect(node(context, supplier, consumer)); } private String getTupleName(int size) { return "scotch.data.tuple.(" + repeat(",", size - 1) + ")"; } private void named(Symbol symbol, Consumer<Symbol> function) { memberNames.push(symbol.getMemberNames()); try { function.accept(symbol); } finally { memberNames.pop(); } } private <N, B extends SyntaxBuilder<N>> N node(ParserRuleContext context, B builder, Consumer<B> consumer) { consumer.accept(builder); if (context == null) { builder.withSourceLocation(NULL_SOURCE); } else { builder.withSourceLocation(source( context.getStart().getTokenSource().getSourceName(), point(context.getStart().getStartIndex(), context.getStart().getLine(), context.getStart().getCharPositionInLine() + 1), point(context.getStop().getStopIndex() + 1, context.getStop().getLine(), context.getStop().getCharPositionInLine() + 2))); } return builder.build(); } private Symbol qualify(String memberName) { return qualified(currentModule, memberName); } private Symbol reserveSymbol() { return scope().reserveSymbol(memberNames.peek()); } private Type reserveType() { return scope().reserveType(); } private Scope scope() { return scopes.peek(); } private <T> T scoped(List<Import> imports, Supplier<T> supplier) { scopes.push(scope().enterScope(imports)); try { return supplier.get(); } finally { scopes.pop(); } } private <T> T scoped(String moduleName, Supplier<T> supplier) { scopes.push(scope().enterScope(moduleName)); try { return supplier.get(); } finally { scopes.pop(); } } private <T> T scoped(Supplier<T> supplier) { scopes.push(scope().enterScope()); try { return supplier.get(); } finally { scopes.pop(); } } private PatternMatch transformCaptureArgument(CaptureArgumentContext captureArgument) { if (captureArgument.idVar() != null) { return node(captureArgument, CaptureMatch.builder(), builder -> { builder.withType(reserveType()); builder.withSymbol(unqualified(captureArgument.getText())); builder.withSourceLocation(NULL_SOURCE); }); } else { throw new TransformException(); } } private List<DefinitionReference> transformClassDefinition(ClassDefinitionContext classDefinition) { return asList(definition(classDefinition, new ClassDefinitionBuilder(), builder -> { Map<String, List<Symbol>> typeContext = transformTypeContext(new HashMap<>(), classDefinition.typeContext()); List<Type> classParameters = transformClassParameters(typeContext, classDefinition.classParameters().classParameter()); builder.withSymbol(qualify(classDefinition.idType().getText())); builder.withArguments(classParameters); builder.withMembers(transformClassMembers(typeContext, classDefinition.classMembers())); })); } private List<DefinitionReference> transformClassMembers(Map<String, List<Symbol>> typeContext, ClassMembersContext classMembers) { return classMembers.classMember().stream() .map(classMember -> { if (classMember.pattern() != null) { return transformPattern(classMember.pattern()); } else if (classMember.patternSignature() != null) { return transformPatternSignature(typeContext, classMember.patternSignature()); } else { throw new TransformException(); } }) .flatMap(List::stream) .collect(toList()); } private List<Type> transformClassParameters(Map<String, List<Symbol>> typeContext, List<ClassParameterContext> classParameters) { return classParameters.stream() .map(ClassParameterContext::getText) .map(Types::var) .map(var -> var.withContext(typeContext.getOrDefault(var.getName(), emptyList()))) .collect(toList()); } private Value transformConditional(ConditionalContext conditional) { return node(conditional, Conditional.builder(), builder -> { builder.withCondition(transformExpression(conditional.expression(0))); builder.withWhenTrue(transformExpression(conditional.expression(1))); builder.withWhenFalse(transformExpression(conditional.expression(2))); builder.withType(reserveType()); }); } private void transformContextParameter(Map<String, List<Symbol>> map, ContextParameterContext contextParameterContext) { map.computeIfAbsent(contextParameterContext.idVar().getText(), k -> new ArrayList<>()).add(symbol(contextParameterContext.qualifiedType().getText())); } private DataConstructorDefinition transformDataConstant(Symbol dataTypeSymbol, int ordinal, DataConstantContext dataConstant) { return node(dataConstant, DataConstructorDefinition.builder(), builder -> { builder.withOrdinal(ordinal); builder.withSymbol(qualify(dataConstant.idType().getText())); builder.withDataType(dataTypeSymbol); }); } private DataConstructorDefinition transformDataConstructor(Symbol dataTypeSymbol, int ordinal, DataConstructorContext dataConstructor) { if (dataConstructor.dataConstant() != null) { return transformDataConstant(dataTypeSymbol, ordinal, dataConstructor.dataConstant()); } else if (dataConstructor.dataRecord() != null) { return transformDataRecord(dataTypeSymbol, ordinal, dataConstructor.dataRecord()); } else if (dataConstructor.dataTuple() != null) { return transformDataTuple(dataTypeSymbol, ordinal, dataConstructor.dataTuple()); } else { throw new TransformException(); } } private List<DefinitionReference> transformDataDefinition(DataDefinitionContext dataDefinition) { Symbol dataTypeSymbol = qualify(dataDefinition.idType().getText()); List<Type> parameters = new ArrayList<>(); List<DefinitionReference> definitions = new ArrayList<>(); List<DataConstructorDefinition> constructors = new ArrayList<>(); definitions.add(definition(dataDefinition, DataTypeDefinition.builder(), builder -> { builder.withSymbol(dataTypeSymbol); if (dataDefinition.dataParameters() != null) { dataDefinition.dataParameters().typeVariable().stream() .map(dataParameter -> transformTypeVariable(emptyMap(), dataParameter, emptyList())) .forEach(parameters::add); builder.withParameters(parameters); } AtomicInteger ordinal = new AtomicInteger(); dataDefinition.dataConstructors().dataConstructor().stream() .map(dataConstructor -> transformDataConstructor(dataTypeSymbol, ordinal.getAndIncrement(), dataConstructor)) .forEach(constructor -> { builder.addConstructor(constructor); constructors.add(constructor); }); })); constructors.stream() .map(constructor -> createConstructor(constructor, sum(dataTypeSymbol, parameters))) .forEach(definitions::add); return definitions; } private DataFieldDefinition transformDataField(DataFieldContext dataField, int ordinal) { return node(dataField, DataFieldDefinition.builder(), builder -> { builder.withName(dataField.idVar().getText()); builder.withOrdinal(ordinal); builder.withType(transformType(emptyMap(), dataField.typeSignature())); }); } private DataConstructorDefinition transformDataRecord(Symbol dataTypeSymbol, int ordinal, DataRecordContext dataRecord) { return node(dataRecord, DataConstructorDefinition.builder(), constructor -> { constructor.withSymbol(dataTypeSymbol); constructor.withDataType(dataTypeSymbol); constructor.withOrdinal(ordinal); AtomicInteger fieldOrdinal = new AtomicInteger(); constructor.withFields(dataRecord.dataFields().dataField().stream() .map(dataField -> transformDataField(dataField, fieldOrdinal.getAndIncrement())) .collect(toList())); }); } private DataConstructorDefinition transformDataTuple(Symbol dataTypeSymbol, int ordinal, DataTupleContext dataTuple) { return node(dataTuple, DataConstructorDefinition.builder(), constructor -> { constructor.withSymbol(qualify(dataTuple.idType().getText())); constructor.withDataType(dataTypeSymbol); constructor.withOrdinal(ordinal); AtomicInteger fieldOrdinal = new AtomicInteger(); constructor.withFields(dataTuple.dataTupleParameters().dataTupleParameter().stream() .map(dataField -> transformDataTupleParameter(dataField, fieldOrdinal.getAndIncrement())) .collect(toList())); }); } private DataFieldDefinition transformDataTupleParameter(DataTupleParameterContext dataField, int ordinal) { return node(dataField, DataFieldDefinition.builder(), builder -> { builder.withName("_" + ordinal); builder.withOrdinal(ordinal); builder.withType(transformDataTupleParameterType(dataField)); }); } private Type transformDataTupleParameterType(DataTupleParameterContext dataField) { if (dataField.listParameter() != null) { return transformType(emptyMap(), dataField.listParameter().typeSignature()); } else if (dataField.tupleParameter() != null) { return transformTupleParameter(dataField.tupleParameter()); } else if (dataField.parenthesizedParameter() != null) { return transformType(emptyMap(), dataField.parenthesizedParameter().typeSignature()); } else if (dataField.qualifiedType() != null) { return transformQualifiedType(emptyMap(), dataField.qualifiedType(), emptyList()); } else if (dataField.typeVariable() != null) { return transformTypeVariable(emptyMap(), dataField.typeVariable(), emptyList()); } else { throw new TransformException(); } } private PatternMatch transformDestructuringArgument(DestructuringArgumentContext destructuringArgument) { return node(destructuringArgument, StructMatch.builder(), builder -> { builder.withConstructor(symbol(destructuringArgument.idType().getText())); builder.withType(reserveType()); destructuringArgument.destructuringFields().destructuringField().stream() .map(this::transformDestructuringField) .forEach(builder::withField); }); } private StructField transformDestructuringField(DestructuringFieldContext destructuringField) { return node(destructuringField, StructField.builder(), builder -> { builder.withFieldName(destructuringField.ID_VAR().getText()); builder.withType(reserveType()); if (destructuringField.unshuffledArgument() == null) { builder.withImplicitCapture(); } else { builder.withPatternMatch(transformUnshuffledArgument(destructuringField.unshuffledArgument())); } }); } private Value transformDoNotation(DoNotationContext doNotation) { List<DoExpression> expressions = transformDoStatements(doNotation.doStatements().doStatement()); List<Value> values = expressions.remove(0).toValue(); while (!expressions.isEmpty()) { expressions.remove(0).toValue(values); } UnshuffledValue.Builder builder = UnshuffledValue.builder(); builder.withSourceLocation(NULL_SOURCE); values.forEach(builder::withMember); return builder.build(); } private void transformDoStatement(DoStatementContext doStatement, List<DoStatementContext> doStatements, List<DoExpression> expressions) { if (doStatement.drawFrom() != null) { transformDrawFrom(doStatement.drawFrom(), doStatements, expressions); } else if (doStatement.expression() != null) { if (!doStatements.isEmpty()) { transformDoStatement(doStatements.remove(0), doStatements, expressions); } expressions.add(new ThenExpression(transformExpression(doStatement.expression()))); } else { throw new TransformException(); } } private List<DoExpression> transformDoStatements(List<DoStatementContext> doStatements) { List<DoExpression> expressions = new ArrayList<>(); transformDoStatement(doStatements.remove(0), doStatements, expressions); return expressions; } private void transformDrawFrom(DrawFromContext drawFrom, List<DoStatementContext> doStatements, List<DoExpression> expressions) { PatternMatch match = transformPatternArgument(drawFrom.patternArgument()); if (match instanceof CaptureMatch) { expressions.add(scoped(() -> node(drawFrom, new BindExpressionBuilder(), bind -> definition(drawFrom, ScopeDefinition.builder(), scope -> { Symbol symbol = reserveSymbol(); scope.withSymbol(symbol); bind.withSymbol(symbol); bind.withArgument(arg(NULL_SOURCE, ((CaptureMatch) match).getSymbol().getCanonicalName(), match.getType(), Optional.empty())); bind.withValue(transformExpression(drawFrom.expression())); if (!doStatements.isEmpty()) { transformDoStatement(doStatements.remove(0), doStatements, expressions); } }) ))); } else { throw new UnsupportedOperationException(); // TODO } } private Value transformExpression(ExpressionContext expression) { return node(expression, UnshuffledValue.builder(), builder -> { builder.withSourceLocation(NULL_SOURCE); expression.primaryExpression().stream() .map(this::transformPrimaryExpression) .forEach(builder::withMember); }); } private Fixity transformFixity(FixityContext fixity) { if (fixity.PREFIX() != null) { return PREFIX; } else if (fixity.LEFT() != null) { return LEFT_INFIX; } else if (fixity.RIGHT() != null) { return RIGHT_INFIX; } else { throw new TransformException(); } } private Value transformFunctionLiteral(FunctionLiteralContext functionLiteral) { return scoped(() -> node(functionLiteral, PatternMatcher.builder(), patternMatcher -> definition(functionLiteral, ScopeDefinition.builder(), matcherScope -> named(reserveSymbol(), matcherSymbol -> { matcherScope.withSymbol(matcherSymbol); patternMatcher.withSymbol(matcherSymbol); patternMatcher.withType(reserveType()); patternMatcher.withPatterns(asList(scoped(() -> node(functionLiteral, PatternCase.builder(), patternCaseBuilder -> definition(functionLiteral, ScopeDefinition.builder(), patternCaseScope -> named(reserveSymbol(), patternSymbol -> { patternCaseScope.withSymbol(patternSymbol); patternCaseBuilder.withSymbol(patternSymbol); AtomicInteger counter = new AtomicInteger(); List<PatternMatch> matches = transformPatternArguments(functionLiteral.patternArguments().patternArgument()); List<Argument> arguments = matches.stream() .map(match -> Argument.builder() .withName("#" + counter.getAndIncrement()) .withType(reserveType()) .withSourceLocation(match.getSourceLocation()) .build()) .collect(toList()); patternCaseBuilder.withMatches(matches); patternMatcher.withArguments(arguments); patternCaseBuilder.withBody(transformExpression(functionLiteral.expression())); }) ) )))); } ) ) )); } private PatternMatch transformIgnoreArgument(@SuppressWarnings("unused") IgnoreArgumentContext ignoreArgument) { return node(ignoreArgument, IgnorePattern.builder(), builder -> builder.withType(reserveType())); } private List<DefinitionReference> transformInstanceDefinition(InstanceDefinitionContext instanceDefinition) { throw new UnsupportedOperationException(); // TODO } private PatternMatch transformListArgument(ListArgumentContext listArgument) { throw new UnsupportedOperationException(); // TODO } private Value transformListLiteral(ListLiteralContext listLiteral) { return node(listLiteral, ListBuilder.builder(symbolGenerator), builder -> listLiteral.listElements().expression().forEach( expression -> builder.addValue(transformExpression(expression)))); } private Type transformListSignature(Map<String, List<Symbol>> typeContexts, ListSignatureContext listSignature) { return sum("scotch.data.list.[]", transformType(typeContexts, listSignature.typeSignature())); } private Value transformLiteral(LiteralContext literal) { return node(literal, Literal.builder(), builder -> { if (literal.INT_LITERAL() != null) { builder.withValue(Integer.parseInt(literal.INT_LITERAL().getText())); } else if (literal.HEX_LITERAL() != null) { String text = literal.HEX_LITERAL().getText(); builder.withValue(Integer.parseInt(text.substring(2), 16)); } else if (literal.OCT_LITERAL() != null) { String text = literal.OCT_LITERAL().getText(); builder.withValue(Integer.parseInt(text.substring(1), 8)); } else if (literal.STRING_LITERAL() != null) { String text = literal.STRING_LITERAL().getText(); builder.withValue(unescapeJava(text.substring(1, text.length() - 1))); } else if (literal.CHARACTER_LITERAL() != null) { String text = literal.CHARACTER_LITERAL().getText(); builder.withValue(unescapeJava(text.substring(1, text.length() - 1)).charAt(0)); } else if (literal.BIGINT_LITERAL() != null) { String text = literal.BIGINT_LITERAL().getText(); builder.withValue(new BigInteger(text.substring(0, text.length() - 1))); } else if (literal.BOOL_LITERAL() != null) { builder.withValue("True".equals(literal.BOOL_LITERAL().getText())); } else if (literal.DOUBLE_LITERAL() != null) { builder.withValue(Double.parseDouble(literal.DOUBLE_LITERAL().getText())); } else { throw new TransformException(); } }); } private PatternMatch transformLiteralArgument(LiteralArgumentContext literalArgument) { return node(literalArgument, EqualMatch.builder(), builder -> builder.withValue(transformLiteral(literalArgument.literal()))); } private DefinitionReference transformModule(ModuleContext module) { List<Import> imports = transformModuleImports(module.moduleImports()); return scoped(imports, () -> definition(module, ImportBlock.builder(), builder -> { builder.withImports(imports); builder.withDefinitions(transformModuleMembers(module.moduleMembers())); builder.withSymbol(reserveSymbol()); })); } private Import transformModuleImport(ModuleImportContext moduleImport) { return node(moduleImport, ModuleImport.builder(), builder -> builder.withModuleName(moduleImport.moduleName().getText())); } private List<Import> transformModuleImports(ModuleImportsContext moduleImports) { return transformOptional(moduleImports, ModuleImportsContext::moduleImport, this::transformModuleImport); } private List<DefinitionReference> transformModuleMember(ModuleMemberContext moduleMember) { if (moduleMember.classDefinition() != null) { return transformClassDefinition(moduleMember.classDefinition()); } else if (moduleMember.instanceDefinition() != null) { return transformInstanceDefinition(moduleMember.instanceDefinition()); } else if (moduleMember.dataDefinition() != null) { return transformDataDefinition(moduleMember.dataDefinition()); } else if (moduleMember.singletonDataDefinition() != null) { return transformSingletonDataDefinition(moduleMember.singletonDataDefinition()); } else if (moduleMember.operatorDefinition() != null) { return transformOperatorDefinition(moduleMember.operatorDefinition()); } else if (moduleMember.patternSignature() != null) { return transformPatternSignature(moduleMember.patternSignature()); } else if (moduleMember.pattern() != null) { return transformPattern(moduleMember.pattern()); } else { throw new TransformException(); } } private List<DefinitionReference> transformModuleMembers(ModuleMembersContext moduleMembers) { return Optional.ofNullable(moduleMembers) .map(ModuleMembersContext::moduleMember) .map(item -> item.stream() .map(this::transformModuleMember) .flatMap(List::stream) .collect(toList())) .orElse(emptyList()); } private DefinitionReference transformModules(String moduleName, List<ModuleContext> modules) { currentModule = moduleName; return scoped(currentModule, () -> definition(null, ModuleDefinition.builder(), builder -> { builder.withSymbol(currentModule); modules.stream() .map(this::transformModule) .forEach(builder::withImportScope); })); } private Symbol transformName(NameContext name) { if (name.operatorName() != null) { return transformOperatorName(name.operatorName()); } else if (name.idVar() != null) { return qualify(name.idVar().getText()); } else if (name.idType() != null) { return qualify(name.idType().getText()); } else { throw new TransformException(); } } private PatternMatch transformOperatorArgument(OperatorArgumentContext operatorArgument) { return node(operatorArgument, CaptureMatch.builder(), builder -> { builder.withType(reserveType()); if (operatorArgument.idVar() == null) { builder.withSymbol(symbol(operatorArgument.OPERATOR().getText())); } else { builder.withSymbol(symbol(operatorArgument.idVar().getText())); } }); } private List<DefinitionReference> transformOperatorDefinition(OperatorDefinitionContext operatorDefinition) { Fixity fixity = transformFixity(operatorDefinition.fixity()); int precedence = transformPrecedence(operatorDefinition.INT_LITERAL()); List<Symbol> operatorNames = transformOperatorNames(operatorDefinition.operatorNames()); return operatorNames.stream() .map(symbol -> OperatorDefinition.builder() .withSourceLocation(NULL_SOURCE) .withSymbol(symbol) .withFixity(fixity) .withPrecedence(precedence) .build()) .map(this::collect) .collect(toList()); } private Symbol transformOperatorName(OperatorNameContext operatorName) { if (operatorName.OPEN_PAREN() != null) { return qualify(operatorName.OPERATOR().getText()); } else { return qualify(operatorName.idVar().getText()); } } private Value transformOperatorName(Optional<String> moduleName, NameContext name) { if (name.operatorName().idVar() != null) { String memberName = name.operatorName().idVar().getText(); return DefaultOperator.builder() .withType(reserveType()) .withSymbol(moduleName .map(n -> qualified(n, memberName)) .orElseGet(() -> unqualified(memberName))) .withSourceLocation(NULL_SOURCE) // TODO .build(); } else if (name.operatorName().OPERATOR() != null) { String memberName = name.operatorName().OPERATOR().getText(); return Identifier.builder() .withType(reserveType()) .withSymbol(moduleName .map(n -> qualified(n, memberName)) .orElseGet(() -> unqualified(memberName))) .withSourceLocation(NULL_SOURCE) // TODO .build(); } else { throw new TransformException(); } } private List<Symbol> transformOperatorNames(OperatorNamesContext operatorNames) { return operatorNames.operatorName().stream() .map(this::transformOperatorName) .collect(toList()); } private <A extends ParserRuleContext, B extends ParserRuleContext, C> List<C> transformOptional(A ctx, Function<A, List<B>> getter, Function<B, C> transform) { return Optional.ofNullable(ctx) .map(getter) .map(item -> item.stream() .map(transform) .collect(toList())) .orElse(emptyList()); } private PatternMatch transformParenthesizedArgument(ParenthesizedArgumentContext parenthesizedArgument) { return node(parenthesizedArgument, UnshuffledStructMatch.builder(), builder -> { builder.withType(reserveType()); parenthesizedArgument.unshuffledArgument().patternArgument().stream() .map(this::transformPatternArgument) .forEach(builder::withPatternMatch); }); } private Value transformParenthesizedExpression(ParenthesizedExpressionContext parenthesizedExpression) { return transformExpression(parenthesizedExpression.expression()); } private Type transformParenthesizedTypeSignature(Map<String, List<Symbol>> typeContexts, ParenthesizedTypeSignatureContext parenthesizedTypeSignature) { return transformType(typeContexts, parenthesizedTypeSignature.typeSignature()); } private Value transformPartialOperator(PartialOperatorContext partialOperator) { throw new UnsupportedOperationException(); // TODO } private List<DefinitionReference> transformPattern(PatternContext pattern) { if (pattern.patternArguments().patternArgument().size() == 1) { Symbol symbol = transformValueSymbol(pattern.patternArguments().patternArgument().get(0)); return asList(scoped(() -> definition(pattern, ValueDefinition.builder(), builder -> named(symbol, s -> { builder.withSymbol(symbol); builder.withBody(transformPatternBody(pattern.patternBody())); })))); } else { return asList(scoped(() -> definition(pattern, UnshuffledDefinition.builder(), builder -> named(reserveSymbol(), symbol -> { builder.withSymbol(symbol); builder.withMatches(transformPatternArguments(pattern.patternArguments().patternArgument())); builder.withBody(transformBody(pattern)); })))); } } private Value transformBody(PatternContext pattern) { if (pattern.guardCases() != null) { return transformGuardCases(pattern.guardCases()); } else if (pattern.patternBody() != null) { return transformPatternBody(pattern.patternBody()); } else { throw new TransformException(); } } private Value transformGuardCases(GuardCasesContext guardCases) { return transformGuardCases_(guardCases.guardCase()); } private Value transformGuardCases_(List<GuardCaseContext> guardCases) { if (guardCases.isEmpty()) { return apply(id(NULL_SOURCE, symbol("scotch.lang.raise"), reserveType()), literal(NULL_SOURCE, "Incomplete guard"), reserveType()); } else { GuardCaseContext guardCase = guardCases.remove(0); return conditional( NULL_SOURCE, transformExpression(guardCase.expression()), transformPatternBody(guardCase.patternBody()), transformGuardCases_(guardCases), reserveType() ); } } private PatternMatch transformPatternArgument(PatternArgumentContext patternArgument) { if (patternArgument.captureArgument() != null) { return transformCaptureArgument(patternArgument.captureArgument()); } else if (patternArgument.destructuringArgument() != null) { return transformDestructuringArgument(patternArgument.destructuringArgument()); } else if (patternArgument.ignoreArgument() != null) { return transformIgnoreArgument(patternArgument.ignoreArgument()); } else if (patternArgument.typeArgument() != null) { return transformTypeArgument(patternArgument.typeArgument()); } else if (patternArgument.tupleArgument() != null) { return transformTupleArgument(patternArgument.tupleArgument()); } else if (patternArgument.parenthesizedArgument() != null) { return transformParenthesizedArgument(patternArgument.parenthesizedArgument()); } else if (patternArgument.literalArgument() != null) { return transformLiteralArgument(patternArgument.literalArgument()); } else if (patternArgument.listArgument() != null) { return transformListArgument(patternArgument.listArgument()); } else if (patternArgument.operatorArgument() != null) { return transformOperatorArgument(patternArgument.operatorArgument()); } else { throw new TransformException(); } } private List<PatternMatch> transformPatternArguments(List<PatternArgumentContext> patternArguments) { return patternArguments.stream() .map(this::transformPatternArgument) .collect(toList()); } private Value transformPatternBody(PatternBodyContext patternBody) { if (patternBody.expression() != null) { return transformExpression(patternBody.expression()); } else if (patternBody.doNotation() != null) { return transformDoNotation(patternBody.doNotation()); } else { throw new TransformException(); } } private List<DefinitionReference> transformPatternSignature(PatternSignatureContext patternSignature) { return transformPatternSignature(new HashMap<>(), patternSignature); } private List<DefinitionReference> transformPatternSignature(Map<String, List<Symbol>> typeContext, PatternSignatureContext patternSignature) { Type type = transformType(transformTypeContext(typeContext, patternSignature.typeContext()), patternSignature.typeSignature()); return patternSignature.patternNames().name().stream() .map(name -> definition(patternSignature, ValueSignature.builder(), builder -> { builder.withType(type); builder.withSymbol(transformName(name)); })) .collect(toList()); } private int transformPrecedence(TerminalNode precedence) { try { int value = Integer.parseInt(precedence.getText()); if (value > 9 || value < 0) { throw new TransformException(); } else { return value; } } catch (NumberFormatException exception) { throw new TransformException(); } } private Value transformPrimaryExpression(PrimaryExpressionContext primaryExpression) { if (primaryExpression.conditional() != null) { return transformConditional(primaryExpression.conditional()); } else if (primaryExpression.functionLiteral() != null) { return transformFunctionLiteral(primaryExpression.functionLiteral()); } else if (primaryExpression.listLiteral() != null) { return transformListLiteral(primaryExpression.listLiteral()); } else if (primaryExpression.literal() != null) { return transformLiteral(primaryExpression.literal()); } else if (primaryExpression.parenthesizedExpression() != null) { return transformParenthesizedExpression(primaryExpression.parenthesizedExpression()); } else if (primaryExpression.partialOperator() != null) { return transformPartialOperator(primaryExpression.partialOperator()); } else if (primaryExpression.reference() != null) { return transformReference(primaryExpression.reference()); } else if (primaryExpression.tupleLiteral() != null) { return transformTupleLiteral(primaryExpression.tupleLiteral()); } else { throw new TransformException(); } } private Type transformPrimarySignature(Map<String, List<Symbol>> typeContexts, PrimarySignatureContext primarySignature) { if (primarySignature.qualifiedType() != null) { return transformQualifiedType(typeContexts, primarySignature.qualifiedType(), primarySignature.typeParameter()); } else if (primarySignature.tupleSignature() != null) { return transformTupleSignature(typeContexts, primarySignature.tupleSignature()); } else if (primarySignature.typeVariable() != null) { return transformTypeVariable(typeContexts, primarySignature.typeVariable(), primarySignature.typeParameter()); } else if (primarySignature.listSignature() != null) { return transformListSignature(typeContexts, primarySignature.listSignature()); } else if (primarySignature.parenthesizedTypeSignature() != null) { return transformParenthesizedTypeSignature(typeContexts, primarySignature.parenthesizedTypeSignature()); } else { throw new UnsupportedOperationException(); // TODO } } private Value transformQualifiedName(Optional<String> moduleName, NameContext name) { if (name.operatorName() != null) { return transformOperatorName(moduleName, name); } else { String memberName = name.getText(); return Identifier.builder() .withType(reserveType()) .withSymbol(moduleName .map(n -> qualified(n, memberName)) .orElseGet(() -> unqualified(memberName))) .withSourceLocation(NULL_SOURCE) // TODO .build(); } } private Type transformQualifiedType(Map<String, List<Symbol>> typeContexts, QualifiedTypeContext qualifiedType, List<TypeParameterContext> typeParameters) { return sum(qualifiedType.getText(), typeParameters.stream() .map(parameter -> transformTypeParameter(typeContexts, parameter)) .collect(toList())); } private Value transformReference(ReferenceContext reference) { if (reference.qualifiedName() != null) { return transformQualifiedName(Optional.ofNullable(reference.qualifiedName().moduleName()).map(ParserRuleContext::getText), reference.qualifiedName().name()); } else if (reference.qualifiedType() != null) { Identifier identifier = node(reference, Identifier.builder(), builder -> { builder.withType(reserveType()); builder.withSymbol(symbol(reference.qualifiedType().getText())); }); if (reference.structFields() == null) { return identifier; } else { return node(reference, Initializer.builder(), builder -> { builder.withType(reserveType()); builder.withValue(identifier); reference.structFields().structField().stream() .map(this::transformStructField) .forEach(builder::addField); }); } } else if (reference.operatorReference() != null) { return Identifier.builder() .withSourceLocation(NULL_SOURCE) .withType(reserveType()) .withSymbol(symbol(reference.operatorReference().getText())) .build(); } else { throw new TransformException(); } } private DataConstructorDefinition transformSingletonConstructor(SingletonDataDefinitionContext singletonDataDefinition, Symbol dataTypeSymbol) { return node(singletonDataDefinition, DataConstructorDefinition.builder(), constructor -> { constructor.withSymbol(dataTypeSymbol); constructor.withDataType(dataTypeSymbol); constructor.withOrdinal(0); AtomicInteger ordinal = new AtomicInteger(); constructor.withFields(singletonDataDefinition.dataFields().dataField().stream() .map(dataField -> transformDataField(dataField, ordinal.getAndIncrement())) .collect(toList())); }); } private List<DefinitionReference> transformSingletonDataDefinition(SingletonDataDefinitionContext singletonDataDefinition) { Symbol dataTypeSymbol = qualify(singletonDataDefinition.idType().getText()); List<Type> parameters = new ArrayList<>(); List<DefinitionReference> definitions = new ArrayList<>(); DataConstructorDefinition constructor = transformSingletonConstructor(singletonDataDefinition, dataTypeSymbol); definitions.add(definition(singletonDataDefinition, DataTypeDefinition.builder(), builder -> { builder.withSymbol(dataTypeSymbol); builder.addConstructor(constructor); if (singletonDataDefinition.dataParameters() != null) { singletonDataDefinition.dataParameters().typeVariable().stream() .map(t -> transformTypeVariable(emptyMap(), t, emptyList())) .forEach(parameters::add); builder.withParameters(parameters); } })); definitions.add(createConstructor(constructor, sum(dataTypeSymbol, parameters))); return definitions; } private InitializerField transformStructField(StructFieldContext structField) { return node(structField, InitializerField.builder(), builder -> { builder.withName(structField.idVar().getText()); builder.withValue(transformExpression(structField.expression())); }); } private PatternMatch transformTupleArgument(TupleArgumentContext tupleArgument) { List<UnshuffledArgumentContext> tupleArguments = tupleArgument.tupleArgumentFields().unshuffledArgument(); return node(tupleArgument, StructMatch.builder(), builder -> { builder.withConstructor(symbol(getTupleName(tupleArguments.size()))); builder.withType(reserveType()); AtomicInteger counter = new AtomicInteger(); tupleArguments.stream() .map(argument -> { PatternMatch patternMatch = transformUnshuffledArgument(argument); return node(argument, StructField.builder(), structField -> { structField.withFieldName("_" + counter.getAndIncrement()); structField.withPatternMatch(patternMatch); structField.withType(reserveType()); }); }) .forEach(builder::withField); }); } private Value transformTupleLiteral(TupleLiteralContext tupleLiteral) { List<TupleElementContext> tupleElements = tupleLiteral.tupleElements().tupleElement(); if (tupleElements.size() > MAX_TUPLE_SIZE) { throw new UnsupportedOperationException(); // TODO } else { return node(tupleLiteral, Initializer.builder(), builder -> { builder.withType(reserveType()); builder.withValue(node(tupleLiteral, Identifier.builder(), id -> { id.withType(reserveType()); id.withSymbol(symbol(getTupleName(tupleElements.size()))); })); AtomicInteger counter = new AtomicInteger(); tupleElements.stream() .map(TupleElementContext::expression) .map(expression -> node(expression, InitializerField.builder(), field -> { field.withName("_" + counter.getAndIncrement()); field.withValue(transformExpression(expression)); })) .forEach(builder::addField); }); } } private Type transformTupleParameter(TupleParameterContext tupleParameter) { throw new UnsupportedOperationException(); // TODO } private Type transformTupleSignature(Map<String, List<Symbol>> typeContexts, TupleSignatureContext tupleSignature) { List<TupleSignatureElementContext> elements = tupleSignature.tupleSignatureElements().tupleSignatureElement(); if (elements.size() > MAX_TUPLE_SIZE) { throw new UnsupportedOperationException(); // TODO } else { return sum(getTupleName(elements.size()), elements.stream() .map(element -> transformType(typeContexts, element.typeSignature())) .collect(toList())); } } private Type transformType(Map<String, List<Symbol>> typeContexts, TypeSignatureContext typeSignature) { List<Type> arguments = new ArrayList<Type>() {{ add(transformPrimarySignature(typeContexts, typeSignature.primarySignature())); typeSignature.typeSignature().stream() .map(t -> transformType(typeContexts, t)) .forEach(this::add); }}; Type result = arguments.remove(arguments.size() - 1); while (!arguments.isEmpty()) { result = fn(arguments.remove(arguments.size() - 1), result); } return result; } private PatternMatch transformTypeArgument(TypeArgumentContext typeArgument) { return node(typeArgument, EqualMatch.builder(), builder -> builder.withValue(node(typeArgument, Identifier.builder(), id -> { id.withSymbol(symbol(typeArgument.qualifiedType().getText())); id.withType(reserveType()); }))); } private Map<String, List<Symbol>> transformTypeContext(Map<String, List<Symbol>> map, TypeContextContext typeContext) { if (typeContext == null) { return emptyMap(); } else if (typeContext.contextParameter() != null) { transformContextParameter(map, typeContext.contextParameter()); return map; } else if (typeContext.contextParameters() != null) { typeContext.contextParameters().contextParameter().forEach(t -> transformContextParameter(map, t)); return map; } else { throw new TransformException(); } } private Type transformTypeParameter(Map<String, List<Symbol>> typeContexts, TypeParameterContext parameter) { if (parameter.qualifiedType() != null) { return transformQualifiedType(typeContexts, parameter.qualifiedType(), emptyList()); } else if (parameter.typeVariable() != null) { return transformTypeVariable(typeContexts, parameter.typeVariable(), emptyList()); } else if (parameter.parenthesizedTypeSignature() != null) { return transformParenthesizedTypeSignature(typeContexts, parameter.parenthesizedTypeSignature()); } else { throw new TransformException(); } } private Type transformTypeVariable(Map<String, List<Symbol>> typeContexts, TypeVariableContext typeVariable, List<TypeParameterContext> typeParameters) { String variableName = typeVariable.getText(); if (typeParameters.isEmpty()) { return var(variableName, typeContexts.getOrDefault(variableName, emptyList())); } else { // TODO how to handle multi-parameter constructors List<Type> parameters = new ArrayList<Type>() {{ add(var(variableName, typeContexts.getOrDefault(variableName, emptyList()))); typeParameters.stream() .map(p -> transformTypeParameter(typeContexts, p)) .forEach(this::add); }}; Type result = parameters.remove(parameters.size() - 1); while (!parameters.isEmpty()) { result = ctor(parameters.remove(parameters.size() - 1), result); } return result; } } private PatternMatch transformUnshuffledArgument(UnshuffledArgumentContext unshuffledArgument) { return node(unshuffledArgument, UnshuffledStructMatch.builder(), builder -> { builder.withType(reserveType()); unshuffledArgument.patternArgument().stream() .map(this::transformPatternArgument) .forEach(builder::withPatternMatch); }); } private Symbol transformValueSymbol(PatternArgumentContext patternArgument) { return qualify(patternArgument.captureArgument().getText()); } @AllArgsConstructor private class BindExpression extends DoExpression { @NonNull private final SourceLocation sourceLocation; @NonNull private final Symbol symbol; @NonNull private final Argument argument; @NonNull private final Value value; @Override public List<Value> toValue() { throw new ParseException("Unexpected bind in do-notation", value.getSourceLocation()); } @Override public void toValue(List<Value> values) { List<Value> outerValues = new ArrayList<Value>() {{ UnshuffledValue.Builder unshuffled = UnshuffledValue.builder(); values.forEach(unshuffled::withMember); unshuffled.withSourceLocation(SourceLocation.extent(values.stream() .map(Value::getSourceLocation) .collect(toList()))); add(value); add(Identifier.builder() .withSymbol(qualified("scotch.control.monad", ">>=")) .withType(reserveType()) .withSourceLocation(value.getSourceLocation().getEndPoint()) .build()); add(FunctionValue.builder() .withSymbol(symbol) .withArguments(asList(argument)) .withBody(unshuffled.build()) .withSourceLocation(sourceLocation) .build()); }}; values.clear(); values.addAll(outerValues); } } private final class BindExpressionBuilder implements SyntaxBuilder<BindExpression> { private Optional<SourceLocation> sourceLocation; private Optional<Symbol> symbol; private Optional<Argument> argument; private Optional<Value> value; @Override public BindExpression build() { return new BindExpression( BuilderUtil.require(sourceLocation, "Source location"), BuilderUtil.require(symbol, "Symbol"), BuilderUtil.require(argument, "Argument"), BuilderUtil.require(value, "Value") ); } public BindExpressionBuilder withArgument(Argument argument) { this.argument = Optional.of(argument); return this; } @Override public BindExpressionBuilder withSourceLocation(SourceLocation sourceLocation) { this.sourceLocation = Optional.of(sourceLocation); return this; } public BindExpressionBuilder withSymbol(Symbol symbol) { this.symbol = Optional.of(symbol); return this; } public BindExpressionBuilder withValue(Value value) { this.value = Optional.of(value); return this; } } private abstract class DoExpression { public abstract List<Value> toValue(); public abstract void toValue(List<Value> values); } @AllArgsConstructor private class ThenExpression extends DoExpression { @NonNull private final Value value; @Override public List<Value> toValue() { return new ArrayList<>(asList(value)); } @Override public void toValue(List<Value> values) { values.add(0, value); values.add(1, Identifier.builder() .withSymbol(qualified("scotch.control.monad", ">>")) .withType(reserveType()) .withSourceLocation(value.getSourceLocation().getEndPoint()) .build()); } } }