Fuel plugin to deploy Sensu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parser.rb 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. require_relative './ast'
  2. module Dentaku
  3. class Parser
  4. attr_reader :input, :output, :operations, :arities
  5. def initialize(tokens, options={})
  6. @input = tokens.dup
  7. @output = []
  8. @operations = options.fetch(:operations, [])
  9. @arities = options.fetch(:arities, [])
  10. end
  11. def get_args(count)
  12. Array.new(count) { output.pop }.reverse
  13. end
  14. def consume(count=2)
  15. operator = operations.pop
  16. output.push operator.new(*get_args(operator.arity || count))
  17. end
  18. def parse
  19. return AST::Nil.new if input.empty?
  20. while token = input.shift
  21. case token.category
  22. when :numeric
  23. output.push AST::Numeric.new(token)
  24. when :logical
  25. output.push AST::Logical.new(token)
  26. when :string
  27. output.push AST::String.new(token)
  28. when :identifier
  29. output.push AST::Identifier.new(token)
  30. when :operator, :comparator, :combinator
  31. op_class = operation(token)
  32. if op_class.right_associative?
  33. while operations.last && operations.last < AST::Operation && op_class.precedence < operations.last.precedence
  34. consume
  35. end
  36. operations.push op_class
  37. else
  38. while operations.last && operations.last < AST::Operation && op_class.precedence <= operations.last.precedence
  39. consume
  40. end
  41. operations.push op_class
  42. end
  43. when :null
  44. output.push AST::Nil.new
  45. when :function
  46. arities.push 0
  47. operations.push function(token)
  48. when :case
  49. case token.value
  50. when :open
  51. # special handling for case nesting: strip out inner case
  52. # statements and parse their AST segments recursively
  53. if operations.include?(AST::Case)
  54. last_case_close_index = nil
  55. first_nested_case_close_index = nil
  56. input.each_with_index do |token, index|
  57. first_nested_case_close_index = last_case_close_index
  58. if token.category == :case && token.value == :close
  59. last_case_close_index = index
  60. end
  61. end
  62. inner_case_inputs = input.slice!(0..first_nested_case_close_index)
  63. subparser = Parser.new(
  64. inner_case_inputs,
  65. operations: [AST::Case],
  66. arities: [0]
  67. )
  68. subparser.parse
  69. output.concat(subparser.output)
  70. else
  71. operations.push AST::Case
  72. arities.push(0)
  73. end
  74. when :close
  75. if operations[1] == AST::CaseThen
  76. while operations.last != AST::Case
  77. consume
  78. end
  79. operations.push(AST::CaseConditional)
  80. consume(2)
  81. arities[-1] += 1
  82. elsif operations[1] == AST::CaseElse
  83. while operations.last != AST::Case
  84. consume
  85. end
  86. arities[-1] += 1
  87. end
  88. unless operations.count == 1 && operations.last == AST::Case
  89. fail ParseError, "Unprocessed token #{ token.value }"
  90. end
  91. consume(arities.pop.succ)
  92. when :when
  93. if operations[1] == AST::CaseThen
  94. while ![AST::CaseWhen, AST::Case].include?(operations.last)
  95. consume
  96. end
  97. operations.push(AST::CaseConditional)
  98. consume(2)
  99. arities[-1] += 1
  100. elsif operations.last == AST::Case
  101. operations.push(AST::CaseSwitchVariable)
  102. consume
  103. end
  104. operations.push(AST::CaseWhen)
  105. when :then
  106. if operations[1] == AST::CaseWhen
  107. while ![AST::CaseThen, AST::Case].include?(operations.last)
  108. consume
  109. end
  110. end
  111. operations.push(AST::CaseThen)
  112. when :else
  113. if operations[1] == AST::CaseThen
  114. while operations.last != AST::Case
  115. consume
  116. end
  117. operations.push(AST::CaseConditional)
  118. consume(2)
  119. arities[-1] += 1
  120. end
  121. operations.push(AST::CaseElse)
  122. else
  123. fail ParseError, "Unknown case token #{ token.value }"
  124. end
  125. when :grouping
  126. case token.value
  127. when :open
  128. if input.first && input.first.value == :close
  129. input.shift
  130. consume(0)
  131. else
  132. operations.push AST::Grouping
  133. end
  134. when :close
  135. while operations.any? && operations.last != AST::Grouping
  136. consume
  137. end
  138. lparen = operations.pop
  139. fail ParseError, "Unbalanced parenthesis" unless lparen == AST::Grouping
  140. if operations.last && operations.last < AST::Function
  141. consume(arities.pop.succ)
  142. end
  143. when :comma
  144. arities[-1] += 1
  145. while operations.any? && operations.last != AST::Grouping
  146. consume
  147. end
  148. else
  149. fail ParseError, "Unknown grouping token #{ token.value }"
  150. end
  151. else
  152. fail ParseError, "Not implemented for tokens of category #{ token.category }"
  153. end
  154. end
  155. while operations.any?
  156. consume
  157. end
  158. unless output.count == 1
  159. fail ParseError, "Invalid statement"
  160. end
  161. output.first
  162. end
  163. def operation(token)
  164. {
  165. add: AST::Addition,
  166. subtract: AST::Subtraction,
  167. multiply: AST::Multiplication,
  168. divide: AST::Division,
  169. pow: AST::Exponentiation,
  170. negate: AST::Negation,
  171. mod: AST::Modulo,
  172. lt: AST::LessThan,
  173. gt: AST::GreaterThan,
  174. le: AST::LessThanOrEqual,
  175. ge: AST::GreaterThanOrEqual,
  176. ne: AST::NotEqual,
  177. eq: AST::Equal,
  178. and: AST::And,
  179. or: AST::Or,
  180. }.fetch(token.value)
  181. end
  182. def function(token)
  183. Dentaku::AST::Function.get(token.value)
  184. end
  185. end
  186. end