Package org.rascalmpl.interpreter

Source Code of org.rascalmpl.interpreter.StringTemplateConverter$Visitor$MidAppend

/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:

*   * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
*   * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl
*   * Mark Hills - Mark.Hills@cwi.nl (CWI)
*   * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.interpreter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.imp.pdb.facts.IConstructor;
import org.eclipse.imp.pdb.facts.ISourceLocation;
import org.eclipse.imp.pdb.facts.IString;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.IValueFactory;
import org.rascalmpl.ast.DataTarget;
import org.rascalmpl.ast.Expression;
import org.rascalmpl.ast.MidStringChars.Lexical;
import org.rascalmpl.ast.Name;
import org.rascalmpl.ast.NullASTVisitor;
import org.rascalmpl.ast.Statement;
import org.rascalmpl.ast.StringConstant;
import org.rascalmpl.ast.StringLiteral.NonInterpolated;
import org.rascalmpl.ast.StringMiddle.Interpolated;
import org.rascalmpl.ast.StringMiddle.Mid;
import org.rascalmpl.ast.StringMiddle.Template;
import org.rascalmpl.ast.StringTail.Post;
import org.rascalmpl.ast.StringTemplate.DoWhile;
import org.rascalmpl.ast.StringTemplate.For;
import org.rascalmpl.ast.StringTemplate.IfThen;
import org.rascalmpl.ast.StringTemplate.IfThenElse;
import org.rascalmpl.ast.StringTemplate.While;
import org.rascalmpl.interpreter.result.Result;
import org.rascalmpl.interpreter.result.ResultFactory;
import org.rascalmpl.parser.ASTBuilder;
import org.rascalmpl.values.ValueFactoryFactory;
import org.rascalmpl.values.uptr.Factory;
import org.rascalmpl.values.uptr.SymbolAdapter;
 
public class StringTemplateConverter {
  private static int labelCounter = 0;
 
  public StringTemplateConverter() {
    super();
  }
 
  private Statement surroundWithSingleIterForLoop(ISourceLocation loc, Name label, Statement body) {
    Name dummy = ASTBuilder.make("Name","Lexical",loc, "_");
    Expression var = ASTBuilder.make("Expression","QualifiedName",loc, ASTBuilder.make("QualifiedName", loc, Arrays.asList(dummy)));
    Expression truth = ASTBuilder.make("Expression","Literal",loc, ASTBuilder.make("Literal","Boolean",loc, ASTBuilder.make("BooleanLiteral","Lexical",loc, "true")));
    Expression list = ASTBuilder.make("Expression","List", loc, Arrays.asList(truth));
    Expression enumerator = ASTBuilder.make("Expression","Enumerator",loc, var, list);
    Statement stat = ASTBuilder.make("Statement","For",loc, ASTBuilder.make("Label","Default", loc, label), Arrays.asList(enumerator), body);
    return stat;
  }


  public Statement convert(org.rascalmpl.ast.StringLiteral str) {
    final Name label= ASTBuilder.make("Name","Lexical", str.getLocation(), "#" + labelCounter);
    labelCounter++;
    Statement stat = str.accept(new Visitor(label));
    return surroundWithSingleIterForLoop(str.getLocation(), label, stat);
  }
 
  private static class Visitor extends NullASTVisitor<Statement> {
    private final Name label;

    public Visitor(Name label) {
      this.label = label;
    }

    private Statement makeBlock(ISourceLocation src, Statement ...stats) {
      return makeBlock(src, Arrays.asList(stats));
    }
   
    private Statement makeBlock(ISourceLocation loc, List<Statement> stats) {
      return ASTBuilder.make("Statement","NonEmptyBlock",loc, ASTBuilder.make("Label", "Empty", loc),
          stats);
    }

   
    private class IndentingAppend extends org.rascalmpl.semantics.dynamic.Statement.Append {

      public IndentingAppend(ISourceLocation __param1, DataTarget __param2,
          Statement __param3) {
        super(null, __param2, __param3);
        setSourceLocation(__param1);
      }
     
