From ca315724ff09597cd938314fa9b1a3a4d6dfdb3a Mon Sep 17 00:00:00 2001 From: Gwenhael Le Moine Date: Wed, 10 Nov 2021 11:01:26 +0100 Subject: [PATCH] add runner and dictionary --- lib/dictionary.rb | 24 ++++++++++++++++++++++++ lib/parser.rb | 10 +--------- lib/runner.rb | 26 ++++++++++++++++++++++++++ repl.rb | 37 +++++++++++++------------------------ spec/parser_spec.rb | 9 ++++++++- 5 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 lib/dictionary.rb create mode 100644 lib/runner.rb diff --git a/lib/dictionary.rb b/lib/dictionary.rb new file mode 100644 index 0000000..85eed64 --- /dev/null +++ b/lib/dictionary.rb @@ -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 diff --git a/lib/parser.rb b/lib/parser.rb index c3242e2..a328899 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -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 diff --git a/lib/runner.rb b/lib/runner.rb new file mode 100644 index 0000000..710437c --- /dev/null +++ b/lib/runner.rb @@ -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 diff --git a/repl.rb b/repl.rb index 8040d88..bf1878e 100644 --- a/repl.rb +++ b/repl.rb @@ -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 diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index abd48a9..30420aa 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -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