您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

Parser.java 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright (c) 2006-2015 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.addons.calc;
  23. import java.text.ParseException;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.Comparator;
  27. import java.util.List;
  28. import java.util.stream.Collectors;
  29. /**
  30. * The parser takes the output from a {@link Lexer} and applies precedence rules to build the tokens
  31. * into a tree.
  32. */
  33. public class Parser {
  34. /** A list of token types sorted by their precedence. */
  35. protected static final List<TokenType> TOKENS_BY_PRECEDENCE;
  36. /** The lexer whose output will be parsed. */
  37. protected final Lexer lexer;
  38. static {
  39. TOKENS_BY_PRECEDENCE = new ArrayList<>(Arrays.asList(
  40. TokenType.values()));
  41. TOKENS_BY_PRECEDENCE.sort(new TokenTypePrecedenceComparator());
  42. }
  43. public Parser(final Lexer lexer) {
  44. this.lexer = lexer;
  45. }
  46. /**
  47. * Parses the output of this parser's lexer, and returns a {@link TreeToken} representing the
  48. * parsed formula.
  49. *
  50. * @return A token tree corresponding to the lexer's token output
  51. *
  52. * @throws ParseException If the lexer encounters a parse error, or if an error occurs while
  53. * parsing the lexer's output (such as a non-sensical formula such as one
  54. * involving a mis-matched bracket).
  55. */
  56. public TreeToken parse() throws ParseException {
  57. final List<TreeToken> tokens = lexer.tokenise().stream()
  58. .map(TreeToken::new).collect(Collectors.toList());
  59. return parse(tokens);
  60. }
  61. /**
  62. * Parses the specified tokens into a tree.
  63. *
  64. * @param tokens The tokens to be parsed
  65. *
  66. * @return A single tree containing all of the specified tokens
  67. *
  68. * @throws ParseException If the tokens contain mismatched brackets
  69. */
  70. protected TreeToken parse(final List<TreeToken> tokens)
  71. throws ParseException {
  72. while (tokens.size() > 1) {
  73. for (TokenType type : TOKENS_BY_PRECEDENCE) {
  74. final int offset = findTokenType(tokens, type);
  75. if (offset > -1) {
  76. switch (type.getArity()) {
  77. case HIDDEN:
  78. parseHiddenOperator(tokens, offset);
  79. break;
  80. case BINARY:
  81. parseBinaryOperator(tokens, offset);
  82. break;
  83. case UNARY:
  84. parseUnaryOperator(tokens, offset);
  85. break;
  86. case NULLARY:
  87. parseNullaryOperator(tokens, offset);
  88. break;
  89. }
  90. break;
  91. }
  92. }
  93. }
  94. return tokens.get(0);
  95. }
  96. /**
  97. * Parses an operator that takes no operands.
  98. *
  99. * @param tokens The supply of tokens from which the operator will be parsed
  100. * @param offset The offset at which the operator occurs
  101. *
  102. * @throws ParseException If the operator is a bracket and that bracket is mismatched
  103. */
  104. protected void parseNullaryOperator(final List<TreeToken> tokens,
  105. final int offset)
  106. throws ParseException {
  107. if (tokens.get(offset).getToken().getType()
  108. == TokenType.BRACKET_CLOSE
  109. || tokens.get(offset).getToken().getType()
  110. == TokenType.BRACKET_OPEN) {
  111. parseBracket(tokens, offset);
  112. } else {
  113. parseNumber(tokens, offset);
  114. }
  115. }
  116. /**
  117. * Parses a bracket operator.
  118. *
  119. * @param tokens The supply of tokens from which the operator will be parsed
  120. * @param offset The offset at which the operator occurs
  121. *
  122. * @throws ParseException If the operator is a bracket and that bracket is mismatched
  123. */
  124. protected void parseBracket(final List<TreeToken> tokens, final int offset)
  125. throws ParseException {
  126. final List<TreeToken> stack = new ArrayList<>();
  127. for (int i = offset - 1; i > 0; i--) {
  128. if (tokens.get(i).getToken().getType() == TokenType.BRACKET_OPEN
  129. && !tokens.get(i).isProcessed()) {
  130. tokens.add(i, parse(stack));
  131. tokens.get(i).setProcessed();
  132. tokens.remove(i + 1);
  133. tokens.remove(i + 1);
  134. return;
  135. } else {
  136. stack.add(0, tokens.get(i));
  137. tokens.remove(i);
  138. }
  139. }
  140. throw new ParseException("Couldn't find matching opening bracket",
  141. offset);
  142. }
  143. /**
  144. * Parses an operator that takes two operands.
  145. *
  146. * @param tokens The supply of tokens from which the operator will be parsed
  147. * @param offset The offset at which the operator occurs
  148. */
  149. protected void parseBinaryOperator(final List<TreeToken> tokens,
  150. final int offset) {
  151. tokens.get(offset).addChild(tokens.get(offset - 1));
  152. tokens.get(offset).addChild(tokens.get(offset + 1));
  153. tokens.get(offset).setProcessed();
  154. tokens.remove(offset + 1);
  155. tokens.remove(offset - 1);
  156. }
  157. /**
  158. * Parses an operator that takes one operand.
  159. *
  160. * @param tokens The supply of tokens from which the operator will be parsed
  161. * @param offset The offset at which the operator occurs
  162. */
  163. protected void parseUnaryOperator(final List<TreeToken> tokens,
  164. final int offset) {
  165. tokens.get(offset).addChild(tokens.get(offset + 1));
  166. tokens.get(offset).setProcessed();
  167. tokens.remove(offset + 1);
  168. }
  169. /**
  170. * Parses an operator that does not actually correspond to a piece of the input (such as the
  171. * START and END operators).
  172. *
  173. * @param tokens The supply of tokens from which the operator will be parsed
  174. * @param offset The offset at which the operator occurs
  175. */
  176. protected void parseHiddenOperator(final List<TreeToken> tokens,
  177. final int offset) {
  178. tokens.remove(offset);
  179. }
  180. /**
  181. * Parses a number.
  182. *
  183. * @param tokens The supply of tokens from which the operator will be parsed
  184. * @param offset The offset at which the operator occurs
  185. */
  186. protected void parseNumber(final List<TreeToken> tokens, final int offset) {
  187. tokens.get(offset).setProcessed();
  188. }
  189. /**
  190. * Retrieves the offset of the first token within the input list that has a type corresponding
  191. * to the specified {@link TokenType}.
  192. *
  193. * @param tokens The tokens to be searched
  194. * @param type The desired token type
  195. *
  196. * @return The index of the first token with that type, or -1 if none found
  197. */
  198. protected static int findTokenType(final List<TreeToken> tokens,
  199. final TokenType type) {
  200. for (int i = 0; i < tokens.size(); i++) {
  201. if (tokens.get(i).getToken().getType() == type && !tokens.get(i)
  202. .isProcessed()) {
  203. return i;
  204. }
  205. }
  206. return -1;
  207. }
  208. /**
  209. * A class which compares token types based on their precedence.
  210. */
  211. protected static class TokenTypePrecedenceComparator implements
  212. Comparator<TokenType> {
  213. @Override
  214. public int compare(final TokenType o1, final TokenType o2) {
  215. return o2.getPrecedence() - o1.getPrecedence();
  216. }
  217. }
  218. }