Package wyrl.io

Source Code of wyrl.io.SpecParser

// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the <organization> nor the
//      names of its contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package wyrl.io;

import java.math.BigInteger;
import java.util.*;
import java.io.*;

import wyautl.util.BigRational;
import static wyrl.core.SpecFile.*;
import static wyrl.io.SpecLexer.*;
import wyrl.core.*;
import wyrl.core.Attribute.Source;
import wyrl.core.Expr.UOp;
import wyrl.core.SpecFile.Decl;
import wyrl.core.SpecFile.RewriteDecl;
import wyrl.core.SpecFile.RuleDecl;
import wyrl.core.SpecFile.TermDecl;
import wyrl.core.SpecFile.TypeDecl;
import wyrl.core.Type.Collection;
import wyrl.core.Type.Ref;
import wyrl.core.Type.Term;
import wyrl.util.*;

public class SpecParser {
  private File filename;
  private ArrayList<Token> tokens;
  private HashSet<File> included;
  private int index;

  public SpecParser(File file, List<Token> tokens) {
    this(file,tokens,new HashSet<File>());
  }

  private SpecParser(File file, List<Token> tokens, HashSet<File> included) {
    this.filename = file;
    this.tokens = new ArrayList<Token>(tokens);
    this.included = included;
  }

  public SpecFile parse() {
    ArrayList<Decl> decls = new ArrayList<Decl>();
    String pkg = parsePackage();
    while(index < tokens.size()) {
      Token t = tokens.get(index);
      if (t instanceof NewLine || t instanceof Comment) {
        matchEndLine();
      } else {
        Token lookahead = tokens.get(index);

        if(lookahead.text.equals("include")) {
          Decl id = parseIncludeDecl();
          // id can be null if the included file was already included
          // elsewhere.
          if(id != null) { decls.add(id); }
        } else if(lookahead.text.equals("term")) {
          decls.add(parseTermDecl());
        } else if(lookahead.text.equals("define")) {
          decls.add(parseTypeDecl());
        } else if(lookahead.text.equals("function")) {
          decls.add(parseFunctionDecl());
        } else {
          decls.add(parseRewriteDecl());
        }
      }
    }

    return new SpecFile(pkg, moduleName(), filename, decls);
  }

  private String moduleName() {
    String name = filename.getName();
    int idx = name.lastIndexOf('.');
    if(idx < 0) {
      return name;
    } else {
      return name.substring(0,idx);
    }
  }

  private String parsePackage() {
    skipWhiteSpace(true);
    Token lookahead = tokens.get(index);
    if(lookahead.text.equals("package")) {
      matchKeyword("package");
      String pkg = matchIdentifier().text;
      while((lookahead=tokens.get(index)) instanceof Dot) {
        match(Dot.class);
        pkg = pkg + "." + matchIdentifier().text;
      }
      return pkg;
    } else {
      return ""; // empty package
    }
  }

  private Decl parseIncludeDecl() {
    int start = index;
    Token token = tokens.get(index);
    matchKeyword("include");
    String relativeFilename = match(Strung.class).string;
    File incFile = new File(filename.getParent(),relativeFilename);
    matchEndLine();

    if(!included.contains(incFile)) {
      try {
        SpecLexer lexer = new SpecLexer(incFile);
        SpecParser parser = new SpecParser(incFile, lexer.scan(), included);
        SpecFile sf = parser.parse();
        included.add(incFile);
        return new IncludeDecl(sf, sourceAttr(start,index-1));
      } catch(IOException e) {
        syntaxError(e.getMessage(),token);
      }
    }

    return null;
  }

  private Decl parseTermDecl() {
    int start = index;
    matchKeyword("term");
    Type.Term data = parseTermType();
    matchEndLine();
    return new TermDecl(data, sourceAttr(start,index-1));
  }

  private Decl parseTypeDecl() {
    int start = index;
    matchKeyword("define");
    String name = matchIdentifier().text;
    matchKeyword("as");
    ArrayList<Type> types = new ArrayList<Type>();
    boolean firstTime=true;
    boolean isOpen = false;
    do {
      if (!firstTime) {
        if (isOpen) {
          syntaxError("'...' must mark end of declaration",
              tokens.get(index));
        }
        match(Bar.class);
      }
      firstTime=false;
      if(index < tokens.size() && tokens.get(index) instanceof DotDotDot) {
        match(DotDotDot.class);
        isOpen = true;
      } else {
        // FIXME: problem with parsing nested unions here
        types.add(parseType());
        skipWhiteSpace(true);
      }
    } while(index < tokens.size() && tokens.get(index) instanceof Bar);

    Type type;
    if(types.size() == 1) {
      type = types.get(0);
    } else {
      type = Type.T_OR(types);
    }

    return new TypeDecl(name, type, isOpen, sourceAttr(start,index-1));
  }

