All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.hydromatic.morel.compile.Inliner Maven / Gradle / Ivy

Go to download

Standard ML interpreter, with relational extensions, implemented in Java

There is a newer version: 0.7.0
Show newest version
/*
 * 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 static java.util.Objects.requireNonNull;
import static net.hydromatic.morel.ast.CoreBuilder.core;
import static net.hydromatic.morel.util.Pair.forEach;

import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.hydromatic.morel.ast.Core;
import net.hydromatic.morel.ast.Op;
import net.hydromatic.morel.eval.Applicable;
import net.hydromatic.morel.eval.Code;
import net.hydromatic.morel.eval.Codes;
import net.hydromatic.morel.eval.Unit;
import net.hydromatic.morel.type.Binding;
import net.hydromatic.morel.type.FnType;
import net.hydromatic.morel.type.PrimitiveType;
import net.hydromatic.morel.type.TypeSystem;
import org.checkerframework.checker.nullness.qual.Nullable;

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

  /** Private constructor. */
  private Inliner(
      TypeSystem typeSystem, Environment env, Analyzer.Analysis analysis) {
    super(typeSystem, env);
    this.analysis = analysis;
  }

  /**
   * Creates an Inliner.
   *
   * 

If {@code analysis} is null, no variables are inlined. */ public static Inliner of( TypeSystem typeSystem, Environment env, Analyzer.@Nullable Analysis analysis) { return new Inliner(typeSystem, env, analysis); } @Override protected Inliner push(Environment env) { return new Inliner(typeSystem, env, analysis); } @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 : requireNonNull(analysis.map.get(id.idPat)); switch (use) { case ATOMIC: case ONCE_SAFE: return binding.exp.accept(this); } } Object v = binding.value; if (v instanceof Macro) { final Macro macro = (Macro) binding.value; final Core.Exp x = macro.expand(typeSystem, env, ((FnType) id.type).paramType); if (x instanceof Core.Literal) { return x; } } if (v != Unit.INSTANCE) { switch (id.type.op()) { case ID: assert id.type instanceof PrimitiveType; return core.literal((PrimitiveType) id.type, v); case FUNCTION_TYPE: assert v instanceof Applicable || v instanceof Macro : v; final BuiltIn builtIn = Codes.BUILT_IN_MAP.get(v); if (builtIn != null) { return core.functionLiteral(typeSystem, builtIn); } // Applicable (including Closure) that does not map to a BuiltIn // is not considered 'constant', mainly because it creates messy // plans. break; default: if (v instanceof Code) { v = ((Code) v).eval(Compiler.EMPTY_ENV); if (v == null) { // Cannot inline SYS_FILE; it requires a session. break; } } return core.valueLiteral(id, v); } } } return super.visit(id); } @Override protected Core.Exp visit(Core.Apply apply) { final Core.Apply apply2 = (Core.Apply) super.visit(apply); if (apply2.fn.op == Op.RECORD_SELECTOR && apply2.arg.op == Op.VALUE_LITERAL) { final Core.RecordSelector selector = (Core.RecordSelector) apply2.fn; @SuppressWarnings("rawtypes") final List list = ((Core.Literal) apply2.arg).unwrap(List.class); final Object o = list.get(selector.slot); if (o instanceof Applicable || o instanceof Macro) { // E.g. apply is '#filter List', o is Codes.LIST_FILTER, // builtIn is BuiltIn.LIST_FILTER. final BuiltIn builtIn = Codes.BUILT_IN_MAP.get(o); if (builtIn != null) { return core.functionLiteral(typeSystem, builtIn); } } return core.valueLiteral(apply2, o); } if (apply2.fn.op == Op.FN) { // Beta-reduction: // (fn x => E) A // becomes // let x = A in E end final Core.Fn fn = (Core.Fn) apply2.fn; return core.let( core.nonRecValDecl(apply2.pos, fn.idPat, apply2.arg), fn.exp); } return apply2; } @Override protected Core.Exp visit(Core.Case caseOf) { final Core.Exp exp = caseOf.exp.accept(this); final List matchList = visitList(caseOf.matchList); if (matchList.size() == 1) { final Map 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 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 builder = ImmutableMap.builder(); 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 ? requireNonNull( analysis.map.get(((Core.NonRecValDecl) let.decl).pat)) : Analyzer.Use.MULTI_UNSAFE; switch (use) { case DEAD: // This declaration has no uses; remove it return let.exp.accept(this); case ATOMIC: case ONCE_SAFE: // This declaration has one use; remove the declaration, and replace its // use inside the expression. final List bindings = new ArrayList<>(); Compiles.bindPattern(typeSystem, bindings, let.decl); return let.exp.accept(bind(bindings)); } return super.visit(let); } } // End Inliner.java





© 2015 - 2025 Weber Informatics LLC | Privacy Policy