Parser.java 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package org.ssssssss.expression.parsing;
  2. import org.ssssssss.expression.ExpressionError;
  3. import org.ssssssss.expression.ExpressionTemplate;
  4. import javax.xml.transform.Source;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. /** Parses a {@link Source} into a {@link ExpressionTemplate}. The implementation is a simple recursive descent parser with a lookahead of
  8. * 1. **/
  9. public class Parser {
  10. /** Parses a {@link Source} into a {@link ExpressionTemplate}. **/
  11. public static List<Ast.Node> parse (String source) {
  12. List<Ast.Node> nodes = new ArrayList<Ast.Node>();
  13. TokenStream stream = new TokenStream(new Tokenizer().tokenize(source));
  14. while (stream.hasMore()) {
  15. nodes.add(parseStatement(stream));
  16. }
  17. return nodes;
  18. }
  19. /** Parse a statement, which may either be a text block, if statement, for statement, while statement, macro definition,
  20. * include statement or an expression. **/
  21. private static Ast.Node parseStatement (TokenStream tokens) {
  22. Ast.Node result = null;
  23. if (tokens.match(TokenType.TextBlock, false)) {
  24. result = new Ast.Text(tokens.consume().getSpan());
  25. } else {
  26. result = parseExpression(tokens);
  27. }
  28. // consume semi-colons as statement delimiters
  29. while (tokens.match(";", true)) {
  30. ;
  31. }
  32. return result;
  33. }
  34. private static Ast.Expression parseExpression (TokenStream stream) {
  35. return parseTernaryOperator(stream);
  36. }
  37. private static Ast.Expression parseTernaryOperator (TokenStream stream) {
  38. Ast.Expression condition = parseBinaryOperator(stream, 0);
  39. if (stream.match(TokenType.Questionmark, true)) {
  40. Ast.Expression trueExpression = parseTernaryOperator(stream);
  41. stream.expect(TokenType.Colon);
  42. Ast.Expression falseExpression = parseTernaryOperator(stream);
  43. return new Ast.TernaryOperation(condition, trueExpression, falseExpression);
  44. } else {
  45. return condition;
  46. }
  47. }
  48. private static final TokenType[][] binaryOperatorPrecedence = new TokenType[][] {new TokenType[] {TokenType.Assignment},
  49. new TokenType[] {TokenType.Or, TokenType.And, TokenType.Xor}, new TokenType[] {TokenType.Equal, TokenType.NotEqual},
  50. new TokenType[] {TokenType.Less, TokenType.LessEqual, TokenType.Greater, TokenType.GreaterEqual}, new TokenType[] {TokenType.Plus, TokenType.Minus},
  51. new TokenType[] {TokenType.ForwardSlash, TokenType.Asterisk, TokenType.Percentage}};
  52. private static Ast.Expression parseBinaryOperator (TokenStream stream, int level) {
  53. int nextLevel = level + 1;
  54. Ast.Expression left = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream) : parseBinaryOperator(stream, nextLevel);
  55. TokenType[] operators = binaryOperatorPrecedence[level];
  56. while (stream.hasMore() && stream.match(false, operators)) {
  57. Token operator = stream.consume();
  58. Ast.Expression right = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream) : parseBinaryOperator(stream, nextLevel);
  59. left = new Ast.BinaryOperation(left, operator, right);
  60. }
  61. return left;
  62. }
  63. private static final TokenType[] unaryOperators = new TokenType[] {TokenType.Not, TokenType.Plus, TokenType.Minus};
  64. private static Ast.Expression parseUnaryOperator (TokenStream stream) {
  65. if (stream.match(false, unaryOperators)) {
  66. return new Ast.UnaryOperation(stream.consume(), parseUnaryOperator(stream));
  67. } else {
  68. if (stream.match(TokenType.LeftParantheses, true)) {
  69. Ast.Expression expression = parseExpression(stream);
  70. stream.expect(TokenType.RightParantheses);
  71. return expression;
  72. } else {
  73. return parseAccessOrCallOrLiteral(stream);
  74. }
  75. }
  76. }
  77. private static Ast.Expression parseAccessOrCallOrLiteral (TokenStream stream) {
  78. if (stream.match(TokenType.Identifier, false)) {
  79. return parseAccessOrCall(stream,TokenType.Identifier);
  80. } else if (stream.match(TokenType.LeftCurly, false)) {
  81. return parseMapLiteral(stream);
  82. } else if (stream.match(TokenType.LeftBracket, false)) {
  83. return parseListLiteral(stream);
  84. } else if (stream.match(TokenType.StringLiteral, false)) {
  85. if(stream.hasNext()){
  86. if(stream.next().getType() == TokenType.Period){
  87. stream.prev();
  88. return parseAccessOrCall(stream,TokenType.StringLiteral);
  89. }
  90. stream.prev();
  91. }
  92. return new Ast.StringLiteral(stream.expect(TokenType.StringLiteral).getSpan());
  93. } else if (stream.match(TokenType.BooleanLiteral, false)) {
  94. return new Ast.BooleanLiteral(stream.expect(TokenType.BooleanLiteral).getSpan());
  95. } else if (stream.match(TokenType.DoubleLiteral, false)) {
  96. return new Ast.DoubleLiteral(stream.expect(TokenType.DoubleLiteral).getSpan());
  97. } else if (stream.match(TokenType.FloatLiteral, false)) {
  98. return new Ast.FloatLiteral(stream.expect(TokenType.FloatLiteral).getSpan());
  99. } else if (stream.match(TokenType.ByteLiteral, false)) {
  100. return new Ast.ByteLiteral(stream.expect(TokenType.ByteLiteral).getSpan());
  101. } else if (stream.match(TokenType.ShortLiteral, false)) {
  102. return new Ast.ShortLiteral(stream.expect(TokenType.ShortLiteral).getSpan());
  103. } else if (stream.match(TokenType.IntegerLiteral, false)) {
  104. return new Ast.IntegerLiteral(stream.expect(TokenType.IntegerLiteral).getSpan());
  105. } else if (stream.match(TokenType.LongLiteral, false)) {
  106. return new Ast.LongLiteral(stream.expect(TokenType.LongLiteral).getSpan());
  107. } else if (stream.match(TokenType.CharacterLiteral, false)) {
  108. return new Ast.CharacterLiteral(stream.expect(TokenType.CharacterLiteral).getSpan());
  109. } else if (stream.match(TokenType.NullLiteral, false)) {
  110. return new Ast.NullLiteral(stream.expect(TokenType.NullLiteral).getSpan());
  111. } else {
  112. ExpressionError.error("Expected a variable, field, map, array, function or method call, or literal.", stream);
  113. return null; // not reached
  114. }
  115. }
  116. private static Ast.Expression parseMapLiteral (TokenStream stream) {
  117. Span openCurly = stream.expect(TokenType.LeftCurly).getSpan();
  118. List<Token> keys = new ArrayList<>();
  119. List<Ast.Expression> values = new ArrayList<>();
  120. while (stream.hasMore() && !stream.match("}", false)) {
  121. if(stream.match(TokenType.StringLiteral, false)){
  122. keys.add(stream.expect(TokenType.StringLiteral));
  123. }else{
  124. keys.add(stream.expect(TokenType.Identifier));
  125. }
  126. stream.expect(":");
  127. values.add(parseExpression(stream));
  128. if (!stream.match("}", false)) {
  129. stream.expect(TokenType.Comma);
  130. }
  131. }
  132. Span closeCurly = stream.expect("}").getSpan();
  133. return new Ast.MapLiteral(new Span(openCurly, closeCurly), keys, values);
  134. }
  135. private static Ast.Expression parseListLiteral (TokenStream stream) {
  136. Span openBracket = stream.expect(TokenType.LeftBracket).getSpan();
  137. List<Ast.Expression> values = new ArrayList<>();
  138. while (stream.hasMore() && !stream.match(TokenType.RightBracket, false)) {
  139. values.add(parseExpression(stream));
  140. if (!stream.match(TokenType.RightBracket, false)) {
  141. stream.expect(TokenType.Comma);
  142. }
  143. }
  144. Span closeBracket = stream.expect(TokenType.RightBracket).getSpan();
  145. return new Ast.ListLiteral(new Span(openBracket, closeBracket), values);
  146. }
  147. private static Ast.Expression parseAccessOrCall (TokenStream stream, TokenType tokenType) {
  148. //Span identifier = stream.expect(TokenType.Identifier);
  149. //Expression result = new VariableAccess(identifier);
  150. Span identifier = stream.expect(tokenType).getSpan();
  151. Ast.Expression result = tokenType == TokenType.StringLiteral ? new Ast.StringLiteral(identifier) :new Ast.VariableAccess(identifier);
  152. while (stream.hasMore() && stream.match(false, TokenType.LeftParantheses, TokenType.LeftBracket, TokenType.Period, TokenType.Lambda)) {
  153. // function or method call
  154. if (stream.match(TokenType.LeftParantheses, false)) {
  155. List<Ast.Expression> arguments = parseArguments(stream);
  156. Span closingSpan = stream.expect(TokenType.RightParantheses).getSpan();
  157. if (result instanceof Ast.VariableAccess || result instanceof Ast.MapOrArrayAccess) {
  158. result = new Ast.FunctionCall(new Span(result.getSpan(), closingSpan), result, arguments);
  159. } else if (result instanceof Ast.MemberAccess) {
  160. for (Ast.Expression expression : arguments) {
  161. if (expression instanceof Ast.LambdaAccess) {
  162. Ast.LambdaAccess lambdaAccess = (Ast.LambdaAccess) expression;
  163. lambdaAccess.setArrayLike((Ast.MemberAccess) result);
  164. }
  165. }
  166. Ast.MethodCall methodCall = new Ast.MethodCall(new Span(result.getSpan(), closingSpan), (Ast.MemberAccess) result, arguments);
  167. if ("map".equals(((Ast.MemberAccess) result).getName().getText())) {
  168. try {
  169. methodCall.setCachedMethod(ArrayLikeLambdaExecutor.class.getMethod("map", Object.class, Object[].class));
  170. } catch (NoSuchMethodException e) {
  171. e.printStackTrace();
  172. }
  173. methodCall.setCachedMethodStatic(true);
  174. }
  175. result = methodCall;
  176. } else {
  177. ExpressionError.error("Expected a variable, field or method.", stream);
  178. }
  179. }
  180. // map or array access
  181. else if (stream.match(TokenType.LeftBracket, true)) {
  182. Ast.Expression keyOrIndex = parseExpression(stream);
  183. Span closingSpan = stream.expect(TokenType.RightBracket).getSpan();
  184. result = new Ast.MapOrArrayAccess(new Span(result.getSpan(), closingSpan), result, keyOrIndex);
  185. }
  186. // field or method access
  187. else if (stream.match(TokenType.Period, true)) {
  188. identifier = stream.expect(TokenType.Identifier).getSpan();
  189. result = new Ast.MemberAccess(result, identifier);
  190. }
  191. else if (stream.match(TokenType.Lambda, true)) {
  192. Ast.Expression key = parseExpression(stream);
  193. // Span closingSpan = stream.expect(TokenType.RightParantheses).getSpan();
  194. result = new Ast.LambdaAccess(new Span(result.getSpan(), key.getSpan()), result, key);
  195. }
  196. }
  197. return result;
  198. }
  199. /** Does not consume the closing parentheses. **/
  200. private static List<Ast.Expression> parseArguments (TokenStream stream) {
  201. stream.expect(TokenType.LeftParantheses);
  202. List<Ast.Expression> arguments = new ArrayList<Ast.Expression>();
  203. while (stream.hasMore() && !stream.match(TokenType.RightParantheses, false)) {
  204. arguments.add(parseExpression(stream));
  205. if (!stream.match(TokenType.RightParantheses, false)) {
  206. stream.expect(TokenType.Comma);
  207. }
  208. }
  209. return arguments;
  210. }
  211. }