add runner and dictionary

This commit is contained in:
Gwenhael Le Moine 2021-11-10 11:01:26 +01:00
parent 94919db7a9
commit ca315724ff
No known key found for this signature in database
GPG key ID: FDFE3669426707A7
5 changed files with 72 additions and 34 deletions

24
lib/dictionary.rb Normal file
View file

@ -0,0 +1,24 @@
# coding: utf-8
module Rpn
class Dictionary
def initialize
@parser = Parser.new
@words = {
'+' => proc { |stack|
stack + @parser.parse_input( (stack.pop[:value] + stack.pop[:value]).to_s )
},
}
end
def add( word )
@words[ word[:name] ] = word[:value]
end
def lookup( name )
@words[ name ] if @words.include?( name )
end
# TODO: alias
end
end

View file

@ -19,6 +19,7 @@ module Rpn
closed_programs = 0
string_delimiters = 0
regrouping = false
regrouped_input = []
splitted_input.each do |elt|
# TODO: handle buried-in-elt « and » (surround by ' ' and re-split)
@ -64,15 +65,6 @@ module Rpn
end
end
if parsed_entry[:type] == :word
if false
# TODO: run word if known
else
parsed_entry[:type] = :name
parsed_entry[:value] = "'#{parsed_entry[:value]}'" if parsed_entry[:value][0] != "'"
end
end
if parsed_entry[:type] == :numeric
i = parsed_entry[:value].to_i
f = parsed_entry[:value].to_f

26
lib/runner.rb Normal file
View file

@ -0,0 +1,26 @@
# coding: utf-8
module Rpn
class Runner
def initialize; end
def run_input( stack, dictionary, input )
input.each do |elt|
case elt[:type]
when :word
command = dictionary.lookup( elt[:value] )
if command.nil?
stack << { type: :name, value: "'#{elt[:value]}'" }
else
stack = command.call( stack )
end
else
stack << elt
end
end
[stack, dictionary]
end
end
end

37
repl.rb
View file

@ -1,15 +1,19 @@
# coding: utf-8
# frozen_string_literal: true
require "readline"
require 'readline'
require "./lib/parser.rb"
require './lib/dictionary'
require './lib/parser'
require './lib/runner'
module Rpn
class Repl
def initialize
@parser = Parser.new
@stack = []
@dictionary = Dictionary.new
@parser = Parser.new
@runner = Runner.new
end
def run
@ -21,39 +25,24 @@ module Rpn
Readline::HISTORY.grep(/^#{Regexp.escape(s)}/)
end
end
Readline.completion_append_character = " "
Readline.completion_append_character = ' '
loop do
input = Readline.readline( "", true )
break if input.nil? || input == "exit"
input = Readline.readline( ' ', true )
break if input.nil? || input == 'exit'
# Remove blank lines from history
Readline::HISTORY.pop if input.empty?
process_input( input )
@stack, @dictionary = @runner.run_input( @stack, @dictionary,
@parser.parse_input( input ) )
print_stack
end
end
def process_input( input )
@parser.parse_input( input ).each do |elt|
@stack << elt # TODO: (parse and) evaluate elt if needed
end
end
def format_element( elt )
pp elt
# case elt[:type]
# when :program
# "« #{elt[:value]} »"
# when :string
# "\"#{elt[:value]}\""
# when :name
# "'#{elt[:value]}'"
# else
elt[:value]
# end
elt[:value]
end
def print_stack

View file

@ -13,7 +13,7 @@ class TestParser < Test::Unit::TestCase
def test_word
result = Rpn::Parser.new.parse_input( 'dup' )
assert_equal [{ value: "'dup'", type: :name }], result
assert_equal [{ value: 'dup', type: :word }], result
end
def test_string
@ -63,6 +63,13 @@ class TestParser < Test::Unit::TestCase
assert_equal [{ value: 2, type: :numeric }, { value: 3, type: :numeric }], result
end
def test_number_number_word
result = Rpn::Parser.new.parse_input( '2 3 +' )
assert_equal [{ value: 2, type: :numeric },
{ value: 3, type: :numeric },
{ value: '+', type: :word }], result
end
def test_number_string
result = Rpn::Parser.new.parse_input( '4 "test"' )
assert_equal [{ value: 4, type: :numeric }, { value: '"test"', type: :string }], result