      @Override
      public Result<IValue> interpret(IEvaluator<Result<IValue>> __eval) {
        Accumulator target = getTarget(__eval);
       
        // TODO refactor this: pull up to Append
        Result<IValue> result = this.getStatement().interpret(__eval);
        IValueFactory vf = ValueFactoryFactory.getValueFactory();
        IValue v = result.getValue();
        if (!(v instanceof IString)) {
          // Ensure that values that are trees are yielding the appropriate string value
          StringBuilder sb = new StringBuilder(500);
          appendToString(v, sb);
          v = vf.string(sb.toString());
        }
        java.lang.String fill = __eval.getCurrentIndent();
        IString content = ((IString)v);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < content.length(); i++) {
          int ch = content.charAt(i);
          sb.appendCodePoint(ch);
          if (ch == '\n') {
            sb.append(fill);
          }
        }
        v = vf.string(sb.toString());
        target.appendString((IString) v);
        return result;
      }
     
      private void appendToString(IValue value, StringBuilder b) {
        if (value.getType() == Factory.Tree) {
          b.append(org.rascalmpl.values.uptr.TreeAdapter.yield((IConstructor) value));
        }
        else if (value.getType().isSubtypeOf(Factory.Type)) {
          b.append(SymbolAdapter.toString((IConstructor) ((IConstructor) value).get("symbol"), false));
        }
        else if (value.getType().isString()) {
          b.append(((IString) value).getValue());
        }
        else {
          b.append(value.toString());
        }
      }
     
    }
   
    private static class ConstAppend extends org.rascalmpl.semantics.dynamic.Statement.Append {
      protected final IString str;

      public ConstAppend(ISourceLocation __param1, DataTarget __param2, String arg) {
        super(null, __param2, null);
        setSourceLocation(__param1);
        str = initString(preprocess(arg));
      }
     
      protected IString initString(IString preprocessedString) {
        return removeMargins(preprocessedString);
      }
     
     
      private IString removeMargins(IString s) {
        // NB: ignored margin indents can only start *after* a new line.
        // So atBeginning is initially false.
        boolean atBeginning = false;
        StringBuffer buf = new StringBuffer();
       
        StringBuilder sb = new StringBuilder(s.length());
        for (int i = 0; i < s.length(); i++) {
          int ch = s.charAt(i);
          if (atBeginning && (ch == ' ' || ch == '\t')) {
            buf.appendCodePoint(ch);
            continue;
          }
          if (atBeginning && ch == '\'') {
            // we've only seen ' ' and/or '\t' so we're about
            // to reach real content, don't add to buf.
            buf = new StringBuffer();
            atBeginning = false;
            continue;
          }
          if (ch == '\n') { // atBeginning &&
            sb.append(buf);
            buf = new StringBuffer();
            atBeginning = true;
            sb.appendCodePoint(ch);
            continue;
          }
          if (atBeginning) {
            // we were in the margin, but found something other
            // than ' ', '\t' and '\'', so anything in buf
            // is actual content; add it.
            sb.append(buf);
            buf = new StringBuffer();
            sb.appendCodePoint(ch);
            atBeginning = false;
            continue;
          }
          sb.appendCodePoint(ch);
        }
       
        // Add trailing whitespace (fixes #543)
        sb.append(buf.toString());
        String jstr = sb.toString();
        // TODO: inline this to avoid another pass over the string.
        return VF.string(org.rascalmpl.interpreter.utils.StringUtils.unescapeSingleQuoteAndBackslash(jstr));
      }
     
      private IString preprocess(String arg) {
        arg = org.rascalmpl.interpreter.utils.StringUtils.unquote(arg);
        // don't unescape ' yet
        arg = org.rascalmpl.interpreter.utils.StringUtils.unescapeBase(arg);
        return VF.string(arg);
      }
     
      @Override
      public Result<IValue> interpret(IEvaluator<Result<IValue>> __eval) {
        Result<IValue> result = ResultFactory.makeResult(str.getType(), str, __eval);
        getTarget(__eval).appendString(str);
        return result;
      }
     
    }
   
   
    private abstract static class IndentingStringFragmentAppend extends ConstAppend {
      private static final Pattern INDENT = Pattern.compile("(?<![\\\\])'([ \t]*)([^']*)$");
      private String indent;
     
      public IndentingStringFragmentAppend(ISourceLocation __param1, DataTarget __param2, String arg) {
        super(__param1, __param2, arg);
      }
     
      @Override
      protected IString initString(IString arg) {
        indent = computeIndent(arg);
        return super.initString(arg);
      }

      private String computeIndent(IString arg) {
        Matcher m = INDENT.matcher(arg.getValue());
        if (m.find()) {
          return m.group(1) + replaceEverythingBySpace(m.group(2));
        }
        return "";
      }
     
