implement interpreter and demo + bug fixes
- implement interpreter and demo - replace StandardError with appropriate subclasses - implement division - rewrite Polynomial.simplify()
This commit is contained in:
@@ -118,6 +118,9 @@ module RubyAlgebra
|
||||
end
|
||||
end
|
||||
|
||||
class TokenizerError < StandardError; end
|
||||
class ParserError < StandardError; end
|
||||
|
||||
# Класс для разбора выражений на токены
|
||||
class Tokenizer
|
||||
attr_reader :lookahead
|
||||
@@ -135,22 +138,16 @@ module RubyAlgebra
|
||||
end
|
||||
|
||||
def _next_token
|
||||
puts '_next_token'
|
||||
|
||||
loop do
|
||||
return EndToken.new unless @i < @expr.length
|
||||
puts "i = #{@i}"
|
||||
c = @expr[@i]
|
||||
puts "c = #{c}"
|
||||
if c.match?(/\p{Alpha}/)
|
||||
puts 'letter'
|
||||
j = @i + 1
|
||||
j += 1 while j < @expr.length && @expr[j].match?(/\p{Alnum}/)
|
||||
token = IdentifierToken.new(@expr[@i...j])
|
||||
@i = j
|
||||
return token
|
||||
elsif c.match(%r{[+\-*/^():,=]})
|
||||
puts 'operator'
|
||||
token = nil
|
||||
case c
|
||||
when '+'
|
||||
@@ -179,21 +176,17 @@ module RubyAlgebra
|
||||
end
|
||||
return token
|
||||
elsif c.match?(/\p{Digit}|\./)
|
||||
puts 'digit'
|
||||
j = @i + 1
|
||||
j += 1 while j < @expr.length && @expr[j].match?(/\p{Digit}|\./)
|
||||
s = @expr[@i...j]
|
||||
@i = j
|
||||
puts "ssss: #{s}"
|
||||
fl = s.count '.'
|
||||
raise StandardError, 'malformed number' if fl > 1
|
||||
raise TokenizerError, 'malformed number' if fl > 1
|
||||
return fl.zero? ? NumberToken.new(s.to_i) : NumberToken.new(s.to_f)
|
||||
elsif c == ' '
|
||||
puts 'whitespace'
|
||||
@i += 1
|
||||
else
|
||||
puts 'unknown'
|
||||
@i += 1
|
||||
raise TokenizerError, "unrecognized character '#{c}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -217,7 +210,7 @@ module RubyAlgebra
|
||||
tokenizer.next_token
|
||||
n = tokenizer.next_token
|
||||
unless n.type == :paren && n.closing == false
|
||||
raise StandardError, "unexpected token #{n.type}, expected ("
|
||||
raise ParserError, "unexpected token #{n.type}, expected ("
|
||||
end
|
||||
n = tokenizer.lookahead
|
||||
if n.type == :id && !n.variable?
|
||||
@@ -231,23 +224,23 @@ module RubyAlgebra
|
||||
tokenizer.next_token
|
||||
n = tokenizer.next_token
|
||||
unless n.type == :id && n.variable?
|
||||
raise StandardError, "unexpected token #{n.type}, expected variable"
|
||||
raise ParserError, "unexpected token #{n.type}, expected variable"
|
||||
end
|
||||
diff_variables << n.symbol
|
||||
end
|
||||
n = tokenizer.next_token
|
||||
unless n.type == :paren && n.closing == true
|
||||
raise StandardError, "unexpected token #{n.type}, expected )"
|
||||
raise ParserError, "unexpected token #{n.type}, expected )"
|
||||
end
|
||||
unless tokenizer.next_token.type == :end
|
||||
raise StandardError, "unexpected token at the end"
|
||||
raise ParserError, "unexpected token at the end"
|
||||
end
|
||||
return AssignmentCommand.new(left_hand_side, :diff, target, diff_variables)
|
||||
elsif operand1.symbol == 'Subs'
|
||||
tokenizer.next_token
|
||||
n = tokenizer.next_token
|
||||
unless n.type == :paren && n.closing == false
|
||||
raise StandardError, "unexpected token #{n.type}, expected ("
|
||||
raise ParserError, "unexpected token #{n.type}, expected ("
|
||||
end
|
||||
n = tokenizer.lookahead
|
||||
if n.type == :id && !n.variable?
|
||||
@@ -261,35 +254,35 @@ module RubyAlgebra
|
||||
tokenizer.next_token
|
||||
n = tokenizer.next_token
|
||||
unless n.type == :id && n.variable?
|
||||
raise StandardError, "unexpected token #{n.type}, expected variable"
|
||||
raise ParserError, "unexpected token #{n.type}, expected variable"
|
||||
end
|
||||
variable = n.symbol
|
||||
unless tokenizer.next_token.type == :equals
|
||||
raise StandardError, "unexpected token #{n.type}, expected ="
|
||||
raise ParserError, "unexpected token #{n.type}, expected ="
|
||||
end
|
||||
n = tokenizer.next_token
|
||||
unless n.type == :num
|
||||
raise StandardError, "unexpected token #{n.type}, expected number"
|
||||
raise ParserError, "unexpected token #{n.type}, expected number"
|
||||
end
|
||||
substitutions[variable] = n.value
|
||||
end
|
||||
n = tokenizer.next_token
|
||||
unless n.type == :paren && n.closing == true
|
||||
raise StandardError, "unexpected token #{n.type}, expected )"
|
||||
raise ParserError, "unexpected token #{n.type}, expected )"
|
||||
end
|
||||
unless tokenizer.next_token.type == :end
|
||||
raise StandardError, "unexpected token at the end"
|
||||
raise ParserError, "unexpected token at the end"
|
||||
end
|
||||
return AssignmentCommand.new(left_hand_side, :subs, target, substitutions)
|
||||
else
|
||||
tokenizer.next_token
|
||||
operator = tokenizer.next_token
|
||||
raise StandardError, "unexpected token #{operator.type}, expected +, -, * or /" unless [:plus, :minus, :mult, :div].include?(operator.type)
|
||||
raise ParserError, "unexpected token #{operator.type}, expected +, -, * or /" unless [:plus, :minus, :mult, :div].include?(operator.type)
|
||||
|
||||
operand2 = tokenizer.next_token
|
||||
unless ([:plus, :minus, :mult].include?(operator.type) && operand2.type == :id && !operand2.variable?) ||
|
||||
([:mult, :div].include?(operator.type) && operand2.type == :num)
|
||||
raise StandardError, 'unsupported operation or invalid syntax'
|
||||
raise ParserError, 'unsupported operation or invalid syntax'
|
||||
end
|
||||
if operand2.type == :num
|
||||
if operator.type == :mult
|
||||
@@ -315,49 +308,52 @@ module RubyAlgebra
|
||||
n = tokenizer.next_token
|
||||
second_lhs = tokenizer.next_token
|
||||
unless second_lhs.type == :id && !second_lhs.variable?
|
||||
raise StandardError, "unexpected token #{second_lhs.type}, expected polynomial variable"
|
||||
raise ParserError, "unexpected token #{second_lhs.type}, expected polynomial variable"
|
||||
end
|
||||
unless tokenizer.next_token.type == :assign
|
||||
raise StandardError, "unexpected token, expected :="
|
||||
raise ParserError, "unexpected token, expected :="
|
||||
end
|
||||
operand1 = tokenizer.next_token
|
||||
unless operand1.type == :id && !operand1.variable?
|
||||
raise StandardError, "unexpected token #{operand1.type}, expected polynomial variable"
|
||||
raise ParserError, "unexpected token #{operand1.type}, expected polynomial variable"
|
||||
end
|
||||
unless tokenizer.next_token.type == :div
|
||||
raise StandardError, "unexpected token, expected /"
|
||||
raise ParserError, "unexpected token, expected /"
|
||||
end
|
||||
operand2 = tokenizer.next_token
|
||||
unless operand2.type == :id && !operand2.variable?
|
||||
raise StandardError, "unexpected token #{operand2.type}, expected polynomial variable"
|
||||
raise ParserError, "unexpected token #{operand2.type}, expected polynomial variable"
|
||||
end
|
||||
return AssignmentCommand.new([left_hand_side, second_lhs.symbol], :div, operand1.symbol)
|
||||
return AssignmentCommand.new([left_hand_side, second_lhs.symbol], :div, operand1.symbol, operand2.symbol)
|
||||
elsif tokenizer.lookahead.type == :end
|
||||
return DisplayCommand.new(left_hand_side)
|
||||
else
|
||||
raise StandardError, "unexpected token #{n.type}, expected ':=' or ','"
|
||||
raise ParserError, "unexpected token #{n.type}, expected ':=' or ','"
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_polynomial(tokenizer)
|
||||
puts "parse_polynomial <lookahead #{tokenizer.lookahead.type}>"
|
||||
terms = [parse_term(tokenizer)]
|
||||
first_term_negative = false
|
||||
if tokenizer.lookahead.type == :plus || tokenizer.lookahead.type == :minus
|
||||
first_term_negative = tokenizer.lookahead.type == :minus
|
||||
tokenizer.next_token
|
||||
end
|
||||
first_term = parse_term(tokenizer)
|
||||
first_term *= -1 if first_term_negative
|
||||
terms = [first_term]
|
||||
while [:plus, :minus].include?(tokenizer.lookahead.type)
|
||||
sign = tokenizer.next_token.type == :plus ? +1 : -1
|
||||
terms << parse_term(tokenizer) * sign
|
||||
end
|
||||
puts 'parse_polynomial end'
|
||||
Polynomial.new(terms)
|
||||
end
|
||||
|
||||
def self.parse_term(tokenizer)
|
||||
puts "parse_term <lookahead #{tokenizer.lookahead.type}>"
|
||||
n = tokenizer.lookahead
|
||||
unless n.type == :id && n.variable? || n.type == :num
|
||||
raise StandardError, "unexpected token #{n.type}, expected number or variable"
|
||||
raise ParserError, "unexpected token #{n.type}, expected number or variable"
|
||||
end
|
||||
if n.type == :num
|
||||
puts "eat coeff"
|
||||
tokenizer.next_token
|
||||
coeff = n.value
|
||||
variables = {}
|
||||
@@ -368,44 +364,34 @@ module RubyAlgebra
|
||||
end
|
||||
loop do
|
||||
n = tokenizer.lookahead
|
||||
puts "<lookahead #{n.type}>"
|
||||
unless n.type == :mult || n.type == :id && n.variable?
|
||||
break
|
||||
end
|
||||
v = parse_variable_factor(tokenizer)
|
||||
variables[v[0]] = v[1]
|
||||
end
|
||||
puts 'parse_term end'
|
||||
pp [coeff, variables]
|
||||
Term.new(coeff, variables)
|
||||
end
|
||||
|
||||
def self.parse_variable_factor(tokenizer)
|
||||
puts "parse_term <lookahead #{tokenizer.lookahead.type}>"
|
||||
n = tokenizer.lookahead
|
||||
tokenizer.next_token
|
||||
puts 'eat variable'
|
||||
if n.type == :mult
|
||||
n = tokenizer.next_token
|
||||
puts 'eat *'
|
||||
end
|
||||
unless n.type == :id && n.variable?
|
||||
raise StandardError, "unexpected token #{n.type}, expected variable"
|
||||
raise ParserError, "unexpected token #{n.type}, expected variable"
|
||||
end
|
||||
variable = n.symbol
|
||||
if tokenizer.lookahead.type == :pow
|
||||
tokenizer.next_token
|
||||
puts 'eat ^'
|
||||
unless tokenizer.lookahead.type == :num
|
||||
raise StandardError, "unexpected token #{n.type}, expected number"
|
||||
raise ParserError, "unexpected token #{n.type}, expected number"
|
||||
end
|
||||
power = tokenizer.next_token.value
|
||||
puts 'eat exponent'
|
||||
else
|
||||
power = 1
|
||||
end
|
||||
puts 'parse_variable_factor end'
|
||||
pp [variable, power]
|
||||
[variable, power]
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user