Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
164 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,108 +1,132 @@ | ||
require 'parser' | ||
require 'evaluator' | ||
|
||
# It's a simple arithmetical evaluator. It's able to parse expressions like these: | ||
# * ( a = 3 * 2 ) - ( 24 + a ), | ||
# * 3 * (4 - 2) + 5*(4/2)/(3-2). | ||
# | ||
# It realizes the following grammar: | ||
# | ||
# non terminal symbols = { SCOPE, EXP, TERM, POW, FACTOR, NUMBER } | ||
# terminal symbols = any number, the charachters " , . + - * / ^ = [ ] ( ) ' " | ||
# | ||
# an id is an unlimited string composed of every charachter "a".."Z" and the "_" | ||
# | ||
# P = { | ||
# SCOPE ::= EXP | ||
# EXP ::= EXP + TERM | ||
# EXP ::= EXP - TERM | ||
# TERM ::= POW | ||
# TERM ::= TERM * FACTOR | ||
# TERM ::= TERM / FACTOR | ||
# FACTOR ::= any sequence of 0,1,2,3,4,5,6,7,8,9 | ||
# FACTOR ::= an id | ||
# FACTOR ::= an id = | ||
# FACTOR ::= ( EXP ) | ||
# } | ||
# | ||
class Ariteval < Parser | ||
|
||
def initialize | ||
super | ||
@evaluator = Evaluator.new | ||
end | ||
def initialize | ||
super | ||
@evaluator = Evaluator.new | ||
end | ||
|
||
def evaluate(source) | ||
parse(source).accept(@evaluator) | ||
@evaluator.result | ||
end | ||
def evaluate(source) | ||
parse(source).accept(@evaluator) | ||
@evaluator.result | ||
end | ||
|
||
# tokens definitions | ||
# tokens definitions | ||
|
||
numbers = ("0".."9").to_a.join("|") | ||
token :number, "(#{numbers})+ *" | ||
numbers = ("0".."9").to_a.join("|") | ||
token :number, "(#{numbers})+ *" | ||
|
||
token :plus, '\+ *' | ||
token :plus, '\+ *' | ||
|
||
token :minus, '- *' | ||
token :minus, '- *' | ||
|
||
token :mul, '\* *' | ||
token :mul, '\* *' | ||
|
||
token :div, '/ *' | ||
token :div, '/ *' | ||
|
||
token :"(", '\( *' | ||
token "(".to_sym, '\( *' | ||
|
||
token :")", '\) *' | ||
token ")".to_sym, '\) *' | ||
|
||
identifiers = (("a".."z").to_a + ("A".."Z").to_a).join("|") | ||
token :ident, "(#{identifiers}) *" | ||
identifiers = (("a".."z").to_a + ("A".."Z").to_a).join("|") | ||
token :ident, "(#{identifiers}) *" | ||
|
||
token :assign, "= *" | ||
token :assign, "= *" | ||
|
||
# production definitions | ||
# production definitions | ||
|
||
lookahead(true) | ||
lookahead(true) | ||
|
||
scope :exp | ||
|
||
production(:exp,:recursive) do |prod| | ||
prod.default { |scanner,parser| parser.parse_term } | ||
|
||
prod.token(:plus) do |term_seq,scanner,parser| | ||
scanner.next | ||
next_term = parser.parse_term | ||
PlusExp.new(term_seq,next_term) | ||
end | ||
|
||
prod.token(:minus) do |term_seq,scanner,parser| | ||
scanner.next | ||
next_term = parser.parse_term | ||
MinusExp.new(term_seq,next_term) | ||
end | ||
scope :exp | ||
|
||
production(:exp,:recursive) do |prod| | ||
prod.default { |scanner,parser| parser.parse_term } | ||
|
||
prod.token(:plus) do |term_seq,scanner,parser| | ||
scanner.next | ||
next_term = parser.parse_term | ||
PlusExp.new(term_seq,next_term) | ||
end | ||
|
||
prod.token(:minus) do |term_seq,scanner,parser| | ||
scanner.next | ||
next_term = parser.parse_term | ||
MinusExp.new(term_seq,next_term) | ||
end | ||
|
||
end | ||
end | ||
|
||
production(:term,:recursive) do |prod| | ||
prod.default { |scanner,parser| parser.parse_factor } | ||
production(:term,:recursive) do |prod| | ||
prod.default { |scanner,parser| parser.parse_factor } | ||
|
||
prod.token(:mul) do |factor_seq,scanner,parser| | ||
scanner.next | ||
next_factor = parser.parse_factor | ||
MulExp.new(factor_seq,next_factor) | ||
end | ||
prod.token(:mul) do |factor_seq,scanner,parser| | ||
scanner.next | ||
next_factor = parser.parse_factor | ||
MulExp.new(factor_seq,next_factor) | ||
end | ||
|
||
prod.token(:div) do |factor_seq,scanner,parser| | ||
scanner.next | ||
next_factor = parser.parse_factor | ||
DivExp.new(factor_seq,next_factor) | ||
end | ||
end | ||
prod.token(:div) do |factor_seq,scanner,parser| | ||
scanner.next | ||
next_factor = parser.parse_factor | ||
DivExp.new(factor_seq,next_factor) | ||
end | ||
end | ||
|
||
production(:factor,:single) do |prod| | ||
production(:factor,:single) do |prod| | ||
|
||
prod.token(:number) do |result,scanner| | ||
current = scanner.current | ||
scanner.next | ||
NumExp.new(current.value.to_i) | ||
end | ||
|
||
prod.token(:"(") do |result,scanner,parser| | ||
scanner.next | ||
result = parser.parse_exp | ||
parser.raise "Every '(' must be followed by a ')'" unless scanner.current == :")" | ||
scanner.next | ||
result | ||
end | ||
|
||
prod.token(:ident,:assign) do |result,scanner,parser| | ||
name = scanner.current.to_s.strip | ||
scanner.next | ||
scanner.next | ||
AssignIdentExp.new(name,parser.parse_exp) | ||
end | ||
|
||
prod.token(:ident) do |result,scanner,parser| | ||
result = IdentExp.new(scanner.current.to_s.strip) | ||
scanner.next | ||
result | ||
end | ||
|
||
end | ||
prod.token(:number) do |result,scanner| | ||
current = scanner.current | ||
scanner.next | ||
NumExp.new(current.value.to_i) | ||
end | ||
|
||
prod.token("(".to_sym) do |result,scanner,parser| | ||
scanner.next | ||
result = parser.parse_exp | ||
parser.raise "Every '(' must be followed by a ')'" unless scanner.current == ")".to_sym | ||
scanner.next | ||
result | ||
end | ||
|
||
prod.token(:ident,:assign) do |result,scanner,parser| | ||
name = scanner.current.to_s.strip | ||
scanner.next | ||
scanner.next | ||
AssignIdentExp.new(name,parser.parse_exp) | ||
end | ||
|
||
prod.token(:ident) do |result,scanner,parser| | ||
result = IdentExp.new(scanner.current.to_s.strip) | ||
scanner.next | ||
result | ||
end | ||
|
||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,61 @@ | ||
|
||
# It's a symple visitor for the abstract syntax tree created with the nodes in exp.rb. | ||
# It evaluates the expressions in the obvious way. | ||
class Evaluator | ||
|
||
attr_reader :ident_table | ||
|
||
def initialize | ||
@result = 0 | ||
@ident_table = {} | ||
end | ||
|
||
def visit_plus_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left + right | ||
end | ||
|
||
def visit_minus_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left - right | ||
end | ||
|
||
def visit_mul_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left * right | ||
end | ||
|
||
def visit_div_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left / right | ||
end | ||
|
||
def visit_num_exp(exp) | ||
@result = exp.value | ||
end | ||
|
||
def visit_assign_ident_exp(exp) | ||
exp.value.accept(self) | ||
ident_table[exp.name] = @result | ||
end | ||
|
||
def visit_ident_exp(exp) | ||
@result = ident_table[exp.value] | ||
end | ||
|
||
def result | ||
result = @result | ||
@result = 0 | ||
return result | ||
end | ||
|
||
private | ||
|
||
def left_and_right(exp) | ||
exp.left.accept(self) | ||
left = @result | ||
attr_reader :ident_table | ||
|
||
def initialize | ||
@result = 0 | ||
@ident_table = {} | ||
end | ||
|
||
def visit_plus_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left + right | ||
end | ||
|
||
def visit_minus_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left - right | ||
end | ||
|
||
def visit_mul_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left * right | ||
end | ||
|
||
def visit_div_exp(exp) | ||
left,right = left_and_right(exp) | ||
@result = left / right | ||
end | ||
|
||
def visit_num_exp(exp) | ||
@result = exp.value | ||
end | ||
|
||
def visit_assign_ident_exp(exp) | ||
exp.value.accept(self) | ||
ident_table[exp.name] = @result | ||
end | ||
|
||
def visit_ident_exp(exp) | ||
@result = ident_table[exp.value] | ||
end | ||
|
||
def result | ||
result = @result | ||
@result = 0 | ||
return result | ||
end | ||
|
||
private | ||
|
||
def left_and_right(exp) | ||
exp.left.accept(self) | ||
left = @result | ||
|
||
exp.right.accept(self) | ||
right = @result | ||
[left,right] | ||
end | ||
exp.right.accept(self) | ||
right = @result | ||
[left,right] | ||
end | ||
end |