      private String replaceEverythingBySpace(String input) {
        StringBuilder sb = new StringBuilder();
        IString is = VF.string(input);
        for (int i = 0; i < is.length(); i++) {
          int ch = is.charAt(i);
          if (ch != ' ' && ch != '\t') {
            sb.append(' ');
          }
          else {
            sb.appendCodePoint(ch);
          }
        }
        return sb.toString();
//        return NONSPACE.matcher(input).replaceAll(" ");
      }

      protected String getIndent() {
        return indent;
      }
     
    }

    private static class PreAppend extends IndentingStringFragmentAppend {
      public PreAppend(ISourceLocation __param1, DataTarget __param2, String arg) {
        super(__param1, __param2, arg);
      }
     
      @Override
      public Result<IValue> interpret(IEvaluator<Result<IValue>> __eval) {
        __eval.indent(getIndent());
        return super.interpret(__eval);
      }
   
    }

    private static class MidAppend extends IndentingStringFragmentAppend {

      public MidAppend(ISourceLocation __param1, DataTarget __param2, String arg) {
        super(__param1, __param2, arg);
      }
     
      @Override
      public Result<IValue> interpret(IEvaluator<Result<IValue>> __eval) {
        __eval.unindent();
        __eval.indent(getIndent());
        return super.interpret(__eval);
      }
   
    }

    private static class PostAppend extends ConstAppend {
      public PostAppend(ISourceLocation __param1, DataTarget __param2, String arg) {
        super(__param1, __param2, arg);
      }
     
      @Override
      public Result<IValue> interpret(IEvaluator<Result<IValue>> __eval) {
        __eval.unindent();
        return super.interpret(__eval);
      }

     
    }   
   
   
    private Statement makeConstAppend(ISourceLocation tree, String str) {
      return new ConstAppend(tree, ASTBuilder.<DataTarget>make("DataTarget","Labeled", tree, label), str);
    }

    private Statement makePostAppend(ISourceLocation tree, String str) {
      return new PostAppend(tree, ASTBuilder.<DataTarget>make("DataTarget","Labeled", tree, label), str);
    }

    private Statement makePreAppend(ISourceLocation tree, String str) {
      return new PreAppend(tree, ASTBuilder.<DataTarget>make("DataTarget","Labeled", tree, label), str);
    }

    private Statement makeMidAppend(ISourceLocation tree, String str) {
      return new MidAppend(tree, ASTBuilder.<DataTarget>make("DataTarget","Labeled", tree, label), str);
    }

    private Statement makeIndentingAppend(Expression exp) {
      ISourceLocation loc = exp.getLocation();
      return new IndentingAppend(exp.getLocation(), ASTBuilder.<DataTarget>make("DataTarget","Labeled", loc, label),
          ASTBuilder.<Statement>make("Statement","Expression", loc, exp));
    }
   
    private  Statement combinePreBodyPost(ISourceLocation src, List<Statement> pre, Statement body, List<Statement> post) {
      List<Statement> stats = new ArrayList<Statement>();
      stats.addAll(pre);
      stats.add(body);
      stats.addAll(post);
      return makeBlock(src, stats);
    }
   
    @Override
    public Statement visitStringLiteralInterpolated(
        org.rascalmpl.ast.StringLiteral.Interpolated x) {
      Statement pre = x.getPre().accept(this);
      Statement exp = makeIndentingAppend(x.getExpression());
      Statement tail = x.getTail().accept(this);
      return makeBlock(x.getLocation(), pre, exp, tail);
    }
   
    @Override
    public Statement visitStringLiteralNonInterpolated(NonInterpolated x) {
      return makeConstAppend(x.getLocation(), ((StringConstant.Lexical)x.getConstant()).getString());
    }
   
    @Override
    public Statement visitStringLiteralTemplate(
        org.rascalmpl.ast.StringLiteral.Template x) {
      Statement pre = x.getPre().accept(this);
      Statement template = x.getTemplate().accept(this);
      Statement tail = x.getTail().accept(this);
      return makeBlock(x.getLocation(), pre, template, tail);
    }
   
 
    @Override
    public Statement visitStringTemplateDoWhile(DoWhile x) {
      Statement body = x.getBody().accept(this);
      return ASTBuilder.makeStat("DoWhile", x.getLocation(), ASTBuilder.make("Label","Empty", x.getLocation()),
          combinePreBodyPost(x.getLocation(), x.getPreStats(), body, x.getPostStats()) , x.getCondition());
    }

