[WIP] refactoring in classes, parsing

This commit is contained in:
Gwenhael Le Moine 2021-11-09 16:50:01 +01:00
parent edccbf1bda
commit a4cfed2cc4
No known key found for this signature in database
GPG key ID: FDFE3669426707A7
3 changed files with 168 additions and 133 deletions

View file

@ -1,62 +1,68 @@
# coding: utf-8
class String
def numeric?
Float(self) != nil rescue false
end
end
module Rpn
class Parser
def initialize; end
def parse_input( input )
splitted_input = input.split(" ")
parsed_tree = []
regrouping = false
splitted_input.each do |elt|
next if elt.length == 1 && elt[0] == "»"
parsed_entry = { "value" => elt }
if regrouping
parsed_entry = parsed_tree.pop
parsed_entry["value"] = "#{parsed_entry["value"]} #{elt}".strip
else
parsed_entry["type"] = case elt[0]
when "«"
"PROGRAM"
when "\""
"STRING"
when "'"
"NAME"
else
if elt.numeric?
"NUMBER"
else
"WORD" # TODO: if word isn"t known then it"s a NAME
end
end
parsed_entry["value"] = elt[1..] if %W[PROGRAM STRING NAME].include?( parsed_entry["type"] )
def numeric?( elt )
!Float(elt).nil?
rescue ArgumentError
false
end
regrouping = ( (parsed_entry["type"] == "NAME" && elt[-1] != "'") ||
(parsed_entry["type"] == "STRING" && elt[-1] != "\"") ||
(parsed_entry["type"] == "PROGRAM" && elt[-1] != "»") )
def parse_input( input )
splitted_input = input.split(' ')
parsed_tree = []
parsed_entry["value"] = if ( (parsed_entry["type"] == "NAME" && elt[-1] == "'") ||
(parsed_entry["type"] == "STRING" && elt[-1] == "\"") ||
(parsed_entry["type"] == "PROGRAM" && elt[-1] == "»") )
(parsed_entry["value"][..-2]).strip
elsif parsed_entry["type"] == "NUMBER"
parsed_entry["value"].to_f
elsif parsed_entry["type"] == "WORD"
parsed_entry["value"]
else
parsed_entry["value"]
end
regrouping = false
splitted_input.each do |elt|
parsed_entry = { value: elt }
parsed_tree << parsed_entry
if regrouping
parsed_entry = parsed_tree.pop
parsed_entry[:value] = "#{parsed_entry[:value]} #{elt}".strip
else
parsed_entry[:type] = case elt[0]
when '«'
:program
when '"'
:string
when "'"
:name # TODO: check for forbidden space
else
if numeric?( elt )
:numeric
else
:word
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
# parsed_entry[:value] = elt[1..] if [:program, :string, :name].include?( parsed_entry[:type] )
end
regrouping = ( (parsed_entry[:type] == :string && elt.size == 1 && elt[-1] != '"') ||
(parsed_entry[:type] == :program && elt[-1] != '»') )
if parsed_entry[:type] == :numeric
i = parsed_entry[:value].to_i
f = parsed_entry[:value].to_f
parsed_entry[:value] = i == f ? i : f
end
parsed_tree << parsed_entry
end
parsed_tree
end
end
parsed_tree
end

100
repl.rb
View file

