|
|||||||||||||||||||
| Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
| SExpParser.java | 75% | 91.3% | 100% | 88.3% |
|
||||||||||||||
| 1 | /*BEGIN_COPYRIGHT_BLOCK | |
| 2 | * | |
| 3 | * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu) | |
| 4 | * All rights reserved. | |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions are met: | |
| 8 | * * Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * * Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the | |
| 14 | * names of its contributors may be used to endorse or promote products | |
| 15 | * derived from this software without specific prior written permission. | |
| 16 | * | |
| 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
| 21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
| 25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 28 | * | |
| 29 | * This software is Open Source Initiative approved Open Source Software. | |
| 30 | * Open Source Initative Approved is a trademark of the Open Source Initiative. | |
| 31 | * | |
| 32 | * This file is part of DrJava. Download the current version of this project | |
| 33 | * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/ | |
| 34 | * | |
| 35 | * END_COPYRIGHT_BLOCK*/ | |
| 36 | ||
| 37 | package edu.rice.cs.util.sexp; | |
| 38 | ||
| 39 | import java.io.File; | |
| 40 | import java.io.Reader; | |
| 41 | import java.io.FileReader; | |
| 42 | import java.io.StringReader; | |
| 43 | import java.io.IOException; | |
| 44 | import java.util.List; | |
| 45 | import java.util.ArrayList; | |
| 46 | import java.util.LinkedList; | |
| 47 | ||
| 48 | /** | |
| 49 | * This parser is not meant to be instantiated. It has | |
| 50 | * static methods that do all the work for you. These | |
| 51 | * parse methods take in the data that is to be parsed | |
| 52 | * and simply returns an s-expression abstract syntax. | |
| 53 | * @author Jonathan Lugo, PLT Group | |
| 54 | */ | |
| 55 | public class SExpParser { | |
| 56 | ||
| 57 | 9 | public static List<SEList> parse(File f) throws SExpParseException, IOException{ |
| 58 | 9 | return parse(new FileReader(f)); |
| 59 | } | |
| 60 | ||
| 61 | 12 | public static List<SEList> parse(String s) throws SExpParseException { |
| 62 | 12 | return parse(new StringReader(s)); |
| 63 | } | |
| 64 | ||
| 65 | 22 | public static List<SEList> parse(Reader r) throws SExpParseException { |
| 66 | 22 | try { |
| 67 | 22 | return new ParseHelper(r).parseMultiple(); |
| 68 | } | |
| 69 | catch(LexingException e) { | |
| 70 | 0 | throw new SExpParseException(e.getMessage()); |
| 71 | } | |
| 72 | catch(PrivateParseException e) { | |
| 73 | 8 | throw new SExpParseException(e.getMessage()); |
| 74 | } | |
| 75 | } | |
| 76 | ||
| 77 | /** A new helper is instantiated for each time | |
| 78 | * the user wants to parse data. This is not | |
| 79 | * reused. The instances of the ParseHelpers are | |
| 80 | * handled solely in the outer class SExpParser. | |
| 81 | */ | |
| 82 | private static class ParseHelper { | |
| 83 | ||
| 84 | private Lexer _lex; | |
| 85 | ||
| 86 | 22 | public ParseHelper(Reader r) { |
| 87 | 22 | _lex = new Lexer(r); |
| 88 | } | |
| 89 | ||
| 90 | /** | |
| 91 | * Parse a forest of top-level s-expressions from {@link #parseTopLevelExp()}. | |
| 92 | * @see #parseTopLevelExp() | |
| 93 | */ | |
| 94 | 22 | public List<SEList> parseMultiple() { |
| 95 | 22 | ArrayList<SEList> l = new ArrayList<SEList>(); |
| 96 | 22 | SEList exp; |
| 97 | ? | while ( (exp = parseTopLevelExp()) != null) { |
| 98 | 55 | l.add(exp); |
| 99 | } | |
| 100 | 14 | return l; |
| 101 | } | |
| 102 | ||
| 103 | /** | |
| 104 | * A top-level s-expression is simply a non-empty list. Our s-expression files | |
| 105 | * can be a forest of several trees, but the Atomic values are not allowed | |
| 106 | * at the top level, only lists. | |
| 107 | * @return the top-level list s-expression | |
| 108 | */ | |
| 109 | 77 | public SEList parseTopLevelExp() { |
| 110 | 77 | Tokens.SExpToken t = _lex.readToken(); |
| 111 | 77 | if (t == Tokens.LeftParenToken.ONLY) { |
| 112 | 59 | return parseList(); |
| 113 | } | |
| 114 | 18 | else if (t == null) { |
| 115 | 14 | return null; |
| 116 | } | |
| 117 | else { | |
| 118 | 4 | throw new PrivateParseException("A top-level s-expression must be a list. " + |
| 119 | "Invalid start of list: " + t); | |
| 120 | } | |
| 121 | } | |
| 122 | ||
| 123 | /** | |
| 124 | * Parses the next s-expression in the lexer's buffer. | |
| 125 | * This may be either a cons or an atom | |
| 126 | * @return the next s-expression in the read buffer. | |
| 127 | */ | |
| 128 | 808 | public SExp parseExp() { |
| 129 | 808 | Tokens.SExpToken t = _lex.readToken(); |
| 130 | 808 | assertNotEOF(t); |
| 131 | 805 | if (t == Tokens.LeftParenToken.ONLY) { |
| 132 | 254 | return parseList(); |
| 133 | } | |
| 134 | else { | |
| 135 | 551 | return parseAtom(t); |
| 136 | } | |
| 137 | } | |
| 138 | ||
| 139 | /** | |
| 140 | * The left paren has already been read. This starts | |
| 141 | * building up the recursive list structure | |
| 142 | * @return the parsed recursive s-expression list | |
| 143 | */ | |
| 144 | 313 | private SEList parseList() { |
| 145 | 313 | LinkedList<SExp> list = new LinkedList<SExp>(); |
| 146 | 313 | Tokens.SExpToken t = _lex.peek(); |
| 147 | 313 | assertNotEOF(t); |
| 148 | ||
| 149 | 312 | while (t != Tokens.RightParenToken.ONLY) { |
| 150 | 808 | list.addFirst(parseExp()); |
| 151 | 805 | t = _lex.peek(); |
| 152 | } | |
| 153 | ||
| 154 | // t has to be a Tokens.RightParenToken at this point. | |
| 155 | // simply eat the token | |
| 156 | 309 | _lex.readToken(); |
| 157 | ||
| 158 | // Compile the cons structure from the list of exps | |
| 159 | 309 | SEList cons = Empty.ONLY; |
| 160 | 309 | for (SExp exp : list) { |
| 161 | 801 | cons = new Cons(exp, cons); |
| 162 | } | |
| 163 | 309 | return cons; |
| 164 | } | |
| 165 | ||
| 166 | /** | |
| 167 | * Parses an atom. The token was already read and | |
| 168 | * found not to start a list, this method interprets | |
| 169 | * what is given. This method chooses which type of | |
| 170 | * atom the token represents and creates the atom. | |
| 171 | * @param t the token to interpret | |
| 172 | * @return the correct corresponding atom | |
| 173 | */ | |
| 174 | 551 | private Atom parseAtom(Tokens.SExpToken t) { |
| 175 | 551 | if (t instanceof Tokens.BooleanToken) { |
| 176 | 0 | if (((Tokens.BooleanToken)t).getValue()) |
| 177 | 0 | return BoolAtom.TRUE; |
| 178 | else | |
| 179 | 0 | return BoolAtom.FALSE; |
| 180 | } | |
| 181 | 551 | else if (t instanceof Tokens.NumberToken) { |
| 182 | 114 | return new NumberAtom(((Tokens.NumberToken)t).getValue()); |
| 183 | } | |
| 184 | 437 | else if (t instanceof Tokens.QuotedTextToken) { |
| 185 | 122 | return new QuotedTextAtom(t.getText()); |
| 186 | } | |
| 187 | else { | |
| 188 | 315 | return new TextAtom(t.getText()); |
| 189 | } | |
| 190 | } | |
| 191 | ||
| 192 | /** | |
| 193 | * Throws the EOF exception if the given token is the end of file | |
| 194 | * @param t the token to check | |
| 195 | */ | |
| 196 | 1121 | private void assertNotEOF(Tokens.SExpToken t) { |
| 197 | 1121 | if (t == null) { |
| 198 | 4 | throw new PrivateParseException("Unexpected <EOF> at line " + _lex.lineno()); |
| 199 | } | |
| 200 | } | |
| 201 | } | |
| 202 | ||
| 203 | /** This runtime exception makes it easier to write the parser since | |
| 204 | * the methods of the helper class won't need to constantly declare | |
| 205 | * the SExpParseException to be thrown. | |
| 206 | */ | |
| 207 | private static class PrivateParseException extends RuntimeException { | |
| 208 | /** | |
| 209 | * Creates a runtime exception with the message that is desired for | |
| 210 | * the eventual checked exception | |
| 211 | * @param msg the message to display | |
| 212 | */ | |
| 213 | 8 | public PrivateParseException(String msg) { super(msg); } |
| 214 | } | |
| 215 | } |
|
||||||||||