    @Override
    public Statement visitStringTemplateFor(For x) {
      Statement body = x.getBody().accept(this);
      return ASTBuilder.makeStat("For", x.getLocation(), ASTBuilder.make("Label","Empty", x.getLocation()), x.getGenerators(),
          combinePreBodyPost(x.getLocation(), x.getPreStats(), body, x.getPostStats()));
    }

    @Override
    public Statement visitStringTemplateIfThen(IfThen x) {
      Statement body = x.getBody().accept(this);
      return ASTBuilder.makeStat("IfThen", x.getLocation(), ASTBuilder.make("Label", "Empty", x.getLocation()), x.getConditions(),
          combinePreBodyPost(x.getLocation(), x.getPreStats(), body, x.getPostStats()));
    }

    @Override
    public Statement visitStringTemplateIfThenElse(IfThenElse x) {
      Statement t = x.getThenString().accept(this);
      Statement e = x.getElseString().accept(this);
      return ASTBuilder.makeStat("IfThenElse", x.getLocation(), ASTBuilder.make("Label","Empty",x.getLocation()),
          x.getConditions(),
            combinePreBodyPost(x.getLocation(), x.getPreStatsThen(), t, x.getPostStatsThen()),
            combinePreBodyPost(x.getLocation(), x.getPreStatsElse(), e, x.getPostStatsElse()));
    }

    @Override
    public Statement visitStringTemplateWhile(While x) {
      Statement body = x.getBody().accept(this);
      return ASTBuilder.makeStat("While", x.getLocation(), ASTBuilder.make("Label","Empty", x.getLocation()), Collections.singletonList(x.getCondition()),
          combinePreBodyPost(x.getLocation(), x.getPreStats(), body, x.getPostStats()));
    }

    @Override
    public Statement visitStringMiddleInterpolated(Interpolated x) {
      Statement mid = x.getMid().accept(this);
      Statement exp = makeIndentingAppend(x.getExpression());
      Statement tail = x.getTail().accept(this);
      return makeBlock(x.getLocation(), mid, exp, tail);
    }

    @Override
    public Statement visitStringMiddleTemplate(Template x) {
      Statement mid = x.getMid().accept(this);
      Statement tmp = x.getTemplate().accept(this);
      Statement tail = x.getTail().accept(this);
      return makeBlock(x.getLocation(), mid, tmp, tail);
    }
   
    @Override
    public Statement visitStringMiddleMid(Mid x) {
      return x.getMid().accept(this);
    }

    @Override
    public Statement visitMidStringCharsLexical(Lexical x) {
      return makeMidAppend(x.getLocation(), x.getString());
    }

    @Override
    public Statement visitPreStringCharsLexical(
        org.rascalmpl.ast.PreStringChars.Lexical x) {
      return makePreAppend(x.getLocation(), x.getString());
    }
   
    @Override
    public Statement visitPostStringCharsLexical(
        org.rascalmpl.ast.PostStringChars.Lexical x) {
      return makePostAppend(x.getLocation(), x.getString());
    }

    @Override
    public Statement visitStringTailMidInterpolated(
        org.rascalmpl.ast.StringTail.MidInterpolated x) {
      Statement mid = x.getMid().accept(this);
      Statement exp = makeIndentingAppend(x.getExpression());
      Statement tail = x.getTail().accept(this);
      return makeBlock(x.getLocation(), mid, exp, tail);
    }

    @Override
    public Statement visitStringConstantLexical(
        org.rascalmpl.ast.StringConstant.Lexical x) {
      return makeConstAppend(x.getLocation(), x.getString());
    }
   
    @Override
    public Statement visitStringTailMidTemplate(
        org.rascalmpl.ast.StringTail.MidTemplate x) {
      Statement mid = x.getMid().accept(this);
      Statement template = x.getTemplate().accept(this);
      Statement tail = x.getTail().accept(this);
      return makeBlock(x.getLocation(), mid, template, tail);
    }
   
    @Override
    public Statement visitStringTailPost(Post x) {
      return x.getPost().accept(this);
    }
 
  }
}
TOP

Related Classes of org.rascalmpl.interpreter.StringTemplateConverter$Visitor$MidAppend

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.