|
|||||||||||||||||||
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 | } |
|