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());
}
}
}