  private Decl parseRewriteDecl() {
    int start = index;
    Token lookahead = tokens.get(index);
    boolean reduce;
    if(lookahead.text.equals("reduce")) {
      matchKeyword("reduce");
      reduce = true;
    } else {
      matchKeyword("infer");
      reduce = false;
    }
    Pattern.Term pattern = (Pattern.Term) parsePatternTerm();
    Pair<String,Integer> nameAndRank = parseNameAndRank();
    match(Colon.class);
    matchEndLine();
    List<RuleDecl> rules = parseRuleBlock(1);

    String name = nameAndRank.first();
    int rank = nameAndRank.second();

    if(reduce) {
      return new ReduceDecl(pattern,rules,name,rank,sourceAttr(start,index-1));
    } else {
      return new InferDecl(pattern,rules,name,rank,sourceAttr(start,index-1));
    }
  }

  private Pair<String,Integer> parseNameAndRank() {
    String name = "";
    int rank = 0;
    skipWhiteSpace(true);
    Token lookahead = tokens.get(index);
    if(lookahead.text.equals("name")) {
      matchKeyword("name");
      Strung s = match(Strung.class);
      name = s.text.substring(1,s.text.length()-1);
    }
    skipWhiteSpace(true);
    lookahead = tokens.get(index);
    if(lookahead.text.equals("rank")) {
      matchKeyword("rank");
      Int i = match(Int.class);
      rank = i.value.intValue();
    }

    return new Pair<String,Integer>(name,rank);
  }

  private Decl parseFunctionDecl() {
    int start = index;
    matchKeyword("function");
    String name = matchIdentifier().text;
    Type from = parseType();
    match(Arrow.class);
    Type to = parseType();
    matchEndLine();
    return new FunctionDecl(name, from, to, sourceAttr(start, index - 1));
  }

  public Pattern parsePattern() {
    skipWhiteSpace(true);
    checkNotEof();
    Token token = tokens.get(index);

    if (token instanceof Star) {
      match(Star.class);
      return new Pattern.Leaf(Type.T_ANY());
    } else if (token instanceof LeftCurly
        || token instanceof LeftCurlyBar
        || token instanceof LeftSquare) {
      return parsePatternCompound();
    } else if (token instanceof LeftBrace) {
      Pattern r = parsePattern();
      return r;
    } else if(token instanceof Identifier){
      return parsePatternTerm();
    } else {
      return new Pattern.Leaf(parseType());
    }
  }

  public Pattern parsePatternTerm() {
    int start = index;
    String name = matchIdentifier().text;
    Token token = tokens.get(index);
    String var = null;
    Pattern p;
    if (token instanceof LeftCurly
        || token instanceof LeftCurlyBar
        || token instanceof LeftSquare) {
      p = parsePatternCompound();
    } else if(token instanceof LeftBrace) {
      match(LeftBrace.class);
      p = parsePattern();
      if (index < tokens.size()
          && tokens.get(index) instanceof Identifier) {
        var = matchIdentifier().text;
      }
      match(RightBrace.class);
    } else {
      // in this case, it's not a pattern.
      return new Pattern.Leaf(Type.T_TERM(name, null), sourceAttr(start,
          index - 1));
    }

    return new Pattern.Term(name, p, var, sourceAttr(start, index - 1));
  }

  public Pattern.Collection parsePatternCompound() {
    int start = index;
    int kind; // 0 for set, 1 for bag, 2 for list
    ArrayList<Pair<Pattern, String>> params = new ArrayList();
    if (index < tokens.size() && tokens.get(index) instanceof LeftSquare) {
      match(LeftSquare.class);
      kind = 2;
    } else if (index < tokens.size()
        && tokens.get(index) instanceof LeftCurlyBar) {
      match(LeftCurlyBar.class);
      kind = 1;
    } else {
      match(LeftCurly.class);
      kind = 0;
    }
    boolean firstTime = true;
    boolean unbound = false;
    while (index < tokens.size()
        && !(tokens.get(index) instanceof RightSquare)
        && !(tokens.get(index) instanceof RightCurly)
        && !(tokens.get(index) instanceof BarRightCurly)) {
      if (unbound) {
        syntaxError("... must be last match", tokens.get(index));
      }
      if (!firstTime) {
        match(Comma.class);
      }
      firstTime = false;
      Pattern p = parsePattern();
      if (index < tokens.size()
          && tokens.get(index) instanceof DotDotDot) {
        match(DotDotDot.class);
        unbound = true;
      }
      String n = null;
      if (index < tokens.size()
          && tokens.get(index) instanceof Identifier) {
        n = matchIdentifier().text;
      }
      params.add(new Pair<Pattern, String>(p, n));
    }
    switch(kind) {
    case 2:
      match(RightSquare.class);
      return new Pattern.List(unbound, params,
          sourceAttr(start, index - 1));
    case 1:
      match(BarRightCurly.class);
      return new Pattern.Bag(unbound, params,
          sourceAttr(start, index - 1));
    default:
      match(RightCurly.class);
      return new Pattern.Set(unbound, params,
          sourceAttr(start, index - 1));
    }
  }

