// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) // All rights reserved. // // This software may be modified and distributed under the terms // of the BSD license. See the LICENSE file for details. package wyil.util.type; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import wybs.lang.NameID; import wycc.util.Pair; import wyfs.util.Trie; import wyil.lang.Type; import static wyil.lang.Type.*; /** * The Type Parser is used to convert a given string into a type (or a syntax * error). This is generally useful for debugging purposes, but it does have * other usees as well. * * @author David J. Pearce * */ public class TypeParser { private int index; private String str; public TypeParser(String str) { this.str = str; } public Type parse() { return parse(new HashSet<String>()); } public Type parse(HashSet<String> typeVariables) { Type term = parseNotTerm(typeVariables); skipWhiteSpace(); while (index < str.length() && (str.charAt(index) == '|')) { // union type match("|"); term = Type.Union(term,parse(typeVariables)); skipWhiteSpace(); } return term; } // FIXME: This is broken and needs to be updated to handle multiple returns. // public Type parseFunctionTerm(HashSet<String> typeVariables) { // Type t = parseNotTerm(typeVariables); // if(index >= str.length()) { return t; } // char lookahead = str.charAt(index); // if(lookahead == '(') { // // this is a tuple, not a bracketed type. // List<Type> parameters = parseParameters(typeVariables); // skipWhiteSpace(); // return Function(t,parameters); // } // return t; // } // // private List<Type> parseParameters(HashSet<String> typeVariables) { // match("("); // ArrayList<Type> elems = new ArrayList(); // elems.add(parse(typeVariables)); // char lookahead = str.charAt(index); // while(lookahead == ',') { // match(","); // elems.add(parse(typeVariables)); // skipWhiteSpace(); // lookahead = str.charAt(index); // } // match(")"); // return elems; // } public Type parseNotTerm(HashSet<String> typeVariables) { skipWhiteSpace(); char lookahead = str.charAt(index); if(lookahead == '!') { match("!"); return Type.Negation(parseNotTerm(typeVariables)); } else { return parseBraceTerm(typeVariables); } } public Type parseBraceTerm(HashSet<String> typeVariables) { skipWhiteSpace(); char lookahead = str.charAt(index); if(lookahead == '(') { match("("); Type t = parse(typeVariables); skipWhiteSpace(); match(")"); skipWhiteSpace(); return t; } else { return parseTerm(typeVariables); } } public Type parseTerm(HashSet<String> typeVariables) { skipWhiteSpace(); char lookahead = str.charAt(index); switch (lookahead) { case 'a': match("any"); return T_ANY; case 'v': match("void"); return T_VOID; case 'n': match("null"); return T_NULL; case 'b': if ((index + 1 < str.length()) && str.charAt(index + 1) == 'o') { match("bool"); return T_BOOL; } else { match("byte"); return T_BYTE; } case 'i': match("int"); return T_INT; case '[': { match("["); Type elem = parse(typeVariables); match("]"); return Type.Array(elem); } case '{': { match("{"); Type elem = parse(typeVariables); skipWhiteSpace(); // record ArrayList<Pair<Type,String>> fields = new ArrayList<Pair<Type,String>>(); String id = parseIdentifier(); fields.add(new Pair<Type,String>(elem, id)); skipWhiteSpace(); boolean isOpen = false; while(index < str.length() && str.charAt(index) == ',') { match(","); if(str.charAt(index) == '.') { match("..."); isOpen=true; break; } elem = parse(typeVariables); id = parseIdentifier(); fields.add(new Pair<Type,String>(elem, id)); skipWhiteSpace(); } match("}"); return Type.Record(isOpen,fields); } default: { String typeVariable = parseIdentifier(); return Type.Nominal(new NameID(Trie.fromString("$"), typeVariable)); } } } private String parseIdentifier() { skipWhiteSpace(); int start = index; while (index < str.length() && Character.isJavaIdentifierPart(str.charAt(index))) { index++; } return str.substring(start,index); } private void skipWhiteSpace() { while (index < str.length() && Character.isWhitespace(str.charAt(index))) { index++; } } private void match(String match) { skipWhiteSpace(); if ((str.length() - index) < match.length() || !str.startsWith(match, index)) { throw new IllegalArgumentException("invalid type string: " + str); } index += match.length(); } }