package scotch.compiler.analyzer; import static scotch.compiler.error.ParseError.parseError; import static scotch.compiler.syntax.pattern.Patterns.field; import static scotch.util.Either.left; import static scotch.util.Either.right; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import scotch.compiler.error.SyntaxError; import scotch.compiler.syntax.pattern.EqualMatch; import scotch.compiler.syntax.pattern.PatternMatch; import scotch.compiler.syntax.pattern.StructMatch; import scotch.compiler.syntax.pattern.StructMatch.Builder; import scotch.compiler.syntax.scope.Scope; import scotch.util.Either; import scotch.symbol.Symbol; import scotch.symbol.descriptor.DataFieldDescriptor; import scotch.compiler.syntax.type.Type; public class StructMatchShuffler { private static Either<SyntaxError, PatternMatch> success(PatternMatch patternMatch) { return right(patternMatch); } public Either<SyntaxError, PatternMatch> shuffle(Scope scope, List<PatternMatch> patternMatches) { if (patternMatches.size() == 1) { return success(patternMatches.get(0)); } else { try { return success(new Shuffler(scope, patternMatches).shuffle()); } catch (ShuffleException exception) { return left(exception.syntaxError); } } } private static final class ShuffleException extends RuntimeException { private final SyntaxError syntaxError; private ShuffleException(SyntaxError syntaxError) { super(syntaxError.prettyPrint()); this.syntaxError = syntaxError; } } private final class Shuffler { private final Scope scope; private final List<PatternMatch> patternMatches; public Shuffler(Scope scope, List<PatternMatch> patternMatches) { this.scope = scope; this.patternMatches = patternMatches; } public PatternMatch shuffle() { Deque<PatternMatch> input = new ArrayDeque<>(patternMatches); Deque<Either<OperatorPair<EqualMatch>, PatternMatch>> output = new ArrayDeque<>(); Deque<OperatorPair<EqualMatch>> stack = new ArrayDeque<>(); boolean expectsPrefix = isOperator(input.peek()); while (!input.isEmpty()) { if (isOperator(input.peek())) { OperatorPair<EqualMatch> o1 = getOperator(input.poll(), expectsPrefix); while (!stack.isEmpty() && o1.isLessPrecedentThan(stack.peek())) { output.push(left(stack.pop())); } stack.push(o1); expectsPrefix = isOperator(input.peek()); } else { output.push(right(shuffleNext(input))); while (expectsArgument(input)) { output.push(appendStructMatch( output.pop().orElseGet(OperatorPair::getValue), shuffleNext(input) )); } } } while (!stack.isEmpty()) { output.push(left(stack.pop())); } return shuffleApply(output); } private PatternMatch shuffleApply(Deque<Either<OperatorPair<EqualMatch>, PatternMatch>> message) { Deque<PatternMatch> stack = new ArrayDeque<>(); while (!message.isEmpty()) { stack.push(message.pollLast().orElseGet(pair -> { if (pair.isPrefix()) { return createStructMatch(pair.getValue(), stack.pop()); } else { PatternMatch right = stack.pop(); PatternMatch left = stack.pop(); return createStructMatch(pair.getValue(), left, right); } })); } if (stack.size() > 1) { throw new UnsupportedOperationException(); // TODO } return stack.pop(); } private PatternMatch createStructMatch(EqualMatch equalMatch, PatternMatch... fieldMatches) { Symbol constructor = equalMatch.getSymbol(); List<DataFieldDescriptor> fields = scope.getDataConstructor(constructor).get().getFields(); if (fields.size() == fieldMatches.length) { Builder builder = StructMatch.builder() .withType(reserveType()) .withConstructor(constructor) .withSourceLocation(equalMatch.getSourceLocation()); for (int i = 0; i < fields.size(); i++) { builder.withField(field( fieldMatches[i].getSourceLocation(), fields.get(i).getName(), reserveType(), fieldMatches[i] )); } return builder.build(); } else { throw new UnsupportedOperationException(); // TODO } } private Type reserveType() { return scope.reserveType(); } private Either<OperatorPair<EqualMatch>, PatternMatch> appendStructMatch(PatternMatch patternMatch, PatternMatch patternMatch1) { throw new UnsupportedOperationException(); // TODO } private boolean expectsArgument(Deque<PatternMatch> input) { return !input.isEmpty() && expectsArgument_(input); } private boolean expectsArgument_(Deque<PatternMatch> input) { return !isOperator(input.peek()); } private OperatorPair<EqualMatch> getOperator(PatternMatch patternMatch, boolean expectsPrefix) { return patternMatch.asConstructorOperator(scope) .map(pair -> pair.into((match, operator) -> { if (expectsPrefix && !operator.isPrefix()) { throw new ShuffleException(parseError("Unexpected binary operator " + match.getSymbol(), match.getSourceLocation())); } else { return new OperatorPair<>(operator, match); } })) .orElseThrow(() -> new ShuffleException(parseError("Value " + patternMatch.prettyPrint() + " is not an operator", patternMatch.getSourceLocation()))); } private boolean isOperator(PatternMatch value) { return value.isOperator(scope); } private PatternMatch shuffleNext(Deque<PatternMatch> input) { return input.poll().destructure() .map(patternMatches -> StructMatchShuffler.this.shuffle(scope, patternMatches).orElseThrow(ShuffleException::new)) .orElseGet(left -> left); } } }