  public List<RuleDecl> parseRuleBlock(int indent) {
    Tabs tabs = getIndent();

    ArrayList<RuleDecl> rules = new ArrayList<RuleDecl>();
    while(tabs != null && tabs.ntabs == indent) {
      index = index + 1;
      rules.add(parseRule());
      tabs = getIndent();
    }

    return rules;
  }

  private Tabs getIndent() {
    skipEmptyLines();
    if (index < tokens.size() && tokens.get(index) instanceof Tabs) {
      return (Tabs) tokens.get(index);
    } else if (index < tokens.size()
        && tokens.get(index) instanceof Comment) {
      // This indicates a completely empty line. In which case, we just
      // ignore it.
      matchEndLine();
      return getIndent();
    } else {
      return null;
    }
  }

  /**
   * Skip over any empty lines. That is lines which contain only whitespace
   * and comments.
   */
  private void skipEmptyLines() {
    int tmp = index;
    do {
      tmp = skipWhiteSpace(tmp,false);
      if (tmp < tokens.size() && !(tokens.get(tmp) instanceof NewLine)) {
        return; // done
      } else if (tmp >= tokens.size()) {
        index = tmp;
        return; // end-of-file reached
      }
      // otherwise, skip newline and continue
      tmp = tmp + 1;
      index = tmp;
    } while (true);
    // deadcode
  }


  public RuleDecl parseRule() {
    int start = index;
    match(Arrow.class);
    ArrayList<Pair<String,Expr>> lets = new ArrayList();
    if(index < tokens.size() && tokens.get(index).text.equals("let")) {
      matchKeyword("let");
      boolean firstTime=true;
      do {
        if(!firstTime) {
          match(Comma.class);
          skipWhiteSpace(true);
        }
        firstTime=false;
        String id = matchIdentifier().text;
        match(Equals.class);
        Expr rhs = parseAddSubExpression();
        lets.add(new Pair(id,rhs));
        skipWhiteSpace(true);
      } while(index < tokens.size() && tokens.get(index) instanceof Comma);
      match(ElemOf.class);
    }
    Expr result = parseAddSubExpression();
    skipWhiteSpace(true);
    if(index < tokens.size() && tokens.get(index) instanceof Comma) {
      match(Comma.class);
      matchKeyword("if");
      Expr condition = parseCondition();
      matchEndLine();
      return new RuleDecl(lets,result,condition,sourceAttr(start,index-1));
    } else {
      return new RuleDecl(lets,result,null,sourceAttr(start,index-1));
    }
  }

  private Expr parseCondition() {
    checkNotEof();
    int start = index;
    Expr c1 = parseConditionExpression();

    if(index < tokens.size() && tokens.get(index) instanceof LogicalAnd) {
      match(LogicalAnd.class);
      skipWhiteSpace(true);

      Expr c2 = parseCondition();
      return new Expr.BinOp(Expr.BOp.AND, c1, c2, sourceAttr(start,
          index - 1));
    } else if(index < tokens.size() && tokens.get(index) instanceof LogicalOr) {
      match(LogicalOr.class);
      skipWhiteSpace(true);

      Expr c2 = parseCondition();
      return new Expr.BinOp(Expr.BOp.OR, c1, c2, sourceAttr(start,
          index - 1));
    }
    return c1;
  }

