package scotch.compiler.analyzer; import static java.util.Arrays.asList; import static scotch.compiler.syntax.value.Values.apply; import static scotch.compiler.text.SourceLocation.source; import static scotch.compiler.text.SourcePoint.point; import static scotch.util.TestUtil.access; import static scotch.util.TestUtil.arg; import static scotch.util.TestUtil.capture; import static scotch.util.TestUtil.equal; import static scotch.util.TestUtil.field; import static scotch.util.TestUtil.id; import static scotch.util.TestUtil.ignore; import static scotch.util.TestUtil.literal; import static scotch.util.TestUtil.matcher; import static scotch.util.TestUtil.pattern; import static scotch.util.TestUtil.struct; import static scotch.symbol.Operator.operator; import static scotch.symbol.Symbol.symbol; import static scotch.symbol.SymbolEntry.mutableEntry; import static scotch.symbol.Value.Fixity.RIGHT_INFIX; import static scotch.symbol.descriptor.DataFieldDescriptor.field; import static scotch.compiler.syntax.type.Types.sum; import static scotch.compiler.syntax.type.Types.t; import static scotch.compiler.syntax.type.Types.var; import java.util.function.Function; import org.junit.Test; import scotch.compiler.Compiler; import scotch.compiler.IsolatedCompilerTest; import scotch.compiler.analyzer.PrecedenceParser.ArityMismatch; import scotch.compiler.syntax.StubResolver; import scotch.compiler.syntax.definition.DefinitionGraph; import scotch.symbol.SymbolEntry; import scotch.symbol.descriptor.DataConstructorDescriptor; public class PrecedenceParserTest extends IsolatedCompilerTest { @Test public void shouldShuffleTwoPlusTwo() { compile( "module scotch.test", "left infix 7 (+)", "four = 2 + 2" ); shouldNotHaveErrors(); shouldHaveValue("scotch.test.four", apply( apply( id("scotch.test.(+)", t(0)), literal(2), t(1) ), literal(2), t(2) )); } @Test public void shouldShufflePattern() { compile( "module scotch.test", "right infix 1 ($)", "x $ y = x y" ); shouldNotHaveErrors(); shouldHaveValue("scotch.test.($)", matcher("scotch.test.($#0)", t(7), asList(arg("#0", t(5)), arg("#1", t(6))), pattern( "scotch.test.($#0#0)", asList(capture(arg("#0", t(8)), "x", t(0)), capture(arg("#1", t(9)), "y", t(2))), apply(id("x", t(3)), id("y", t(4)), t(10)) ))); } @Test public void shouldTranslateEqualMatchToUseEq() { compile( "module scotch.test", "fib 0 = 0" ); shouldHaveValue("scotch.test.fib", matcher("scotch.test.(fib#0)", t(2), arg("#0", t(1)), pattern( "scotch.test.fib#0#0", asList(equal(arg("#0", t(3)), literal(0))), literal(0) ))); } @Test public void shouldReportArityMismatchInPattern() { compile( "module scotch.test", "fn a b = a b", "fn a b c = a c" ); shouldHaveErrors(new ArityMismatch(symbol("scotch.test.(fn#0)"), 2, 3, source("<unknown>", point(32, 3, 1), point(46, 3, 15)))); } @Test public void shouldParsePrecedenceOfConstructorOperator() { compile( "module scotch.test", "import scotch.data.list", "tail (_:xs) = xs" ); shouldNotHaveErrors(); shouldHaveValue("scotch.test.tail", matcher("scotch.test.(tail#0)", t(7), arg("#0", t(6)), pattern( "scotch.test.(tail#0#0)", asList(struct(arg("#0", t(11)), ":", t(8), asList( field("_0", t(9), ignore(t(2))), field("_1", t(10), capture(access(arg("#0", t(11)), "_1", t(13)), "xs", t(4))) ))), id("xs", t(5)) ))); } @Test public void shouldParsePrecedenceOfNestedConstructorOperator() { compile( "module scotch.test", "import scotch.data.list", "secondTail (_:_:xs) = xs" ); shouldNotHaveErrors(); shouldHaveValue("scotch.test.secondTail", matcher("scotch.test.(secondTail#0)", t(9), arg("#0", t(8)), pattern( "scotch.test.(secondTail#0#0)", asList(struct(arg("#0", t(16)), ":", t(13), asList( field("_0", t(14), ignore(t(2))), field("_1", t(15), struct(access(arg("#0", t(16)), "_1", t(18)), ":", t(10), asList( field("_0", t(11), ignore(t(4))), field("_1", t(12), capture(access(access(arg("#0", t(16)), "_1", t(18)), "_1", t(20)), "xs", t(6))) ))) ))), id("xs", t(7)) ))); } @Override protected StubResolver initResolver() { StubResolver resolver = super.initResolver(); SymbolEntry symbolEntry = mutableEntry(symbol("scotch.data.list.(:)")); symbolEntry.defineOperator(operator(RIGHT_INFIX, 5)); symbolEntry .defineDataConstructor(DataConstructorDescriptor .builder(1, symbol("scotch.data.list.[]"), symbol("scotch.data.list.(:)"), "scotch/data/list/ConsList$Cons") .withFields(asList( field(0, "_0", "get_0", var("a").toDescriptor()), field(1, "_1", "get_1", sum("scotch.data.list.[]", var("a")).toDescriptor()) )) .build()); resolver.define(symbolEntry); return resolver; } @Override protected Function<Compiler, DefinitionGraph> compile() { return Compiler::parsePrecedence; } }