Skip to content

Instantly share code, notes, and snippets.

@oeN
Forked from nicolaracco/parser.rb
Last active August 29, 2015 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oeN/cf4c6a06c1a099be1eb9 to your computer and use it in GitHub Desktop.
Save oeN/cf4c6a06c1a099be1eb9 to your computer and use it in GitHub Desktop.
basic tokenizer and parser with an approach of Polish notation for calcs
require 'tokenizer'
class Parser
attr_reader :tokens, :prioritize
GRAMMAR = {
number: "insert_operand",
operator: "insert_operator",
open_bracket: "open_bracket",
close_bracket: "close_bracket"
}
def parse expression
tokenizer = Tokenizer.new expression
@tokens = []
@prioritize = false
while tokenizer.look_next_token
tokenizer.next_token
GRAMMAR.each do |t, action|
send( action, tokenizer.current_token ) if t == tokenizer.current_token.type
end
end
end
def get_result
operate
@tokens.first.value
end
def insert_operand token
@tokens << token
end
def insert_operator token
if prioritize
@tokens.insert @tokens.size - 1, token
else
@tokens.insert 0, token
end
end
def open_bracket token
@prioritize = true
end
def close_bracket token
@prioritize = false
end
def operate
number_stack = []
while @tokens.size != 1 || @tokens.first.type != :number
current_token = @tokens.pop
number_stack << current_token.value if current_token.type == :number
if current_token.type == :operator
first_value = number_stack.pop
second_value = number_stack.pop
if first_value && second_value
@tokens << Token.new( :number, first_value.send( current_token.value, second_value ) )
end
end
end
end
end
class Token
attr_reader :type, :value
def initialize type, value
@type, @value = type, value
end
end
require 'token'
class Tokenizer
OPERATORS = ['+', '-', '*', '/']
NUMBER_REGEXP = /^\d*(\.\d+)?$/
OPEN_BRACKETS = ['(']
CLOSE_BRACKETS = [')']
attr_reader :tokens, :cursor
def initialize expression
tokenize expression
@cursor = -1
end
def current_token
return nil if @cursor == -1
@tokens[cursor]
end
def next_token
@cursor += 1
current_token
end
def look_next_token
@tokens[cursor + 1]
end
private
def tokenize expression
@tokens = []
expression_to_parse = expression.strip
while expression_to_parse.size > 0
@tokens << read_next_token(expression_to_parse)
end
end
def read_next_token expression_to_parse
expression_to_parse.strip!
next_char = expression_to_parse.slice 0
if OPERATORS.include? next_char
read_next_operator_token expression_to_parse
elsif next_char =~ NUMBER_REGEXP
read_next_numeric_token expression_to_parse
elsif OPEN_BRACKETS.include? next_char
read_next_open_bracket_token expression_to_parse
elsif CLOSE_BRACKETS.include? next_char
read_next_close_bracket_token expression_to_parse
else
raise "Unknown token starting with '#{next_char}'"
end
end
def read_next_open_bracket_token expression_to_parse
Token.new :open_bracket, expression_to_parse.slice!(0)
end
def read_next_close_bracket_token expression_to_parse
Token.new :close_bracket, expression_to_parse.slice!(0)
end
def read_next_operator_token expression_to_parse
Token.new :operator, expression_to_parse.slice!(0)
end
def read_next_numeric_token expression_to_parse
numeric_value = expression_to_parse.slice! 0
next_char = expression_to_parse.slice 0
while next_char && "#{numeric_value}#{next_char}" =~ NUMBER_REGEXP
numeric_value << expression_to_parse.slice!(0)
end
Token.new :number, numeric_value.to_f
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment