diff --git a/README.md b/README.md index 2cca5e8..7d3215e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ https://github.com/louisrubet/rpn/ inspired language in ruby +To run REPL locally: `ruby -Ilib bin/rpl` + +To run the test suite: `find ./spec/ -name \*.rb -exec ruby -Ilib {} \;` + # TODO-list * pseudo filesystem: subdir for variables * UI toolkit (based on https://github.com/AndyObtiva/glimmer-dsl-libui ?) diff --git a/bin/rpl b/bin/rpl new file mode 100755 index 0000000..720b6be --- /dev/null +++ b/bin/rpl @@ -0,0 +1,51 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'readline' + +require 'rpl' + +class RplRepl + def initialize + @interpreter = Rpl.new + end + + def run + Readline.completion_proc = proc do |s| + ( @interpreter.dictionary.words.keys + @interpreter.dictionary.vars.keys ).grep(/^#{Regexp.escape(s)}/) + end + Readline.completion_append_character = ' ' + + loop do + input = Readline.readline( ' ', true ) + break if input.nil? || input == 'quit' + + pp Readline::HISTORY if input == 'history' + + # Remove blank lines from history + Readline::HISTORY.pop if input.empty? + + begin + @interpreter.run( input ) + rescue ArgumentError => e + p e + end + + print_stack + end + end + + def format_element( elt ) + @interpreter.stringify( elt ) + end + + def print_stack + stack_size = @interpreter.stack.size + + @interpreter.stack.each_with_index do |elt, i| + puts "#{stack_size - i}: #{format_element( elt )}" + end + end +end + +RplRepl.new.run diff --git a/lib/rpl.rb b/lib/rpl.rb new file mode 100644 index 0000000..d06f292 --- /dev/null +++ b/lib/rpl.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rpl/interpreter' + +require 'rpl/core/branch' +require 'rpl/core/general' +require 'rpl/core/mode' +require 'rpl/core/operations' +require 'rpl/core/program' +require 'rpl/core/stack' +require 'rpl/core/store' +require 'rpl/core/string' +require 'rpl/core/test' +require 'rpl/core/time-date' +require 'rpl/core/trig' +require 'rpl/core/logarithm' +require 'rpl/core/filesystem' +require 'rpl/core/list' + +class Rpl < Interpreter + def initialize( stack = [], dictionary = Dictionary.new ) + super + + populate_dictionary if @dictionary.words.empty? + end + + prepend RplLang::Core::Branch + prepend RplLang::Core::FileSystem + prepend RplLang::Core::General + prepend RplLang::Core::List + prepend RplLang::Core::Logarithm + prepend RplLang::Core::Mode + prepend RplLang::Core::Operations + prepend RplLang::Core::Program + prepend RplLang::Core::Stack + prepend RplLang::Core::Store + prepend RplLang::Core::String + prepend RplLang::Core::Test + prepend RplLang::Core::TimeAndDate + prepend RplLang::Core::Trig + + def populate_dictionary; end +end diff --git a/lib/core/branch.rb b/lib/rpl/core/branch.rb similarity index 95% rename from lib/core/branch.rb rename to lib/rpl/core/branch.rb index dea644d..4577187 100644 --- a/lib/core/branch.rb +++ b/lib/rpl/core/branch.rb @@ -34,8 +34,7 @@ module RplLang run( args[1][:value] ) end - - end ) # specific + end ) @dictionary.add_word( ['loop'], 'Branch', @@ -49,7 +48,7 @@ module RplLang run( args[2][:value] ) end - end ) # specific + end ) end end end diff --git a/lib/core/filesystem.rb b/lib/rpl/core/filesystem.rb similarity index 100% rename from lib/core/filesystem.rb rename to lib/rpl/core/filesystem.rb diff --git a/lib/core/general.rb b/lib/rpl/core/general.rb similarity index 100% rename from lib/core/general.rb rename to lib/rpl/core/general.rb diff --git a/lib/core/list.rb b/lib/rpl/core/list.rb similarity index 100% rename from lib/core/list.rb rename to lib/rpl/core/list.rb diff --git a/lib/core/logarithm.rb b/lib/rpl/core/logarithm.rb similarity index 100% rename from lib/core/logarithm.rb rename to lib/rpl/core/logarithm.rb diff --git a/lib/core/mode.rb b/lib/rpl/core/mode.rb similarity index 99% rename from lib/core/mode.rb rename to lib/rpl/core/mode.rb index 586bbe1..039530a 100644 --- a/lib/core/mode.rb +++ b/lib/rpl/core/mode.rb @@ -6,8 +6,6 @@ module RplLang def populate_dictionary super - - # Mode @dictionary.add_word( ['prec'], 'Mode', '( a -- ) set precision to a', diff --git a/lib/core/operations.rb b/lib/rpl/core/operations.rb similarity index 99% rename from lib/core/operations.rb rename to lib/rpl/core/operations.rb index 3040e79..fef436b 100644 --- a/lib/core/operations.rb +++ b/lib/rpl/core/operations.rb @@ -385,7 +385,6 @@ module RplLang # proc do # end ) - end end end diff --git a/lib/core/program.rb b/lib/rpl/core/program.rb similarity index 100% rename from lib/core/program.rb rename to lib/rpl/core/program.rb diff --git a/lib/core/stack.rb b/lib/rpl/core/stack.rb similarity index 99% rename from lib/core/stack.rb rename to lib/rpl/core/stack.rb index d2ecbf9..c1be0f7 100644 --- a/lib/core/stack.rb +++ b/lib/rpl/core/stack.rb @@ -124,7 +124,6 @@ module RplLang end @stack << args[0] - end ) @dictionary.add_word( ['rolld'], diff --git a/lib/core/store.rb b/lib/rpl/core/store.rb similarity index 99% rename from lib/core/store.rb rename to lib/rpl/core/store.rb index 50fd1bf..deaed1e 100644 --- a/lib/core/store.rb +++ b/lib/rpl/core/store.rb @@ -60,7 +60,6 @@ module RplLang « swap » ift over rcl + swap sto' ) - end ) @dictionary.add_word( ['sto-'], @@ -83,7 +82,6 @@ module RplLang « swap » ift over rcl * swap sto' ) - end ) @dictionary.add_word( ['sto÷', 'sto/'], @@ -95,7 +93,6 @@ module RplLang « swap » ift over rcl swap / swap sto' ) - end ) @dictionary.add_word( ['sneg'], diff --git a/lib/core/string.rb b/lib/rpl/core/string.rb similarity index 98% rename from lib/core/string.rb rename to lib/rpl/core/string.rb index 273c06f..0c68d41 100644 --- a/lib/core/string.rb +++ b/lib/rpl/core/string.rb @@ -95,7 +95,7 @@ module RplLang end @stack << result - end ) # specific + end ) @dictionary.add_word( ['split'], 'String', @@ -107,7 +107,7 @@ module RplLang @stack << { type: :string, value: elt } end - end ) # specific + end ) end end end diff --git a/lib/core/test.rb b/lib/rpl/core/test.rb similarity index 98% rename from lib/core/test.rb rename to lib/rpl/core/test.rb index f9b54f9..1e58fe6 100644 --- a/lib/core/test.rb +++ b/lib/rpl/core/test.rb @@ -112,7 +112,7 @@ module RplLang proc do @stack << { type: :boolean, value: true } - end ) # specific + end ) @dictionary.add_word( ['false'], 'Test', @@ -120,7 +120,7 @@ module RplLang proc do @stack << { type: :boolean, value: false } - end ) # specific + end ) end end end diff --git a/lib/core/time-date.rb b/lib/rpl/core/time-date.rb similarity index 100% rename from lib/core/time-date.rb rename to lib/rpl/core/time-date.rb diff --git a/lib/core/trig.rb b/lib/rpl/core/trig.rb similarity index 99% rename from lib/core/trig.rb rename to lib/rpl/core/trig.rb index 0c3da78..11e7403 100644 --- a/lib/core/trig.rb +++ b/lib/rpl/core/trig.rb @@ -63,7 +63,6 @@ module RplLang ift » ifte' ) - end ) @dictionary.add_word( ['tan'], diff --git a/lib/dictionary.rb b/lib/rpl/dictionary.rb similarity index 100% rename from lib/dictionary.rb rename to lib/rpl/dictionary.rb diff --git a/lib/interpreter.rb b/lib/rpl/interpreter.rb similarity index 99% rename from lib/interpreter.rb rename to lib/rpl/interpreter.rb index 919aa1c..f693143 100644 --- a/lib/interpreter.rb +++ b/lib/rpl/interpreter.rb @@ -2,7 +2,7 @@ require 'bigdecimal/math' -require_relative './dictionary' +require 'rpl/dictionary' class Interpreter include BigMath diff --git a/rpl.gemspec b/rpl.gemspec new file mode 100644 index 0000000..f8c4bb8 --- /dev/null +++ b/rpl.gemspec @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +Gem::Specification.new do |s| + s.name = 'rpl' + s.version = '0.1.0' + s.summary = 'Functional Stack Language' + s.description = "A language inspired by HP's RPL and https://github.com/louisrubet/rpn/" + s.authors = ['Gwenhael Le Moine'] + s.email = 'gwenhael@le-moine.org' + s.files = ['lib/rpl.rb', + 'lib/rpl/dictionary.rb', + 'lib/rpl/interpreter.rb', + 'lib/rpl/core/branch.rb', + 'lib/rpl/core/filesystem.rb', + 'lib/rpl/core/general.rb', + 'lib/rpl/core/list.rb', + 'lib/rpl/core/logarithm.rb', + 'lib/rpl/core/mode.rb', + 'lib/rpl/core/operations.rb', + 'lib/rpl/core/program.rb', + 'lib/rpl/core/stack.rb', + 'lib/rpl/core/store.rb', + 'lib/rpl/core/string.rb', + 'lib/rpl/core/test.rb', + 'lib/rpl/core/time-date.rb', + 'lib/rpl/core/trig.rb'] + s.homepage = 'https://github.com/gwenhael-le-moine/rpl.rb' + s.license = 'GPL-3.0' + + s.executables << 'rpl' + + s.required_ruby_version = '~> 2.7' +end diff --git a/rpl.rb b/rpl.rb deleted file mode 100644 index d7802e3..0000000 --- a/rpl.rb +++ /dev/null @@ -1,90 +0,0 @@ -# frozen_string_literal: true - -require 'readline' - -require_relative './lib/interpreter' - -require_relative './lib/core/branch' -require_relative './lib/core/general' -require_relative './lib/core/mode' -require_relative './lib/core/operations' -require_relative './lib/core/program' -require_relative './lib/core/stack' -require_relative './lib/core/store' -require_relative './lib/core/string' -require_relative './lib/core/test' -require_relative './lib/core/time-date' -require_relative './lib/core/trig' -require_relative './lib/core/logarithm' -require_relative './lib/core/filesystem' -require_relative './lib/core/list' - -class Rpl < Interpreter - def initialize( stack = [], dictionary = Dictionary.new ) - super - - populate_dictionary if @dictionary.words.empty? - end - - prepend RplLang::Core::Branch - prepend RplLang::Core::FileSystem - prepend RplLang::Core::General - prepend RplLang::Core::List - prepend RplLang::Core::Logarithm - prepend RplLang::Core::Mode - prepend RplLang::Core::Operations - prepend RplLang::Core::Program - prepend RplLang::Core::Stack - prepend RplLang::Core::Store - prepend RplLang::Core::String - prepend RplLang::Core::Test - prepend RplLang::Core::TimeAndDate - prepend RplLang::Core::Trig - - def populate_dictionary; end -end - -class RplRepl - def initialize - @interpreter = Rpl.new - end - - def run - Readline.completion_proc = proc do |s| - Readline::HISTORY.grep(/^#{Regexp.escape(s)}/) - end - Readline.completion_append_character = ' ' - - loop do - input = Readline.readline( ' ', true ) - break if input.nil? || input == 'quit' - - pp Readline::HISTORY if input == 'history' - - # Remove blank lines from history - Readline::HISTORY.pop if input.empty? - - begin - @interpreter.run( input ) - rescue ArgumentError => e - p e - end - - print_stack - end - end - - def format_element( elt ) - @interpreter.stringify( elt ) - end - - def print_stack - stack_size = @interpreter.stack.size - - @interpreter.stack.each_with_index do |elt, i| - puts "#{stack_size - i}: #{format_element( elt )}" - end - end -end - -RplRepl.new.run if __FILE__ == $PROGRAM_NAME diff --git a/spec/core_spec.rb b/spec/core_spec.rb index 66ae333..0f6caaf 100644 --- a/spec/core_spec.rb +++ b/spec/core_spec.rb @@ -2,7 +2,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestParser < Test::Unit::TestCase def test_stack_extract diff --git a/spec/language_branch_spec.rb b/spec/language_branch_spec.rb index 1506009..33c832b 100644 --- a/spec/language_branch_spec.rb +++ b/spec/language_branch_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageBranch < Test::Unit::TestCase def test_loop diff --git a/spec/language_filesystem_spec.rb b/spec/language_filesystem_spec.rb index bed3488..a31ce15 100644 --- a/spec/language_filesystem_spec.rb +++ b/spec/language_filesystem_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageFileSystem < Test::Unit::TestCase def test_fread diff --git a/spec/language_operations_spec.rb b/spec/language_operations_spec.rb index 9adb68d..cdc64af 100644 --- a/spec/language_operations_spec.rb +++ b/spec/language_operations_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TesttLanguageOperations < Test::Unit::TestCase def test_add diff --git a/spec/language_program_spec.rb b/spec/language_program_spec.rb index d70e44c..e655366 100644 --- a/spec/language_program_spec.rb +++ b/spec/language_program_spec.rb @@ -2,7 +2,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageProgram < Test::Unit::TestCase def test_eval diff --git a/spec/language_stack_spec.rb b/spec/language_stack_spec.rb index 8e89648..85e7de5 100644 --- a/spec/language_stack_spec.rb +++ b/spec/language_stack_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageStack < Test::Unit::TestCase def test_swap diff --git a/spec/language_store_spec.rb b/spec/language_store_spec.rb index ddfc8eb..36089e5 100644 --- a/spec/language_store_spec.rb +++ b/spec/language_store_spec.rb @@ -2,7 +2,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageProgram < Test::Unit::TestCase def test_sto diff --git a/spec/language_string_spec.rb b/spec/language_string_spec.rb index d00adc6..44249d9 100644 --- a/spec/language_string_spec.rb +++ b/spec/language_string_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageString < Test::Unit::TestCase def test_to_string diff --git a/spec/language_test_spec.rb b/spec/language_test_spec.rb index fc26e7d..1bdbcca 100644 --- a/spec/language_test_spec.rb +++ b/spec/language_test_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageTest < Test::Unit::TestCase def test_greater_than diff --git a/spec/language_time-date_spec.rb b/spec/language_time-date_spec.rb index 089a440..81e669d 100644 --- a/spec/language_time-date_spec.rb +++ b/spec/language_time-date_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestLanguageTimeDate < Test::Unit::TestCase def test_time diff --git a/spec/language_trig_spec.rb b/spec/language_trig_spec.rb index 328a4b6..401432e 100644 --- a/spec/language_trig_spec.rb +++ b/spec/language_trig_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TesttLanguageOperations < Test::Unit::TestCase def test_pi diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 0865d7d..ed3e2c3 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -3,7 +3,7 @@ require 'test/unit' -require_relative '../rpl' +require 'rpl' class TestParser < Test::Unit::TestCase def test_number