Created
December 20, 2011 17:51
-
-
Save ptn/1502488 to your computer and use it in GitHub Desktop.
Poor Man's Forth
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
module Stacker | |
module Construct | |
class Construct | |
# Read extra data from the input, parse it and return the new position | |
# from where to continue parsing or nil if a grammar error is found. | |
def parse(input, pos) | |
pos | |
end | |
end | |
class Number < Construct | |
def recognize?(command) | |
number = command.to_i | |
if number == 0 | |
return false unless command =~ /0+$/ | |
end | |
@number = number | |
end | |
def operate(stack) | |
stack.push @number | |
end | |
end | |
class BinaryOperator < Construct | |
def initialize() | |
super | |
@operators = { | |
"ADD" => Proc.new { |x, y| x + y }, | |
"SUBSTRACT" => Proc.new { |x, y| x - y }, | |
"MULTIPLY" => Proc.new { |x, y| x * y }, | |
"DIVIDE" => Proc.new { |x, y| x / y }, | |
"MOD" => Proc.new { |x, y| x % y }, | |
"<" => Proc.new { |x, y| x < y }, | |
">" => Proc.new { |x, y| x > y }, | |
"=" => Proc.new { |x, y| x == y }, | |
} | |
end | |
def recognize?(command) | |
@operator = @operators[command] | |
end | |
def operate(stack) | |
param2 = stack.pop | |
param1 = stack.pop | |
result = @operator.call(param1, param2) | |
stack.push result | |
end | |
end | |
class UnaryOperator < Construct | |
def recognize?(command) | |
# No unary operators yet. | |
false | |
end | |
end | |
class ProcedureInterpreter | |
def initialize(calls) | |
@calls = calls | |
@stack = [] | |
end | |
def execute(param) | |
@stack.push param | |
@calls.each do |call| | |
call.operate(@stack) | |
end | |
@stack.first | |
end | |
end | |
# Should this be split in ProcedureCall and ProcedureDefinition ? | |
# Where to store all ProcedureDefinition objects so that ProcedureCall can | |
# search them if they are managed by an Interpreter and do not communicate | |
# directly? | |
class Procedure < Construct | |
def initialize() | |
super | |
@procedures = {} | |
@parser = Stacker::Parser::ProcedureParser.new | |
end | |
def recognize?(command) | |
split = command.split | |
if split.length == 1 | |
@proc = @procedures[command] | |
elsif split.length == 2 && split[0] == "PROCEDURE" | |
@name = split[1] | |
end | |
end | |
def parse(input, pos) | |
if @proc | |
pos | |
elsif @name | |
new_pos = input.index("/PROCEDURE") | |
return nil if new_pos.nil? | |
raw_body = input[pos..new_pos-1].strip | |
body = [] | |
@parser.parse(raw_body) { |construct| body << construct } | |
@procedures[@name] = ProcedureInterpreter.new(body) | |
new_pos += "/PROCEDURE\n".length | |
end | |
end | |
def operate(stack) | |
stack.push @proc.execute(stack.pop) if @proc | |
@name = @proc = nil | |
end | |
end | |
end | |
module Parser | |
class UnknownConstruct < Exception;end | |
class ParseError < Exception;end | |
class Parser | |
def initialize() | |
@constructs = [] | |
end | |
def parse(input) | |
input.concat("\n") unless input[-1] == "\n" | |
@input = input | |
@pos = 0 | |
while @pos < @input.length | |
puts "#{self.class}: #{@pos}" ############################## | |
command = read_line | |
parse_error = @constructs.each do |construct| | |
if construct.recognize? command | |
@pos = construct.parse(@input, @pos) | |
raise ParseError if @pos.nil? | |
yield construct if block_given? | |
break(nil) | |
end | |
end | |
raise UnknownConstruct, "#{self.class} can't parse construct '#{command}' at position #{@pos} in input '#{input}'" if parse_error | |
end | |
end | |
def add_construct(construct) | |
@constructs << construct | |
end | |
private | |
def read_line() | |
index = @input.index("\n", @pos) | |
command = @input[@pos..index-1] | |
@pos = index + 1 | |
command | |
end | |
end | |
class ProcedureParser < Parser | |
def initialize() | |
super | |
add_construct Construct::UnaryOperator.new | |
add_construct Construct::BinaryOperator.new | |
add_construct Construct::Number.new | |
end | |
end | |
# Should inherit from ProcedureParser | |
class FortranParser < Parser | |
def initialize() | |
super | |
add_construct Construct::Procedure.new | |
add_construct Construct::UnaryOperator.new | |
add_construct Construct::BinaryOperator.new | |
add_construct Construct::Number.new | |
end | |
end | |
end | |
class Interpreter | |
attr_reader :stack | |
def initialize(parser) | |
@stack = [] | |
@parser = parser | |
end | |
def execute(commands) | |
@parser.parse(commands) { |construct| construct.operate(@stack) } | |
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
module Stacker | |
module Construct | |
class Construct | |
# Read extra data from the input, parse it and return the new position | |
# from where to continue parsing or nil if a grammar error is found. | |
def parse(input, pos) | |
pos | |
end | |
end | |
class Number < Construct | |
def recognize?(command) | |
number = command.to_i | |
if number == 0 | |
return false unless command =~ /0+$/ | |
end | |
@number = number | |
end | |
def operate(stack) | |
stack.push @number | |
end | |
end | |
class BinaryOperator < Construct | |
def initialize() | |
super | |
@operators = { | |
"ADD" => Proc.new { |x, y| x + y }, | |
"SUBSTRACT" => Proc.new { |x, y| x - y }, | |
"MULTIPLY" => Proc.new { |x, y| x * y }, | |
"DIVIDE" => Proc.new { |x, y| x / y }, | |
"MOD" => Proc.new { |x, y| x % y }, | |
"<" => Proc.new { |x, y| x < y }, | |
">" => Proc.new { |x, y| x > y }, | |
"=" => Proc.new { |x, y| x == y }, | |
} | |
end | |
def recognize?(command) | |
@operator = @operators[command] | |
end | |
def operate(stack) | |
param2 = stack.pop | |
param1 = stack.pop | |
result = @operator.call(param1, param2) | |
stack.push result | |
end | |
end | |
class UnaryOperator < Construct | |
def recognize?(command) | |
# No unary operators yet. | |
false | |
end | |
end | |
class ProcedureInterpreter | |
def initialize(calls) | |
@calls = calls | |
@stack = [] | |
end | |
def execute(param) | |
@stack.push param | |
@calls.each do |call| | |
call.operate(@stack) | |
end | |
@stack.first | |
end | |
end | |
# Should this be split in ProcedureCall and ProcedureDefinition ? | |
# Where to store all ProcedureDefinition objects so that ProcedureCall can | |
# search them if they are managed by an Interpreter and do not communicate | |
# directly? | |
class Procedure < Construct | |
def initialize() | |
super | |
@procedures = {} | |
@parser = Stacker::Parser::ProcedureParser.new | |
end | |
def recognize?(command) | |
split = command.split | |
if split.length == 1 | |
@proc = @procedures[command] | |
elsif split.length == 2 && split[0] == "PROCEDURE" | |
@name = split[1] | |
end | |
end | |
def parse(input, pos) | |
if @proc | |
pos | |
elsif @name | |
new_pos = input.index("/PROCEDURE") | |
return nil if new_pos.nil? | |
raw_body = input[pos..new_pos-1].strip | |
body = [] | |
@parser.parse(raw_body) { |construct| body << construct } | |
@procedures[@name] = ProcedureInterpreter.new(body) | |
new_pos += "/PROCEDURE\n".length | |
end | |
end | |
def operate(stack) | |
stack.push @proc.execute(stack.pop) if @proc | |
@name = @proc = nil | |
end | |
end | |
end | |
module Parser | |
class UnknownConstruct < Exception;end | |
class ParseError < Exception;end | |
class Parser | |
def initialize() | |
@constructs = [] | |
end | |
def parse(input) | |
input.concat("\n") unless input[-1] == "\n" | |
@input = input | |
@pos = 0 | |
while @pos < @input.length | |
puts "#{self.class}: #{@pos}" ############################## | |
command = read_line | |
parse_error = @constructs.each do |construct| | |
if construct.recognize? command | |
@pos = construct.parse(@input, @pos) | |
raise ParseError if @pos.nil? | |
yield construct if block_given? | |
break(nil) | |
end | |
end | |
raise UnknownConstruct, "#{self.class} can't parse construct '#{command}' at position #{@pos} in input '#{input}'" if parse_error | |
end | |
end | |
def add_construct(construct) | |
@constructs << construct | |
end | |
private | |
def read_line() | |
index = @input.index("\n", @pos) | |
command = @input[@pos..index-1] | |
@pos = index + 1 | |
command | |
end | |
end | |
class ProcedureParser < Parser | |
def initialize() | |
super | |
add_construct Construct::UnaryOperator.new | |
add_construct Construct::BinaryOperator.new | |
add_construct Construct::Number.new | |
end | |
end | |
# Should inherit from ProcedureParser | |
class FortranParser < Parser | |
def initialize() | |
super | |
add_construct Construct::Procedure.new | |
add_construct Construct::UnaryOperator.new | |
add_construct Construct::BinaryOperator.new | |
add_construct Construct::Number.new | |
end | |
end | |
end | |
class Interpreter | |
attr_reader :stack | |
def initialize(parser) | |
@stack = [] | |
@parser = parser | |
end | |
def execute(commands) | |
@parser.parse(commands) { |construct| construct.operate(@stack) } | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment