Parser.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. package org.ssssssss.script.parsing;
  2. import org.ssssssss.script.MagicScript;
  3. import org.ssssssss.script.MagicScriptContext;
  4. import org.ssssssss.script.MagicScriptError;
  5. import org.ssssssss.script.interpreter.AstInterpreter;
  6. import org.ssssssss.script.parsing.ast.*;
  7. import org.ssssssss.script.parsing.ast.binary.BinaryOperation;
  8. import javax.xml.transform.Source;
  9. import java.util.ArrayList;
  10. import java.util.Arrays;
  11. import java.util.HashMap;
  12. import java.util.List;
  13. /**
  14. * Parses a {@link Source} into a {@link MagicScript}. The implementation is a simple recursive descent parser with a lookahead of
  15. * 1.
  16. **/
  17. public class Parser {
  18. private static final TokenType[][] binaryOperatorPrecedence = new TokenType[][]{new TokenType[]{TokenType.Assignment},
  19. new TokenType[]{TokenType.Or, TokenType.And, TokenType.Xor}, new TokenType[]{TokenType.Equal, TokenType.NotEqual},
  20. new TokenType[]{TokenType.Less, TokenType.LessEqual, TokenType.Greater, TokenType.GreaterEqual}, new TokenType[]{TokenType.Plus, TokenType.Minus},
  21. new TokenType[]{TokenType.ForwardSlash, TokenType.Asterisk, TokenType.Percentage}};
  22. private static final TokenType[] unaryOperators = new TokenType[]{TokenType.Not, TokenType.Plus, TokenType.Minus};
  23. /**
  24. * Parses a {@link Source} into a {@link MagicScript}.
  25. **/
  26. public static List<Node> parse(String source) {
  27. List<Node> nodes = new ArrayList<Node>();
  28. TokenStream stream = new TokenStream(new Tokenizer().tokenize(source));
  29. while (stream.hasMore()) {
  30. Node node = parseStatement(stream);
  31. if (node != null) {
  32. nodes.add(node);
  33. }
  34. }
  35. return nodes;
  36. }
  37. private static Node parseStatement(TokenStream tokens) {
  38. return parseStatement(tokens, false);
  39. }
  40. private static Node parseStatement(TokenStream tokens, boolean expectRightCurly) {
  41. Node result = null;
  42. if (tokens.match("import", false)) {
  43. result = parseImport(tokens);
  44. } else if (tokens.match("var", false)) {
  45. result = parseVarDefine(tokens);
  46. } else if (tokens.match("if", false)) {
  47. result = parseIfStatement(tokens);
  48. } else if (tokens.match("return", false)) {
  49. result = parseReturn(tokens);
  50. } else if (tokens.match("for", false)) {
  51. result = parseForStatement(tokens);
  52. } else if (tokens.match("continue", false)) {
  53. result = new Continue(tokens.consume().getSpan());
  54. } else if (tokens.match("break", false)) {
  55. result = new Break(tokens.consume().getSpan());
  56. } else {
  57. result = parseExpression(tokens, expectRightCurly);
  58. }
  59. // consume semi-colons as statement delimiters
  60. while (tokens.match(";", true)) {
  61. ;
  62. }
  63. return result;
  64. }
  65. private static Import parseImport(TokenStream stream) {
  66. Span opening = stream.expect("import").getSpan();
  67. if (stream.hasMore()) {
  68. Token expected = stream.consume();
  69. String packageName = null;
  70. boolean isStringLiteral = expected.getType() == TokenType.StringLiteral;
  71. if (isStringLiteral) {
  72. packageName = new StringLiteral(expected.getSpan()).getValue();
  73. } else if (expected.getType() == TokenType.Identifier) {
  74. packageName = expected.getSpan().getText();
  75. } else {
  76. MagicScriptError.error("Expected identifier or string, but got stream is " + expected.getType().getError(), stream.getPrev().getSpan());
  77. }
  78. String varName = packageName;
  79. if (isStringLiteral || stream.match("as",false)) {
  80. stream.expect("as");
  81. expected = stream.expect(TokenType.Identifier);
  82. varName = expected.getSpan().getText();
  83. }
  84. return new Import(new Span(opening, expected.getSpan()), packageName, varName, !isStringLiteral);
  85. }
  86. MagicScriptError.error("Expected identifier or string, but got stream is EOF", stream.getPrev().getSpan());
  87. return null;
  88. }
  89. private static VariableDefine parseVarDefine(TokenStream stream) {
  90. Span opening = stream.expect("var").getSpan();
  91. TokenType expected = null;
  92. if (stream.hasMore()) {
  93. expected = TokenType.Identifier;
  94. Token token = stream.expect(expected);
  95. String variableName = token.getSpan().getText();
  96. expected = TokenType.Assignment;
  97. if (stream.hasMore()) {
  98. stream.expect(expected);
  99. Expression right = parseExpression(stream);
  100. return new VariableDefine(new Span(opening, stream.getPrev().getSpan()), variableName, right);
  101. }
  102. }
  103. MagicScriptError.error("Expected " + expected.getError() + ", but got stream is EOF", stream.getPrev().getSpan());
  104. return null;
  105. }
  106. private static FunctionStatement parseFunctionStatement(TokenStream stream) {
  107. Span openingFunction = stream.expect("function").getSpan();
  108. stream.expect("(");
  109. List<String> parameters = new ArrayList<>();
  110. while (stream.hasMore() && stream.match(TokenType.Identifier, false)) {
  111. Token token = stream.consume();
  112. parameters.add(token.getSpan().getText());
  113. if (!stream.hasMore()) {
  114. MagicScriptError.error("function Did not closing ", stream.prev().getSpan());
  115. }
  116. if (!stream.match(TokenType.Comma, true)) {
  117. if (!stream.match(TokenType.RightParantheses, false)) {
  118. MagicScriptError.error("Did not find closing ).", stream.prev().getSpan());
  119. } else {
  120. break;
  121. }
  122. }
  123. }
  124. stream.expect(")");
  125. stream.expect("{");
  126. List<Node> childNodes = new ArrayList<>();
  127. while (stream.hasMore() && !stream.match(false, "}")) {
  128. childNodes.add(parseStatement(stream, true));
  129. }
  130. Span closingEnd = expectCloseing(stream);
  131. return new FunctionStatement(new Span(openingFunction, closingEnd), childNodes, parameters);
  132. }
  133. private static ForStatement parseForStatement(TokenStream stream) {
  134. Span openingFor = stream.expect("for").getSpan();
  135. stream.expect("(");
  136. Span index = null;
  137. Span value = stream.expect(TokenType.Identifier).getSpan();
  138. if (stream.match(TokenType.Comma, true)) {
  139. index = value;
  140. value = stream.expect(TokenType.Identifier).getSpan();
  141. }
  142. stream.expect("in");
  143. Expression mapOrArray = parseExpression(stream);
  144. stream.expect(")");
  145. stream.expect("{");
  146. List<Node> body = new ArrayList<Node>();
  147. while (stream.hasMore() && !stream.match(false, "}")) {
  148. body.add(parseStatement(stream, true));
  149. }
  150. Span closingEnd = expectCloseing(stream);
  151. return new ForStatement(new Span(openingFor, closingEnd), index, value, mapOrArray, body);
  152. }
  153. private static Span expectCloseing(TokenStream stream) {
  154. if (!stream.hasMore()) {
  155. MagicScriptError.error("Did not find closing }.", stream.prev().getSpan());
  156. }
  157. return stream.expect("}").getSpan();
  158. }
  159. private static Node parseIfStatement(TokenStream stream) {
  160. Span openingIf = stream.expect("if").getSpan();
  161. Expression condition = parseExpression(stream);
  162. stream.expect("{");
  163. List<Node> trueBlock = new ArrayList<Node>();
  164. while (stream.hasMore() && !stream.match("}", false)) {
  165. Node node = parseStatement(stream, true);
  166. if (node != null) {
  167. trueBlock.add(node);
  168. }
  169. }
  170. expectCloseing(stream);
  171. List<IfStatement> elseIfs = new ArrayList<IfStatement>();
  172. List<Node> falseBlock = new ArrayList<Node>();
  173. while (stream.hasMore() && stream.match("else", true)) {
  174. if (stream.hasMore() && stream.match("if", false)) {
  175. Span elseIfOpening = stream.expect("if").getSpan();
  176. Expression elseIfCondition = parseExpression(stream);
  177. stream.expect("{");
  178. List<Node> elseIfBlock = new ArrayList<Node>();
  179. while (stream.hasMore() && !stream.match("}", false)) {
  180. Node node = parseStatement(stream, true);
  181. if (node != null) {
  182. elseIfBlock.add(node);
  183. }
  184. }
  185. expectCloseing(stream);
  186. Span elseIfSpan = new Span(elseIfOpening, elseIfBlock.size() > 0 ? elseIfBlock.get(elseIfBlock.size() - 1).getSpan() : elseIfOpening);
  187. elseIfs.add(new IfStatement(elseIfSpan, elseIfCondition, elseIfBlock, new ArrayList<IfStatement>(), new ArrayList<Node>()));
  188. } else {
  189. stream.expect("{");
  190. while (stream.hasMore() && !stream.match("}", false)) {
  191. falseBlock.add(parseStatement(stream, true));
  192. }
  193. expectCloseing(stream);
  194. break;
  195. }
  196. }
  197. Span closingEnd = stream.getPrev().getSpan();
  198. return new IfStatement(new Span(openingIf, closingEnd), condition, trueBlock, elseIfs, falseBlock);
  199. }
  200. private static Node parseReturn(TokenStream tokens) {
  201. Span returnSpan = tokens.expect("return").getSpan();
  202. if (tokens.match(";", false)) return new Return(returnSpan, null);
  203. Expression returnValue = parseExpression(tokens);
  204. return new Return(new Span(returnSpan, returnValue.getSpan()), returnValue);
  205. }
  206. public static Expression parseExpression(TokenStream stream) {
  207. return parseTernaryOperator(stream);
  208. }
  209. public static Expression parseExpression(TokenStream stream, boolean expectRightCurly) {
  210. return parseTernaryOperator(stream, expectRightCurly);
  211. }
  212. private static Expression parseTernaryOperator(TokenStream stream, boolean expectRightCurly) {
  213. Expression condition = parseBinaryOperator(stream, 0, expectRightCurly);
  214. if (stream.match(TokenType.Questionmark, true)) {
  215. Expression trueExpression = parseTernaryOperator(stream, expectRightCurly);
  216. stream.expect(TokenType.Colon);
  217. Expression falseExpression = parseTernaryOperator(stream, expectRightCurly);
  218. return new TernaryOperation(condition, trueExpression, falseExpression);
  219. } else {
  220. return condition;
  221. }
  222. }
  223. private static Expression parseTernaryOperator(TokenStream stream) {
  224. return parseTernaryOperator(stream, false);
  225. }
  226. private static Expression parseBinaryOperator(TokenStream stream, int level, boolean expectRightCurly) {
  227. int nextLevel = level + 1;
  228. Expression left = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream, expectRightCurly) : parseBinaryOperator(stream, nextLevel, expectRightCurly);
  229. TokenType[] operators = binaryOperatorPrecedence[level];
  230. while (stream.hasMore() && stream.match(false, operators)) {
  231. Token operator = stream.consume();
  232. Expression right = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream, expectRightCurly) : parseBinaryOperator(stream, nextLevel, expectRightCurly);
  233. left = BinaryOperation.create(left, operator, right);
  234. }
  235. return left;
  236. }
  237. private static Expression parseUnaryOperator(TokenStream stream, boolean expectRightCurly) {
  238. if (stream.match(false, unaryOperators)) {
  239. return new UnaryOperation(stream.consume(), parseUnaryOperator(stream, expectRightCurly));
  240. } else {
  241. if (stream.match(TokenType.LeftParantheses, false)) { //(
  242. Span openSpan = stream.expect(TokenType.LeftParantheses).getSpan();
  243. int index = stream.makeIndex();
  244. List<String> parameters = new ArrayList<>();
  245. while(stream.match(TokenType.Identifier,false)){
  246. Token identifier = stream.expect(TokenType.Identifier);
  247. parameters.add(identifier.getSpan().getText());
  248. if(stream.match(TokenType.Comma,true)){ //,
  249. continue;
  250. }
  251. if(stream.match(TokenType.RightParantheses,true)){ //)
  252. if(stream.match(TokenType.Lambda,true)){ // =>
  253. return parseLambdaBody(stream, openSpan, parameters);
  254. }
  255. break;
  256. }
  257. }
  258. if(stream.match(TokenType.RightParantheses,true) && stream.match(TokenType.Lambda, true)){
  259. return parseLambdaBody(stream, openSpan, parameters);
  260. }
  261. stream.resetIndex(index);
  262. Expression expression = parseExpression(stream);
  263. stream.expect(TokenType.RightParantheses);
  264. return expression;
  265. } else {
  266. return parseAccessOrCallOrLiteral(stream,expectRightCurly);
  267. }
  268. }
  269. }
  270. private static Expression parseLambdaBody(TokenStream stream, Span openSpan, List<String> parameters) {
  271. int index = stream.makeIndex();
  272. List<Node> childNodes = new ArrayList<>();
  273. try {
  274. Expression expression = parseExpression(stream);
  275. childNodes.add(new Return(new Span("return", 0, 6), expression));
  276. return new LambdaFunction(new Span(openSpan, expression.getSpan()), parameters, childNodes);
  277. } catch (Exception e) {
  278. stream.resetIndex(index);
  279. if (stream.match(TokenType.LeftCurly, true)) {
  280. while (stream.hasMore() && !stream.match(false, "}")) {
  281. childNodes.add(parseStatement(stream, true));
  282. }
  283. Span closeSpan = expectCloseing(stream);
  284. return new LambdaFunction(new Span(openSpan, closeSpan), parameters, childNodes);
  285. } else {
  286. Node node = parseStatement(stream);
  287. childNodes.add(new Return(new Span("return", 0, 6), node));
  288. return new LambdaFunction(new Span(openSpan, node.getSpan()), parameters, childNodes);
  289. }
  290. }
  291. }
  292. private static Expression parseAccessOrCallOrLiteral(TokenStream stream, boolean expectRightCurly) {
  293. if (expectRightCurly && stream.match("}", false)) {
  294. return null;
  295. } else if (stream.match("function", false)) {
  296. return parseFunctionStatement(stream);
  297. } else if (stream.match(TokenType.Identifier, false)) {
  298. return parseAccessOrCall(stream, TokenType.Identifier);
  299. } else if (stream.match(TokenType.LeftCurly, false)) {
  300. return parseMapLiteral(stream);
  301. } else if (stream.match(TokenType.LeftBracket, false)) {
  302. return parseListLiteral(stream);
  303. } else if (stream.match(TokenType.StringLiteral, false)) {
  304. if (stream.hasNext()) {
  305. if (stream.next().getType() == TokenType.Period) {
  306. stream.prev();
  307. return parseAccessOrCall(stream, TokenType.StringLiteral);
  308. }
  309. stream.prev();
  310. }
  311. return new StringLiteral(stream.expect(TokenType.StringLiteral).getSpan());
  312. } else if (stream.match(TokenType.BooleanLiteral, false)) {
  313. return new BooleanLiteral(stream.expect(TokenType.BooleanLiteral).getSpan());
  314. } else if (stream.match(TokenType.DoubleLiteral, false)) {
  315. return new DoubleLiteral(stream.expect(TokenType.DoubleLiteral).getSpan());
  316. } else if (stream.match(TokenType.FloatLiteral, false)) {
  317. return new FloatLiteral(stream.expect(TokenType.FloatLiteral).getSpan());
  318. } else if (stream.match(TokenType.ByteLiteral, false)) {
  319. return new ByteLiteral(stream.expect(TokenType.ByteLiteral).getSpan());
  320. } else if (stream.match(TokenType.ShortLiteral, false)) {
  321. return new ShortLiteral(stream.expect(TokenType.ShortLiteral).getSpan());
  322. } else if (stream.match(TokenType.IntegerLiteral, false)) {
  323. return new IntegerLiteral(stream.expect(TokenType.IntegerLiteral).getSpan());
  324. } else if (stream.match(TokenType.LongLiteral, false)) {
  325. return new LongLiteral(stream.expect(TokenType.LongLiteral).getSpan());
  326. } else if (stream.match(TokenType.CharacterLiteral, false)) {
  327. return new CharacterLiteral(stream.expect(TokenType.CharacterLiteral).getSpan());
  328. } else if (stream.match(TokenType.NullLiteral, false)) {
  329. return new NullLiteral(stream.expect(TokenType.NullLiteral).getSpan());
  330. } else {
  331. MagicScriptError.error("Expected a variable, field, map, array, function or method call, or literal.", stream);
  332. return null; // not reached
  333. }
  334. }
  335. private static Expression parseMapLiteral(TokenStream stream) {
  336. Span openCurly = stream.expect(TokenType.LeftCurly).getSpan();
  337. List<Token> keys = new ArrayList<>();
  338. List<Expression> values = new ArrayList<>();
  339. while (stream.hasMore() && !stream.match("}", false)) {
  340. if (stream.match(TokenType.StringLiteral, false)) {
  341. keys.add(stream.expect(TokenType.StringLiteral));
  342. } else {
  343. keys.add(stream.expect(TokenType.Identifier));
  344. }
  345. stream.expect(":");
  346. values.add(parseExpression(stream));
  347. if (!stream.match("}", false)) {
  348. stream.expect(TokenType.Comma);
  349. }
  350. }
  351. Span closeCurly = stream.expect("}").getSpan();
  352. return new MapLiteral(new Span(openCurly, closeCurly), keys, values);
  353. }
  354. private static Expression parseListLiteral(TokenStream stream) {
  355. Span openBracket = stream.expect(TokenType.LeftBracket).getSpan();
  356. List<Expression> values = new ArrayList<>();
  357. while (stream.hasMore() && !stream.match(TokenType.RightBracket, false)) {
  358. values.add(parseExpression(stream));
  359. if (!stream.match(TokenType.RightBracket, false)) {
  360. stream.expect(TokenType.Comma);
  361. }
  362. }
  363. Span closeBracket = stream.expect(TokenType.RightBracket).getSpan();
  364. return new ListLiteral(new Span(openBracket, closeBracket), values);
  365. }
  366. private static Expression parseAccessOrCall(TokenStream stream, TokenType tokenType) {
  367. //Span identifier = stream.expect(TokenType.Identifier);
  368. //Expression result = new VariableAccess(identifier);
  369. Span identifier = stream.expect(tokenType).getSpan();
  370. if (tokenType == TokenType.Identifier && stream.match(TokenType.Lambda, true)) {
  371. return parseLambdaBody(stream, identifier, Arrays.asList(identifier.getText()));
  372. }
  373. Expression result = tokenType == TokenType.StringLiteral ? new StringLiteral(identifier) : new VariableAccess(identifier);
  374. while (stream.hasMore() && stream.match(false, TokenType.LeftParantheses, TokenType.LeftBracket, TokenType.Period)) {
  375. // function or method call
  376. if (stream.match(TokenType.LeftParantheses, false)) {
  377. List<Expression> arguments = parseArguments(stream);
  378. Span closingSpan = stream.expect(TokenType.RightParantheses).getSpan();
  379. if (result instanceof VariableAccess || result instanceof MapOrArrayAccess)
  380. result = new FunctionCall(new Span(result.getSpan(), closingSpan), result, arguments);
  381. else if (result instanceof MemberAccess) {
  382. result = new MethodCall(new Span(result.getSpan(), closingSpan), (MemberAccess)result, arguments);
  383. } else {
  384. MagicScriptError.error("Expected a variable, field or method.", stream);
  385. }
  386. }
  387. // map or array access
  388. else if (stream.match(TokenType.LeftBracket, true)) {
  389. Expression keyOrIndex = parseExpression(stream);
  390. Span closingSpan = stream.expect(TokenType.RightBracket).getSpan();
  391. result = new MapOrArrayAccess(new Span(result.getSpan(), closingSpan), result, keyOrIndex);
  392. }
  393. // field or method access
  394. else if (stream.match(TokenType.Period, true)) {
  395. identifier = stream.expect(TokenType.Identifier).getSpan();
  396. result = new MemberAccess(result, identifier);
  397. }
  398. }
  399. return result;
  400. }
  401. /**
  402. * Does not consume the closing parentheses.
  403. **/
  404. private static List<Expression> parseArguments(TokenStream stream) {
  405. stream.expect(TokenType.LeftParantheses);
  406. List<Expression> arguments = new ArrayList<Expression>();
  407. while (stream.hasMore() && !stream.match(TokenType.RightParantheses, false)) {
  408. arguments.add(parseExpression(stream));
  409. if (!stream.match(TokenType.RightParantheses, false)) stream.expect(TokenType.Comma);
  410. }
  411. return arguments;
  412. }
  413. public static void main(String[] args) {
  414. System.out.println(AstInterpreter.interpret(MagicScript.create("return message!=null&&message.length()>=3"), new MagicScriptContext(new HashMap<>())));
  415. ;
  416. }
  417. }