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

com.twineworks.tweakflow.repl.ReplState Maven / Gradle / Ivy

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Twineworks GmbH
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.twineworks.tweakflow.repl;

import com.twineworks.tweakflow.lang.TweakFlow;
import com.twineworks.tweakflow.lang.analysis.AnalysisSet;
import com.twineworks.tweakflow.lang.analysis.AnalysisUnit;
import com.twineworks.tweakflow.lang.errors.LangException;
import com.twineworks.tweakflow.lang.interpreter.EvaluationResult;
import com.twineworks.tweakflow.lang.load.loadpath.FilesystemLocation;
import com.twineworks.tweakflow.lang.load.loadpath.LoadPath;
import com.twineworks.tweakflow.lang.load.loadpath.MemoryLocation;
import com.twineworks.tweakflow.lang.load.loadpath.ResourceLocation;
import com.twineworks.tweakflow.lang.parse.ParseResult;
import com.twineworks.tweakflow.lang.runtime.Runtime;
import com.twineworks.tweakflow.util.LangUtil;

import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ReplState {

  private ConcurrentHashMap parseCache = new ConcurrentHashMap<>();
  private final String stdlibPath = "std.tf";

  // currently loaded module
  private String mainModulePath = stdlibPath;
  private List modulePaths = Collections.singletonList(mainModulePath);
  private String mainModuleKey = mainModulePath;

  // file system load path
  private List loadPathElements = new ArrayList<>();
  // resources load path
  private List resourceLoadPathElements = new ArrayList<>();

  // interactive expression evaluation and variable prompt
  private final String interactivePath = "[interactive]";
  private final String promptVarName = "$";
  private final Map varDefs = new LinkedHashMap<>();
  private String promptInput;

  private LoadPath loadPath;

  // evaluation results and convenient derivatives
  private EvaluationResult evaluationResult;
  private Runtime runtime;

  // exit indicator
  private boolean shouldQuit = false;

  // multiLine mode indicator
  private boolean multiLine = false;

  private String input = "";

  public LoadPath getLoadPath() {
    return loadPath;
  }

  public ReplState setLoadPath(LoadPath loadPath) {
    this.loadPath = loadPath;
    return this;
  }

  public List getLoadPathElements() {
    return loadPathElements;
  }

  public String getMainModulePath() {
    return mainModulePath;
  }

  public String getMainModuleKey() {
    return mainModuleKey;
  }

  public Runtime getRuntime(){
    return runtime;
  }

  public ReplState setRuntime(Runtime runtime){
    this.runtime = runtime;
    return this;
  }

  private ReplState setMainModuleKey(String mainModuleKey) {
    this.mainModuleKey = mainModuleKey;
    return this;
  }

  public String getPromptVarName() {
    return promptVarName;
  }

  public ReplState setModulePaths(List modulePaths){
    Objects.requireNonNull(modulePaths);
    if (modulePaths.size() == 0) throw new IllegalArgumentException("modulePaths cannot be empty");
    this.modulePaths = modulePaths;
    mainModulePath = modulePaths.get(0);
    return this;
  }

  public long getAnalysisDurationMillis(){
    if (runtime == null) return 0;
    return runtime.getAnalysisResult().getAnalysisDurationMillis();
  }

  public long getLoadDurationMillis(){
    if (runtime == null) return 0;
    AnalysisSet analysisSet = runtime.getAnalysisResult().getAnalysisSet();
    long loadDuration = 0;
    for (AnalysisUnit analysisUnit : analysisSet.getUnits().values()) {
      loadDuration += analysisUnit.getLoadDurationMillis();
    }
    return loadDuration;
  }


  public String getInteractivePath() {
    return interactivePath;
  }

  public String getStdlibPath() {
    return stdlibPath;
  }

  public Map getVarDefs() {
    return varDefs;
  }

  public EvaluationResult getEvaluationResult() {
    return evaluationResult;
  }

  public ReplState setEvaluationResult(EvaluationResult evaluationResult) {
    this.evaluationResult = evaluationResult;
    return this;
  }

  public boolean shouldQuit() {
    return shouldQuit;
  }

  public ReplState setShouldQuit(boolean shouldQuit) {
    this.shouldQuit = shouldQuit;
    return this;
  }

  public Runtime.Module getMainModule(){
    return runtime.getModules().get(mainModuleKey);
  }

  public Runtime.InteractiveUnit getInteractiveUnit(){
    return (Runtime.InteractiveUnit) runtime.getUnits().getChildren().get(getInteractivePath());
  }

  public Runtime.InteractiveSection getInteractiveSection() {
    return getInteractiveUnit().getSection(mainModuleKey);
  }

  public Runtime.Globals getGlobals(){
    return runtime.getGlobals();
  }

  public Runtime.Units getUnits(){
    return runtime.getUnits();
  }

  public Runtime.Exports getExports(){
    return runtime.getExports();
  }

  public ReplState copy() {
    ReplState copy = new ReplState()
        .setShouldQuit(shouldQuit)
        .setLoadPath(loadPath)
        .setRuntime(runtime)
        .setEvaluationResult(evaluationResult)
        .setModulePaths(new ArrayList<>(modulePaths))
        .setMultiLine(multiLine)
        .setPromptInput(promptInput)
        .setMainModuleKey(mainModuleKey);

    copy.varDefs.putAll(varDefs);
    copy.loadPathElements.addAll(loadPathElements);
    copy.resourceLoadPathElements.addAll(resourceLoadPathElements);
    copy.parseCache.putAll(parseCache);
    return copy;
  }

  public List getResourceLoadPathElements() {
    return resourceLoadPathElements;
  }

  private LoadPath.Builder nonInteractiveLoadPath(){
    LoadPath.Builder loadPathBuilder = new LoadPath.Builder();

    // std resources are first
    loadPathBuilder.add(new ResourceLocation.Builder()
        .allowCaching(true)
        .allowNativeFunctions(true)
        .path(Paths.get("com/twineworks/tweakflow/std"))
        .build()
    );

    // add resource paths
    for (String s : getResourceLoadPathElements()) {
      loadPathBuilder.add(new ResourceLocation.Builder()
          .allowCaching(true)
          .allowNativeFunctions(true)
          .path(Paths.get(s))
          .build());
    }

    // all file-system locations not cached

    // default if none given
    if (getLoadPathElements().size() == 0){
      loadPathBuilder.add(new FilesystemLocation.Builder(Paths.get(".").toAbsolutePath())
          .confineToPath(false)
          .allowCaching(false)
          .allowNativeFunctions(true)
          .build());
    }

    // all file system loading locations mentioned in state
    for (String s : getLoadPathElements()) {
      FilesystemLocation location = new FilesystemLocation.Builder(Paths.get(s))
          .allowCaching(false)
          .confineToPath(true)
          .allowNativeFunctions(true)
          .build();
      loadPathBuilder.add(location);
    }

    return loadPathBuilder;
  }

  private LoadPath makeLoadPath(){

    LoadPath.Builder loadPathBuilder = nonInteractiveLoadPath();
    LoadPath nonInteractive = loadPathBuilder.build();

    // resolved path is used as a key in data structures
    // needed for interactive section to specify scope
    mainModuleKey = nonInteractive.findParseUnit(mainModulePath).getPath();

    // the memory location for the interactive module
    MemoryLocation interactiveLocation = new MemoryLocation.Builder()
        .add(getInteractivePath(), buildInteractiveProgramText())
        .build();

    loadPathBuilder.add(interactiveLocation);

    loadPathBuilder.withParseResultCache(parseCache);
    return loadPathBuilder.build();

  }

  private String buildInteractiveProgramText(){

    StringBuilder builder = new StringBuilder(100);

    // interactive unit for scope of loaded module
    builder
        .append("interactive\n")
        .append("  in_scope ").append(LangUtil.escapeIdentifier(mainModuleKey)).append(" {\n");

    // add defined variables
    Map varDefs = getVarDefs();

    for (String var : varDefs.keySet()) {
      builder
//          .append("    ")
          .append(varDefs.get(var))
          .append("\n")
          .append(";")
          .append("\n");
    }

    // add input expression, if any
    if (getPromptInput() != null){
      builder
//          .append("    ")
          .append(LangUtil.escapeIdentifier(getPromptVarName())).append(": ").append(getPromptInput())
          .append("\n")
          .append(";")
          .append("\n");
    }

    builder.append("}");
    return builder.toString();

  }

  public void evaluate(){

    runtime = null;

    try {
      setLoadPath(makeLoadPath());

      List paths = new ArrayList<>();
      paths.addAll(modulePaths);
      paths.add(getInteractivePath());

      runtime = TweakFlow.compile(getLoadPath(), paths);
      runtime.evaluate();
      setEvaluationResult(EvaluationResult.ok());

    } catch (LangException e){
      setEvaluationResult(EvaluationResult.error(e));
    }

  }

  public String getPromptInput() {
    return promptInput;
  }

  public ReplState setPromptInput(String promptInput) {
    this.promptInput = promptInput;
    return this;
  }

  public boolean isMultiLine() {
    return multiLine;
  }

  public ReplState setMultiLine(boolean multiLine) {
    this.multiLine = multiLine;
    return this;
  }

  public String getInput() {
    return input;
  }

  public ReplState setInput(String input) {
    this.input = input;
    return this;
  }

  public ReplState addInputLine(String line){
    if (input.isEmpty()){
      this.input = line;
    }
    else{
      this.input += "\n"+line;
    }
    return this;
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy