View Javadoc
1   package de.fhdw.wtf.parser;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.List;
6   
7   import de.fhdw.wtf.common.ast.Group;
8   import de.fhdw.wtf.common.ast.GroupElement;
9   import de.fhdw.wtf.common.ast.Name;
10  import de.fhdw.wtf.common.exception.parser.AbstractParserException;
11  import de.fhdw.wtf.common.exception.parser.NoSemicolonException;
12  import de.fhdw.wtf.common.stream.TokenStream;
13  import de.fhdw.wtf.common.token.IdentifierToken;
14  import de.fhdw.wtf.common.token.Token;
15  
16  /**
17   * Parser to parse a group from a given tokenstream.
18   */
19  final class GroupParser {
20  	/**
21  	 * TokenStream.
22  	 */
23  	private final TokenStream stream;
24  	
25  	/**
26  	 * Collection of exceptions.
27  	 */
28  	private final Collection<AbstractParserException> exceptions;
29  	
30  	/**
31  	 * Constructor of {@link GroupParser}.
32  	 * 
33  	 * @param stream
34  	 *            tokenStream
35  	 * @param exceptions
36  	 *            collection of exceptions
37  	 */
38  	private GroupParser(final TokenStream stream, final Collection<AbstractParserException> exceptions) {
39  		this.stream = stream;
40  		this.exceptions = exceptions;
41  	}
42  	
43  	/**
44  	 * FactoryMethod for {@link GroupParser}.
45  	 * 
46  	 * @param stream
47  	 *            tokenStream
48  	 * @param exceptions
49  	 *            collection of exceptions
50  	 * @return The {@link GroupParser}-Object.
51  	 */
52  	static GroupParser create(final TokenStream stream, final Collection<AbstractParserException> exceptions) {
53  		return new GroupParser(stream, exceptions);
54  	}
55  	
56  	/**
57  	 * Parses a Group with <code>groupName</code> as the group name from this.stream. This method expects the first
58  	 * Token in the stream to be an EqualToken and the second to be an SquareBracketOpenToken. This method calls an
59  	 * operation to parse the Elements of the group. If one of the Tokens in the stream is unexpected, an
60  	 * AbstractParserException will be thrown.
61  	 * 
62  	 * @param groupName
63  	 *            groupName
64  	 * @param firstToken
65  	 *            firstToken
66  	 * @return parsed Group
67  	 */
68  	Group parse(final Name groupName, final Token firstToken) {
69  		final List<GroupElement> groups = new ArrayList<>();
70  		final Group group = Group.create(groupName, groups, firstToken);
71  		try {
72  			ParserUtils.requireAndRemoveEqualToken(this.stream);
73  			ParserUtils.requireAndRemoveSquareBracketOpenToken(this.stream);
74  		} catch (final AbstractParserException e) {
75  			if (this.stream.hasNext() && this.stream.peek().isSquareBracketOpenToken()) {
76  				this.stream.next();
77  			}
78  			this.exceptions.add(e);
79  		}
80  		while (this.stream.hasNext() && !(this.stream.peek().isEndToken()) && !this.checkGroupEnd()) {
81  			try {
82  				final IdentifierToken idtoken = ParserUtils.requireAndRemoveIdentifier(this.stream);
83  				final Token nextToken = this.stream.peek();
84  				ParserUtils.requireAndRemoveColonToken(this.stream);
85  				ParserUtils.requireGroupElement(this.stream);
86  				final Name newName = groupName.addName(idtoken, nextToken);
87  				newName.setFirstToken(idtoken); // Don't reuse the group tokens!
88  												// -> safe if reusing the
89  												// tokens.
90  				groups.add(this.parseGroupElement(newName));
91  			} catch (final AbstractParserException e) {
92  				this.exceptions.add(e);
93  				ParserUtils.skipToSemicolonToken(this.stream);
94  				// if the next token is an EndToken, we can stop here.
95  				// If we don't stop here, we always get a NoSemicolonException.
96  				if (this.stream.peek().isEndToken()) {
97  					return group;
98  				} else {
99  					continue;
100 				}
101 			}
102 		}
103 		try {
104 			ParserUtils.requireSemicolonToken(this.stream);
105 			this.stream.next();
106 			group.setLastToken(this.stream.peek());
107 		} catch (final NoSemicolonException e) {
108 			this.exceptions.add(e);
109 		}
110 		return group;
111 	}
112 	
113 	/**
114 	 * This method parses a GroupElement into a syntax tree structure. Be careful to call this method only in the case
115 	 * that the next token is a GroupElement.
116 	 * 
117 	 * @param name
118 	 *            : Name of the GroupElement.
119 	 * @return : GroupElement.
120 	 * @throws AbstractParserException
121 	 *             If neither .parseClass or .parseGroup throws an exception.
122 	 */
123 	private GroupElement parseGroupElement(final Name name) throws AbstractParserException {
124 		final Token nextToken = this.stream.next();
125 		if (nextToken.isClassToken()) {
126 			return RegularClassParser.create(this.stream, this.exceptions).parse(name, name.getFirstToken());
127 		} else if (nextToken.isExceptionToken()) {
128 			return ExceptionClassParser.create(this.stream, this.exceptions).parse(name, name.getFirstToken());
129 		} else {
130 			return this.parse(name, name.getFirstToken());
131 		}
132 	}
133 	
134 	/**
135 	 * Returns true if the next Token of this.stream is a SquareBracketCloseToken, otherwise false.
136 	 * 
137 	 * @return Boolean
138 	 */
139 	private boolean checkGroupEnd() {
140 		if (!this.stream.peek().isSquareBracketCloseToken()) {
141 			return false;
142 		}
143 		this.stream.removeFirst();
144 		return true;
145 	}
146 }