Skip to content

Commit

Permalink
Inline singleton case
Browse files Browse the repository at this point in the history
For example, `fn x => case x of y => y + 1`
becomes `fn x => x + 1`.

Change some 'visit()' methods from `public` to `protected`.
  • Loading branch information
julianhyde committed Jan 19, 2024
1 parent c2e4c8d commit 77809e3
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/main/java/net/hydromatic/morel/compile/Analyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private Analysis result() {
use(idPat);
}

@Override public void visit(Core.Id id) {
@Override protected void visit(Core.Id id) {
use(id.idPat).useCount++;
super.visit(id);
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/net/hydromatic/morel/compile/Compiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -360,11 +360,11 @@ private static class PatternBinder extends Visitor {
this.bindings = bindings;
}

@Override public void visit(Core.IdPat idPat) {
@Override protected void visit(Core.IdPat idPat) {
bindPattern(typeSystem, bindings, idPat);
}

@Override public void visit(Core.AsPat asPat) {
@Override protected void visit(Core.AsPat asPat) {
bindPattern(typeSystem, bindings, asPat);
super.visit(asPat);
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/net/hydromatic/morel/compile/EnvShuttle.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,26 @@ protected EnvShuttle bind(List<Binding> bindingList) {
return core.match(match.pos, pat2, match.exp.accept(bind(bindings)));
}

@Override public Core.Exp visit(Core.Let let) {
@Override protected Core.Exp visit(Core.Let let) {
final List<Binding> bindings = new ArrayList<>();
Compiles.bindPattern(typeSystem, bindings, let.decl);
return let.copy(let.decl.accept(this), let.exp.accept(bind(bindings)));
}

@Override public Core.Exp visit(Core.Local local) {
@Override protected Core.Exp visit(Core.Local local) {
final List<Binding> bindings = new ArrayList<>();
Compiles.bindDataType(typeSystem, bindings, local.dataType);
return local.copy(local.dataType, local.exp.accept(bind(bindings)));
}

@Override public Core.RecValDecl visit(Core.RecValDecl recValDecl) {
@Override protected Core.RecValDecl visit(Core.RecValDecl recValDecl) {
final List<Binding> bindings = new ArrayList<>();
recValDecl.list.forEach(decl ->
Compiles.bindPattern(typeSystem, bindings, decl.pat));
return recValDecl.copy(bind(bindings).visitList(recValDecl.list));
}

@Override public Core.Exp visit(Core.From from) {
@Override protected Core.Exp visit(Core.From from) {
List<Binding> bindings = ImmutableList.of();
final List<Core.FromStep> steps = new ArrayList<>();
for (Core.FromStep step : from.steps) {
Expand Down
55 changes: 48 additions & 7 deletions src/main/java/net/hydromatic/morel/compile/Inliner.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@
import net.hydromatic.morel.type.FnType;
import net.hydromatic.morel.type.PrimitiveType;
import net.hydromatic.morel.type.TypeSystem;
import net.hydromatic.morel.util.Pair;

import com.google.common.collect.ImmutableMap;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import java.util.Map;

import static net.hydromatic.morel.ast.CoreBuilder.core;

import static java.util.Objects.requireNonNull;

/**
* Shuttle that inlines constant values.
*/
public class Inliner extends EnvShuttle {
private final @Nullable Analyzer.Analysis analysis;
private final Analyzer.@Nullable Analysis analysis;

/** Private constructor. */
private Inliner(TypeSystem typeSystem, Environment env,
Expand All @@ -52,22 +58,22 @@ private Inliner(TypeSystem typeSystem, Environment env,
*
* <p>If {@code analysis} is null, no variables are inlined. */
public static Inliner of(TypeSystem typeSystem, Environment env,
@Nullable Analyzer.Analysis analysis) {
Analyzer.@Nullable Analysis analysis) {
return new Inliner(typeSystem, env, analysis);
}

@Override protected Inliner push(Environment env) {
return new Inliner(typeSystem, env, analysis);
}

@Override public Core.Exp visit(Core.Id id) {
@Override protected Core.Exp visit(Core.Id id) {
final Binding binding = env.getOpt(id.idPat);
if (binding != null
&& !binding.parameter) {
if (binding.exp != null) {
final Analyzer.Use use =
analysis == null ? Analyzer.Use.MULTI_UNSAFE
: analysis.map.get(id.idPat);
: requireNonNull(analysis.map.get(id.idPat));
switch (use) {
case ATOMIC:
case ONCE_SAFE:
Expand Down Expand Up @@ -143,12 +149,47 @@ public static Inliner of(TypeSystem typeSystem, Environment env,
return apply2;
}

@Override public Core.Exp visit(Core.Let let) {
@Override protected Core.Exp visit(Core.Case caseOf) {
final Core.Exp exp = caseOf.exp.accept(this);
final List<Core.Match> matchList = visitList(caseOf.matchList);
if (matchList.size() == 1) {
final Map<Core.Id, Core.Id> substitution =
getSub(exp, matchList.get(0));
if (substitution != null) {
return Replacer.substitute(typeSystem, substitution,
matchList.get(0).exp);
}
}
return caseOf.copy(exp, matchList);
}

private @Nullable Map<Core.Id, Core.Id> getSub(Core.Exp exp,
Core.Match match) {
if (exp.op == Op.ID && match.pat.op == Op.ID_PAT) {
return ImmutableMap.of(core.id((Core.IdPat) match.pat), (Core.Id) exp);
}
if (exp.op == Op.TUPLE && match.pat.op == Op.TUPLE_PAT) {
final Core.Tuple tuple = (Core.Tuple) exp;
final Core.TuplePat tuplePat = (Core.TuplePat) match.pat;
if (tuple.args.stream().allMatch(arg -> arg.op == Op.ID)
&& tuplePat.args.stream().allMatch(arg -> arg.op == Op.ID_PAT)) {
final ImmutableMap.Builder<Core.Id, Core.Id> builder =
ImmutableMap.builder();
Pair.forEach(tuple.args, tuplePat.args, (arg, pat) ->
builder.put(core.id((Core.IdPat) pat), (Core.Id) arg));
return builder.build();
}
}
return null;
}

@Override protected Core.Exp visit(Core.Let let) {
final Analyzer.Use use =
analysis == null
? Analyzer.Use.MULTI_UNSAFE
: let.decl instanceof Core.NonRecValDecl
? analysis.map.get(((Core.NonRecValDecl) let.decl).pat)
? requireNonNull(
analysis.map.get(((Core.NonRecValDecl) let.decl).pat))
: Analyzer.Use.MULTI_UNSAFE;
switch (use) {
case DEAD:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ private Core.From toFrom(Core.Exp exp) {
}
}

@Override public Core.Exp visit(Core.From from) {
@Override protected Core.Exp visit(Core.From from) {
final Core.From from2 = (Core.From) super.visit(from);
if (from2.steps.size() > 0) {
if (!from2.steps.isEmpty()) {
final Core.FromStep step = from2.steps.get(0);
if (step instanceof Core.Scan
&& ((Core.Scan) step).exp.op == Op.FROM
Expand Down
57 changes: 57 additions & 0 deletions src/main/java/net/hydromatic/morel/compile/Replacer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Licensed to Julian Hyde under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Julian Hyde licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package net.hydromatic.morel.compile;

import net.hydromatic.morel.ast.Core;
import net.hydromatic.morel.type.TypeSystem;

import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
* Replaces identifiers with other identifiers.
*/
public class Replacer extends EnvShuttle {
private final Map<Core.Id, Core.Id> substitution;

private Replacer(TypeSystem typeSystem, Environment env,
Map<Core.Id, Core.Id> substitution) {
super(typeSystem, env);
this.substitution = requireNonNull(substitution);
}

static Core.Exp substitute(TypeSystem typeSystem,
Map<Core.Id, Core.Id> substitution, Core.Exp exp) {
final Replacer replacer =
new Replacer(typeSystem, Environments.empty(), substitution);
return exp.accept(replacer);
}

@Override protected Replacer push(Environment env) {
return new Replacer(typeSystem, env, substitution);
}

@Override protected Core.Exp visit(Core.Id id) {
final Core.Id id2 = substitution.get(id);
return id2 != null ? id2 : id;
}
}

// End Replacer.java
40 changes: 40 additions & 0 deletions src/test/java/net/hydromatic/morel/InlineTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,46 @@ private String v(int i) {
hasToString(core1))
.assertEval(isUnordered(list(list(Unit.INSTANCE, 10))));
}

/** Tests that a singleton {@code case} is inlined. */
@Test void testInlineCase() {
final String ml = "let\n"
+ " val f = fn x => case x of y => y + 2\n"
+ "in\n"
+ " f 3\n"
+ "end";
ml(ml)
.assertCore(0,
hasToString("val it = "
+ "let val f = fn x => case x of y => y + 2 in f 3 end"))
.assertCore(2, hasToString("val it = let val x = 3 in x + 2 end"))
.assertEval(is(5));
}

/** Tests that a singleton {@code case} is inlined. */
@Test void testInlineCase2() {
final String ml = "let\n"
+ " val f = fn (x, y) => case (x, y) of (x1, y1) => x1 - y1\n"
+ "in\n"
+ " f (13, 5)\n"
+ "end";
ml(ml)
.assertCore(0,
hasToString("val it = "
+ "let"
+ " val f = fn v0 => "
+ "case v0 of (x, y) => "
+ "case (x, y) of (x1, y1) => x1 - y1 "
+ "in"
+ " f (13, 5) "
+ "end"))
.assertCore(2,
hasToString("val it = "
+ "let val v0 = (13, 5) "
+ "in case v0 of (x, y) => -:int (x, y) "
+ "end"))
.assertEval(is(8));
}
}

// End InlineTest.java

0 comments on commit 77809e3

Please sign in to comment.