@ -5,49 +5,65 @@ require "readline"
require "./lib/parser.rb"
def run_REPL( stack )
Readline.completion_proc = proc do |s|
directory_list = Dir.glob("#{s}*")
if directory_list.positive?
directory_list
else
Readline::HISTORY.grep(/^#{Regexp.escape(s)}/)
module Rpn
class Repl
def initialize
@parser = Parser.new
@stack = []
end
def run
Readline.completion_proc = proc do |s|
directory_list = Dir.glob("#{s}*")
if directory_list.positive?
directory_list
else
Readline::HISTORY.grep(/^#{Regexp.escape(s)}/)
end
end
Readline.completion_append_character = " "
loop do
input = Readline.readline( "", true )
break if input.nil? || input == "exit"
# Remove blank lines from history
Readline::HISTORY.pop if input.empty?
process_input( input )
print_stack
end
end
def process_input( input )
@parser.parse_input( input ).each do |elt|
@stack << elt
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
end
def print_stack
stack_size = @stack.size
@stack.each_with_index do |elt, i|
puts "#{stack_size - i}: #{format_element( elt )}"
end
end
end
Readline.completion_append_character = " "
loop do
input = Readline.readline( "", true )
break if input == "exit"
# Remove blank lines from history
Readline::HISTORY.pop if input.empty?
stack = process_input( stack, input )
stack = display_stack( stack )
end
end
def process_input( stack, input )
parse_input( input ).each do |elt|
stack << elt
end
stack
end
def display_stack( stack )
stack_size = stack.size
stack.each_with_index { |elt, i| puts "#{stack_size - i}: #{elt["value"]}"}
stack
end
def stack_init
stack = []
stack
end
run_REPL( stack_init )
Rpn::Repl.new.run

View file

@ -1,54 +1,67 @@
# coding: utf-8
# frozen_string_literal: true
require "test/unit"
require 'test/unit'
require_relative "../lib/parser"
require_relative '../lib/parser'
class TestParser < Test::Unit::TestCase
def test_parse_input_number
result = parse_input( "1" )
assert_equal [ { "value" => 1, "type" => "NUMBER" } ], result
end
def test_number
result = Rpn::Parser.new.parse_input( '1' )
assert_equal [{ value: 1, type: :numeric }], result
end
def test_parse_input_word
result = parse_input( "dup" )
assert_equal [ { "value" => "dup", "type" => "WORD" } ], result
end
def test_word
result = Rpn::Parser.new.parse_input( 'dup' )
assert_equal [{ value: 'dup', type: :word }], result
end
def test_parse_input_string
result = parse_input( "\"test\"" )
assert_equal [ { "value" => "test", "type" => "STRING" } ], result
end
def test_string
result = Rpn::Parser.new.parse_input( '"test"' )
assert_equal [{ value: '"test"', type: :string }], result
def test_parse_input_name
result = parse_input( "'test'" )
assert_equal [ { "value" => "test", "type" => "NAME" } ], result
end
result = Rpn::Parser.new.parse_input( '" test"' )
assert_equal [{ value: '" test"', type: :string }], result
def test_parse_input_program
result = parse_input( "« test »" )
assert_equal [ { "value" => "test", "type" => "PROGRAM" } ], result
result = Rpn::Parser.new.parse_input( '"test "' )
assert_equal [{ value: '"test "', type: :string }], result
result = parse_input( "«test »" )
assert_equal [ { "value" => "test", "type" => "PROGRAM" } ], result
result = Rpn::Parser.new.parse_input( '" test "' )
assert_equal [{ value: '" test "', type: :string }], result
end
result = parse_input( "« test»" )
assert_equal [ { "value" => "test", "type" => "PROGRAM" } ], result
def test_name
result = Rpn::Parser.new.parse_input( "'test'" )
assert_equal [{ value: "'test'", type: :name }], result
end
result = parse_input( "« test test »" )
assert_equal [ { "value" => "test test", "type" => "PROGRAM" } ], result
def test_program
result = Rpn::Parser.new.parse_input( '« test »' )
assert_equal [{ value: '« test »', type: :program }], result
result = parse_input( "« test \"test\" test »" )
assert_equal [ { "value" => "test \"test\" test", "type" => "PROGRAM" } ], result
end
result = Rpn::Parser.new.parse_input( '«test »' )
assert_equal [{ value: '«test »', type: :program }], result
def test_parse_input_number_number
result = parse_input( "2 3" )
assert_equal [ { "value" => 2, "type" => "NUMBER" }, { "value" => 3, "type" => "NUMBER" } ], result
end
result = Rpn::Parser.new.parse_input( '« test»' )
assert_equal [{ value: '« test»', type: :program }], result
def test_parse_input_number_string
result = parse_input( "4 \"test\"" )
assert_equal [ { "value" => 4, "type" => "NUMBER" }, { "value" => "test", "type" => "STRING" } ], result
end
result = Rpn::Parser.new.parse_input( '« test test »' )
assert_equal [{ value: '« test test »', type: :program }], result
result = Rpn::Parser.new.parse_input( '« test « test » »' )
assert_equal [{ value: '« test « test » »', type: :program }], result
result = Rpn::Parser.new.parse_input( '« test "test" test »' )
assert_equal [{ value: '« test "test" test »', type: :program }], result
end
def test_number_number
result = Rpn::Parser.new.parse_input( '2 3' )
assert_equal [{ value: 2, type: :numeric }, { value: 3, type: :numeric }], 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
end
end