parser.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. define([
  2. './lexer'
  3. ], function (Lexer) {
  4. var NodeTypes = {
  5. MetricExpression: 1,
  6. MetricNode: 2,
  7. FunctionCall: 4,
  8. NumericLiteral: 5,
  9. StringLiteral: 6
  10. };
  11. function Node(type, value) {
  12. this.type = type;
  13. this.value = value;
  14. }
  15. function Parser(expression) {
  16. this.expression = expression;
  17. this.lexer = new Lexer(expression);
  18. this.state = "start";
  19. this.error = null;
  20. this.tokens = this.lexer.tokenize();
  21. this.index = 0;
  22. }
  23. Parser.Nodes = NodeTypes;
  24. Parser.prototype = {
  25. getAst: function () {
  26. return this.start();
  27. },
  28. start: function () {
  29. return this.functionCall() || this.metricExpression();
  30. },
  31. metricExpression: function() {
  32. if (!this.match('identifier')) {
  33. return null;
  34. }
  35. var node = {
  36. type: 'metric',
  37. segments: [{
  38. type: 'segment',
  39. value: this.tokens[this.index].value
  40. }]
  41. }
  42. this.index++;
  43. if (this.match('.')) {
  44. this.index++;
  45. var rest = this.metricExpression();
  46. if (!rest) {
  47. this.errorMark('Expected metric identifier');
  48. return null;
  49. }
  50. node.segments = node.segments.concat(rest.segments)
  51. }
  52. return node;
  53. },
  54. functionCall: function() {
  55. if (!this.match('identifier', '(')) {
  56. return null;
  57. }
  58. var node = {
  59. type: 'function',
  60. name: this.tokens[this.index].value,
  61. };
  62. this.index += 2;
  63. node.params = this.functionParameters();
  64. if (!this.match(')')) {
  65. this.errorMark('Expected closing paranthesis');
  66. return null;
  67. }
  68. this.index++;
  69. return node;
  70. },
  71. functionParameters: function () {
  72. if (this.match(')') || this.match('')) {
  73. return [];
  74. }
  75. var param =
  76. this.functionCall() ||
  77. this.metricExpression() ||
  78. this.numericLiteral() ||
  79. this.stringLiteral();
  80. if (!this.match(',')) {
  81. return [param];
  82. }
  83. this.index++;
  84. return [param].concat(this.functionParameters());
  85. },
  86. numericLiteral: function () {
  87. if (!this.match('number')) {
  88. return null;
  89. }
  90. this.index++;
  91. return {
  92. type: 'number',
  93. value: this.tokens[this.index-1].value
  94. };
  95. },
  96. stringLiteral: function () {
  97. if (!this.match('string')) {
  98. return null;
  99. }
  100. this.index++;
  101. return {
  102. type: 'string',
  103. value: this.tokens[this.index-1].value
  104. };
  105. },
  106. errorMark: function(text) {
  107. var currentToken = this.tokens[this.index];
  108. var type = currentToken ? currentToken.type : 'end of string';
  109. this.error = {
  110. text: text + " instead found " + type,
  111. pos: currentToken ? currentToken.pos : this.lexer.char
  112. };
  113. },
  114. matchToken: function(type, index) {
  115. var token = this.tokens[this.index + index];
  116. return (token === undefined && type === '') ||
  117. token && token.type === type;
  118. },
  119. match: function(token1, token2) {
  120. return this.matchToken(token1, 0) &&
  121. (!token2 || this.matchToken(token2, 1))
  122. },
  123. };
  124. return Parser;
  125. });