  private Expr parseConditionExpression() {
    int start = index;

    if (index < tokens.size() && tokens.get(index) instanceof None) {
      match(None.class);
      return parseQuantifierSet(start,Expr.COp.NONE);
    } else if (index < tokens.size() && tokens.get(index) instanceof Some) {
      match(Some.class);
      return parseQuantifierSet(start,Expr.COp.SOME);
    }

    Expr lhs = parseRangeExpression();

    if (index < tokens.size() && tokens.get(index) instanceof LessEquals) {
      match(LessEquals.class);
      skipWhiteSpace(true);

      Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.LTEQ, lhs,  rhs, sourceAttr(start,index-1));
    } else if (index < tokens.size() && tokens.get(index) instanceof LeftAngle) {
       match(LeftAngle.class);
       skipWhiteSpace(true);

       Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.LT, lhs,  rhs, sourceAttr(start,index-1));
    } else if (index < tokens.size() && tokens.get(index) instanceof GreaterEquals) {
      match(GreaterEquals.class);
      skipWhiteSpace(true);
      Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.GTEQ,  lhs,  rhs, sourceAttr(start,index-1));
    } else if (index < tokens.size() && tokens.get(index) instanceof RightAngle) {
      match(RightAngle.class);
      skipWhiteSpace(true);

      Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.GT, lhs,  rhs, sourceAttr(start,index-1));
    } else if (index < tokens.size() && tokens.get(index) instanceof EqualsEquals) {
      match(EqualsEquals.class);
      skipWhiteSpace(true);

      Expr rhs = parseRangeExpression();
      return new Expr.BinOp(Expr.BOp.EQ, lhs,  rhs, sourceAttr(start,index-1));
    } else if (index < tokens.size() && tokens.get(index) instanceof NotEquals) {
      match(NotEquals.class);
      skipWhiteSpace(true);

      Expr rhs = parseRangeExpression();
      return new Expr.BinOp(Expr.BOp.NEQ, lhs,  rhs, sourceAttr(start,index-1));
    } else if (index < tokens.size() && tokens.get(index) instanceof ElemOf) {
      match(ElemOf.class);
      skipWhiteSpace(true);
      Expr rhs = parseRangeExpression();
      return new Expr.BinOp(Expr.BOp.IN, lhs,  rhs, sourceAttr(start,index-1));
    } else if (index < tokens.size() && tokens.get(index).text.equals("is")) {
      return parseTypeEquals(lhs,start);
    } else {
      return lhs;
    }
  }

  private Expr parseTypeEquals(Expr lhs, int start) {
    matchKeyword("is");
    skipWhiteSpace(true);

    Type type = Type.T_REF(parseType());
    Expr.Constant tc = new Expr.Constant(type, sourceAttr(start, index - 1));

    return new Expr.BinOp(Expr.BOp.IS, lhs, tc, sourceAttr(start,
        index - 1));
  }

  private Expr parseRangeExpression() {
    int start = index;
    Expr lhs = parseAddSubExpression();

    if (index < tokens.size() && tokens.get(index) instanceof DotDot) {
      match(DotDot.class);
      skipWhiteSpace(true);
      Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.RANGE, lhs,  rhs, sourceAttr(start,index-1));
    }

    return lhs;
  }

  private Expr parseAddSubExpression() {
    int start = index;
    Expr lhs = parseMulDivExpression();

    if (index < tokens.size() && tokens.get(index) instanceof Plus) {
      match(Plus.class);
      skipWhiteSpace(true);
      Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.ADD, lhs, rhs, sourceAttr(start,
          index - 1));
    } else if (index < tokens.size() && tokens.get(index) instanceof Minus) {
      match(Minus.class);
      skipWhiteSpace(true);

      Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.SUB, lhs, rhs, sourceAttr(start,
          index - 1));
    } else if (index < tokens.size() && tokens.get(index) instanceof PlusPlus) {
      // wrong precidence
      match(PlusPlus.class);
      skipWhiteSpace(true);

      Expr rhs = parseAddSubExpression();
      return new Expr.BinOp(Expr.BOp.APPEND, lhs, rhs, sourceAttr(start,
          index - 1));
    }

    return lhs;
  }

  private Expr parseMulDivExpression() {
    int start = index;
    Expr lhs = parseCastExpression();

    if (index < tokens.size() && tokens.get(index) instanceof Star) {
      match(Star.class);
      skipWhiteSpace(true);

      Expr rhs = parseMulDivExpression();
      return new Expr.BinOp(Expr.BOp.MUL, lhs, rhs, sourceAttr(start,
          index - 1));
    } else if (index < tokens.size()
        && tokens.get(index) instanceof RightSlash) {
      match(RightSlash.class);
      skipWhiteSpace(true);

      Expr rhs = parseMulDivExpression();
      return new Expr.BinOp(Expr.BOp.DIV, lhs, rhs, sourceAttr(start,
          index - 1));
    }

    return lhs;
  }

  private Expr parseCastExpression() {
    Token lookahead = tokens.get(index);
    if(lookahead instanceof LeftBrace) {
      int start = index;
      try {
        match(LeftBrace.class);
        Type type = parseType();
        match(RightBrace.class);
        Expr expr = parseIndexTerm();
        return new Expr.Cast(type, expr, sourceAttr(start,
            index - 1));
      } catch(SyntaxError e) {
        // ok, failed parsing the cast expression ... cannot be a cast
        // then!  restart assuming just an index term...
        index = start;
      }
    }
    return parseIndexTerm();
  }

  private Expr parseIndexTerm() {
    checkNotEof();
    int start = index;
    int ostart = index;
    Expr lhs = parseTerm();

    Token lookahead = tokens.get(index);

    while (lookahead instanceof LeftSquare
        || lookahead instanceof LeftBrace || lookahead instanceof Hash) {
      start = index;
      if(lookahead instanceof LeftSquare) {
        match(LeftSquare.class);
        skipWhiteSpace(true);
        Expr rhs = parseCondition();
        skipWhiteSpace(true);
        if (index < tokens.size()
            && tokens.get(index) instanceof LeftSlash) {
          // substitution expression
          match(LeftSlash.class);
          Expr e = parseAddSubExpression();
          lhs = new Expr.Substitute(lhs, rhs, e, sourceAttr(start,
              index - 1));
          match(RightSquare.class);
        } else if (index < tokens.size()
            && tokens.get(index) instanceof Assignment) {
          // list update expression
          match(Assignment.class);
          Expr e = parseAddSubExpression();
          lhs = new Expr.ListUpdate(lhs, rhs, e, sourceAttr(start,
              index - 1));
          match(RightSquare.class);
        } else if (lhs instanceof Expr.Variable
            && index < tokens.size()
            && tokens.get(index) instanceof Comma) {
          // here, we have a case of mistaken identity
          index = ostart; // back track
          return parseConstructorExpr();
        } else {
          match(RightSquare.class);
          lhs = new Expr.ListAccess(lhs, rhs, sourceAttr(start,
            index - 1));
        }
      }
      if(index < tokens.size()) {
        lookahead = tokens.get(index);
      } else {
        lookahead = null;
      }
    }

    return lhs;
  }

  private Expr parseTerm() {
    checkNotEof();

    int start = index;
    Token token = tokens.get(index);

    if(token instanceof LeftBrace) {
      match(LeftBrace.class);
      skipWhiteSpace(true);
      checkNotEof();
      Expr v = parseCondition();
      skipWhiteSpace(true);
      checkNotEof();
      token = tokens.get(index);
      match(RightBrace.class);
      return v;
    } else if ((index + 1) < tokens.size()
        && token instanceof Identifier
        && (tokens.get(index + 1) instanceof LeftBrace
            // || tokens.get(index + 1) instanceof LeftSquare
            || tokens.get(index + 1) instanceof LeftCurly
            || tokens.get(index + 1) instanceof LeftCurlyBar)) {
      // must be a method invocation
      return parseConstructorExpr();
    } else if (token.text.equals("null")) {
      matchKeyword("null");
      return new Expr.Constant(null,
          sourceAttr(start, index - 1));
    } else if (token.text.equals("true")) {
      matchKeyword("true");
      return new Expr.Constant(true,
          sourceAttr(start, index - 1));
    } else if (token.text.equals("false")) {
      matchKeyword("false");
      return new Expr.Constant(false,
          sourceAttr(start, index - 1));
    } else if (token.text.equals("num")) {
      return parseNumerator();
    } else if (token.text.equals("den")) {
      return parseDenominator();
    } else if (token instanceof Identifier) {
      return new Expr.Variable(matchIdentifier().text, sourceAttr(start,
          index - 1));
    } else if (token instanceof Int) {
      BigInteger val = match(Int.class).value;
      return new Expr.Constant(val, sourceAttr(start, index - 1));
    } else if (token instanceof Real) {
      BigRational val = match(Real.class).value;
      return new Expr.Constant(val, sourceAttr(start,
          index - 1));
    } else if (token instanceof Strung) {
      return parseString();
    } else if (token instanceof Minus) {
      return parseNegation();
    } else if (token instanceof Star) {
      return parseDeref();
    } else if (token instanceof Bar) {
      return parseLengthOf();
    } else if (token instanceof LeftSquare) {
      return parseListVal();
    } else if (token instanceof LeftCurlyBar) {
      return parseBagVal();
    } else if (token instanceof LeftCurly) {
      return parseSetVal();
    } else if (token instanceof EmptySet) {
      match(EmptySet.class);
      return new Expr.Constant(new HashSet(),
          sourceAttr(start, index - 1));
    } else if (token instanceof Shreak) {
      match(Shreak.class);
      return new Expr.UnOp(Expr.UOp.NOT, parseTerm(),
          sourceAttr(start, index - 1));
    }
    syntaxError("unrecognised term.",token);
    return null;
  }

  private Expr parseListVal() {
    int start = index;
    ArrayList<Expr> exprs = new ArrayList<Expr>();
    match(LeftSquare.class);
    skipWhiteSpace(true);
    checkNotEof();
    Token token = tokens.get(index);

    if(!(token instanceof RightSquare)) {
      Expr expr = parseCondition();
      skipWhiteSpace(true);
      token = tokens.get(index);
      if(token instanceof Bar) {
        // comprehension
        return parseComprehensionRest(start,expr,Expr.COp.LISTCOMP);
      }
      exprs.add(expr);
    }

    while(!(token instanceof RightSquare)) {
      match(Comma.class);
      skipWhiteSpace(true);
      exprs.add(parseCondition());
      skipWhiteSpace(true);
      checkNotEof();
      token = tokens.get(index);
    }

    match(RightSquare.class);
    return new Expr.NaryOp(Expr.NOp.LISTGEN, exprs, sourceAttr(start,
        index - 1));
  }

  private Expr parseBagVal() {
    int start = index;
    ArrayList<Expr> exprs = new ArrayList<Expr>();
    match(LeftCurlyBar.class);
    skipWhiteSpace(true);
    Token token = tokens.get(index);

    if(!(token instanceof BarRightCurly)) {
      Expr expr = parseCondition();
      skipWhiteSpace(true);
      token = tokens.get(index);
      if(token instanceof Bar) {
        // comprehension
        return parseComprehensionRest(start,expr,Expr.COp.BAGCOMP);
      }
      exprs.add(expr);
    }

    while(!(token instanceof BarRightCurly)) {
      match(Comma.class);
      skipWhiteSpace(true);
      exprs.add(parseCondition());
      skipWhiteSpace(true);
      checkNotEof();
      token = tokens.get(index);
    }

    match(BarRightCurly.class);
    return new Expr.NaryOp(Expr.NOp.BAGGEN, exprs, sourceAttr(start,
        index - 1));
  }

  private Expr parseSetVal() {
    int start = index;
    ArrayList<Expr> exprs = new ArrayList<Expr>();
    match(LeftCurly.class);
    skipWhiteSpace(true);
    Token token = tokens.get(index);

    if(!(token instanceof RightCurly)) {
      Expr expr = parseCondition();
      skipWhiteSpace(true);
      token = tokens.get(index);
      if(token instanceof Bar) {
        // comprehension
        return parseComprehensionRest(start,expr,Expr.COp.SETCOMP);
      }
      exprs.add(expr);
    }

    while(!(token instanceof RightCurly)) {
      match(Comma.class);
      skipWhiteSpace(true);
      exprs.add(parseCondition());
      skipWhiteSpace(true);
      checkNotEof();
      token = tokens.get(index);
    }

    match(RightCurly.class);
    return new Expr.NaryOp(Expr.NOp.SETGEN, exprs, sourceAttr(start,
        index - 1));
  }

  private Expr parseComprehensionRest(int start, Expr result, Expr.COp cop) {
    match(Bar.class);
    skipWhiteSpace(true);
    ArrayList<Pair<Expr.Variable,Expr>> sources = new ArrayList<Pair<Expr.Variable,Expr>>();
    Expr condition = null;
    boolean firstTime = true;
    Token token = tokens.get(index);

    outer: while(true) {
      if(condition != null) {
        syntaxError("condition must come last in comprehension",
            token);
      }
      if(!firstTime) {
        match(Comma.class);
        skipWhiteSpace(true);
      } else {
        firstTime=false;
      }
      condition = parseCondition();
      Pair<Expr.Variable,Expr> source = extractComprehensionSource(condition);
      if(source != null) {
        condition = null;
        sources.add(source);
      }
      skipWhiteSpace(true);
      token = tokens.get(index);

      switch(cop) {
        case SETCOMP:
          if(token instanceof RightCurly) {
            match(RightCurly.class);
            break outer;
          }
          break;
        case BAGCOMP:
          if(token instanceof BarRightCurly) {
            match(BarRightCurly.class);
            break outer;
          }
          break;
        case LISTCOMP:
          if(token instanceof RightSquare) {
            match(RightSquare.class);
            break outer;
          }
          break;
      }

    }

    return new Expr.Comprehension(cop, result, sources, condition, sourceAttr(start,index-1));
  }

  private Expr parseQuantifierSet(int start, Expr.COp cop) {
    match(LeftCurly.class);
    skipWhiteSpace(true);
    ArrayList<Pair<Expr.Variable,Expr>> sources = new ArrayList<Pair<Expr.Variable,Expr>>();
    boolean firstTime = true;
    Token token = tokens.get(index);
    while(!(token instanceof Bar)) {
      if(!firstTime) {
        match(Comma.class);
        skipWhiteSpace(true);
      } else {
        firstTime=false;
      }
      Expr condition = parseConditionExpression();
      Pair<Expr.Variable,Expr> source = extractComprehensionSource(condition);
      if(source == null) {
        syntaxError("source expression required",
            token);
      }
      sources.add(source);
      skipWhiteSpace(true);
      token = tokens.get(index);
    }
    match(Bar.class);
    Expr condition = parseCondition();
    match(RightCurly.class);

    return new Expr.Comprehension(cop, null, sources, condition, sourceAttr(start,index-1));
  }
  private Pair<Expr.Variable, Expr> extractComprehensionSource(Expr expr) {
    if (expr instanceof Expr.BinOp) {
      Expr.BinOp bop = (Expr.BinOp) expr;
      if (bop.op == Expr.BOp.IN && bop.lhs instanceof Expr.Variable) {
        Expr.Variable var = (Expr.Variable) bop.lhs;
        return new Pair<Expr.Variable, Expr>(var, bop.rhs);
      }
    }
    return null;
  }

  private Expr parseDeref() {
    int start = index;
    match(Star.class);
    Expr e = parseCastExpression();
    return new Expr.TermAccess(e, sourceAttr(start, index - 1));
  }

  private Expr parseLengthOf() {
    int start = index;
    match(Bar.class);
    skipWhiteSpace(true);
    Expr e = parseCastExpression();
    skipWhiteSpace(true);
    match(Bar.class);
    return new Expr.UnOp(Expr.UOp.LENGTHOF, e, sourceAttr(start, index - 1));
  }

  private Expr parseNumerator() {
    int start = index;
    matchKeyword("num");
    skipWhiteSpace(true);
    Expr e = parseCastExpression();
    return new Expr.UnOp(UOp.NUMERATOR, e, sourceAttr(start, index - 1));
  }

  private Expr parseDenominator() {
    int start = index;
    matchKeyword("den");
    skipWhiteSpace(true);
    Expr e = parseCastExpression();
    return new Expr.UnOp(UOp.DENOMINATOR, e, sourceAttr(start, index - 1));
  }

  private Expr parseNegation() {
    int start = index;
    match(Minus.class);
    skipWhiteSpace(true);
    Expr e = parseCastExpression();

    if(e instanceof Expr.Constant) {
      Expr.Constant c = (Expr.Constant) e;
      if(c.value instanceof BigInteger) {
        java.math.BigInteger bi = (BigInteger)c.value;
        return new Expr.Constant(bi.negate(), sourceAttr(start, index));
      }
    }

    return new Expr.UnOp(Expr.UOp.NEG, e, sourceAttr(start, index));
  }

  private Expr.Constructor parseConstructorExpr() {
    int start = index;
    Identifier name = matchIdentifier();
    skipWhiteSpace(true);
    checkNotEof();
    Token token = tokens.get(index);
    Expr argument;
    if(token instanceof LeftBrace) {
      match(LeftBrace.class);
      argument = parseConditionExpression();
      match(RightBrace.class);
    } else if(token instanceof LeftSquare) {
      argument = parseListVal();
    } else if(token instanceof LeftCurlyBar) {
      argument = parseBagVal();
    } else if(token instanceof LeftCurly) {
      argument = parseSetVal();
    } else {
      syntaxError("unknown token encountered",token);
      return null;
    }

    return new Expr.Constructor(name.text, argument, false, sourceAttr(
        start, index - 1));
  }

  private Expr parseString() {
    int start = index;
    String s = match(Strung.class).string;
    return new Expr.Constant(s, sourceAttr(start, index - 1));
  }

  private Type parseType() {
    skipWhiteSpace(true);
    checkNotEof();
    int start = index;
    Token token = tokens.get(index);
    Type t;

    if(token instanceof Star) {
      match(Star.class);
      t = Type.T_ANY();
    } else if(token.text.equals("int")) {
      matchKeyword("int");
      t = Type.T_INT();
    } else if(token.text.equals("real")) {
      matchKeyword("real");
      t = Type.T_REAL();
    } else if(token.text.equals("void")) {
      matchKeyword("void");
      t = Type.T_VOID();
    } else if(token.text.equals("bool")) {
      matchKeyword("bool");
      t = Type.T_BOOL();
    } else if(token.text.equals("string")) {
      matchKeyword("string");
      t = Type.T_STRING();
    } else if (token instanceof LeftBrace) {
      match(LeftBrace.class);
      t = parseType();
      match(RightBrace.class);
    } else if(token instanceof Shreak) {
      match(Shreak.class);
      t = Type.T_NOT(parseType());
    } else if (token instanceof LeftCurly || token instanceof LeftCurlyBar
        || token instanceof LeftSquare) {
      t = parseCompoundType();
    } else {
      t = parseTermType();
    }

    return t;
  }

  private Type.Term parseTermType() {
    skipWhiteSpace(false);
    checkNotEof();
    Identifier id = matchIdentifier();
    Type.Ref data = null;
    if (index < tokens.size()) {
      Token token = tokens.get(index);
      if (token instanceof LeftBrace || token instanceof LeftCurly
          || token instanceof LeftCurlyBar
          || token instanceof LeftSquare) {
        data = Type.T_REF(parseType());
      }
    }
    return Type.T_TERM(id.text, data);
  }

  private Type.Collection parseCompoundType() {
    int kind; // 0 = set, 1 = bag, 2 = list
    if (index < tokens.size() && tokens.get(index) instanceof LeftSquare) {
      match(LeftSquare.class);
      kind = 2;
    } else if (index < tokens.size()
        && tokens.get(index) instanceof LeftCurlyBar) {
      match(LeftCurlyBar.class);
      kind = 1;
    } else {
      match(LeftCurly.class);
      kind = 0;
    }
    ArrayList<Type> types = new ArrayList<Type>();
    boolean firstTime = true;
    while (index < tokens.size()
        && !(tokens.get(index) instanceof RightSquare
            || tokens.get(index) instanceof RightCurly
            || tokens.get(index) instanceof BarRightCurly
            || tokens.get(index) instanceof DotDotDot)) {
      if (!firstTime) {
        match(Comma.class);
      }
      firstTime = false;
      types.add(Type.T_REF(parseType()));
    }
    boolean unbounded = false;
    if (index < tokens.size() && tokens.get(index) instanceof DotDotDot) {
      match(DotDotDot.class);
      unbounded = true;
    }
    switch(kind) {
    case 2:
      match(RightSquare.class);
      return Type.T_LIST(unbounded,types);
    case 1:
      match(BarRightCurly.class);
      return Type.T_BAG(unbounded,types);
    default:
      match(RightCurly.class);
      return Type.T_SET(unbounded,types);
    }
  }

  private void skipWhiteSpace(boolean includeNewLine) {
    index = skipWhiteSpace(index,includeNewLine);
  }

  private int skipWhiteSpace(int index, boolean includeNewLine) {
    while (index < tokens.size()
        && isWhiteSpace(includeNewLine, tokens.get(index))) {
      index++;
    }
    return index;
  }

  private boolean isWhiteSpace(boolean includeNewLine, Token t) {
    return (includeNewLine && t instanceof SpecLexer.NewLine)
        || t instanceof SpecLexer.Comment
        || t instanceof SpecLexer.Tabs;
  }

  private void checkNotEof() {
    if (index >= tokens.size()) {
      throw new SyntaxError("unexpected end-of-file", filename,
          index - 1, index - 1);
    }
    return;
  }

  private <T extends Token> T match(Class<T> c) {
    checkNotEof();
    Token t = tokens.get(index);
    if (!c.isInstance(t)) {
      syntaxError("syntax error" , t);
    }
    index = index + 1;
    return (T) t;
  }

  private Token matchAll(Class<? extends Token>... cs) {
    checkNotEof();
    Token t = tokens.get(index);
    for(Class<? extends Token> c : cs) {
      if (c.isInstance(t)) {
        index = index + 1;
        return t;
      }
    }
    syntaxError("syntax error" , t);
    return null;
  }

  private Identifier matchIdentifier() {
    checkNotEof();
    Token t = tokens.get(index);
    if (t instanceof Identifier) {
      Identifier i = (Identifier) t;
      index = index + 1;
      return i;
    }
    syntaxError("identifier expected", t);
    return null; // unreachable.
  }

  private Keyword matchKeyword(String keyword) {
    checkNotEof();
    Token t = tokens.get(index);
    if (t instanceof Keyword) {
      if (t.text.equals(keyword)) {
        index = index + 1;
        return (Keyword) t;
      }
    }
    syntaxError("keyword " + keyword + " expected.", t);
    return null;
  }

  private void matchEndLine() {
    while(index < tokens.size()) {
      Token t = tokens.get(index++);
      if(t instanceof NewLine) {
        break;
      } else if(!(t instanceof Comment) && !(t instanceof Tabs)) {
        syntaxError("syntax error",t);
      }
    }
  }

  private Attribute.Source sourceAttr(int start, int end) {
    Token t1 = tokens.get(start);
    Token t2 = tokens.get(end);
    return new Attribute.Source(t1.start,t2.end());
  }

  private void syntaxError(String msg, Expr e) {
    Attribute.Source loc = e.attribute(Attribute.Source.class);
    throw new SyntaxError(msg, filename, loc.start, loc.end);
  }

  private void syntaxError(String msg, Token t) {
    throw new SyntaxError(msg, filename, t.start, t.start + t.text.length() - 1);
  }
}
TOP

Related Classes of wyrl.io.SpecParser

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.