forthic 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4e71f6368dc2bcc817f0095a389740a6676005ab8ddb862cdef02f0e0910d09d
4
+ data.tar.gz: b408177ce893d5353aa00ce2a73698005c07e22e3d68a8ec6cade6032887a591
5
+ SHA512:
6
+ metadata.gz: 2ffd692800bdf839d25a106c79bea485addaeabb7d2eafd5366a9d034927a642fed7b4ecb498e5f081016f5079077d3a4db4ba96eae113a8ce547024b4164736
7
+ data.tar.gz: b4ab0aec5f7b788873bbbc71761f5382f565864057101125919338c24453bdccdedbcf26f281dc5c46dee4a16fc7824a08516a52c9c318af91ab0f932a329f42
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-12-27
4
+
5
+ - Initial release
data/Guardfile ADDED
@@ -0,0 +1,42 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard :minitest do
19
+ # with Minitest::Unit
20
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
21
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
22
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
23
+
24
+ # with Minitest::Spec
25
+ # watch(%r{^spec/(.*)_spec\.rb$})
26
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
27
+ # watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
28
+
29
+ # Rails 4
30
+ # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
31
+ # watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
32
+ # watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
33
+ # watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
34
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
35
+ # watch(%r{^test/.+_test\.rb$})
36
+ # watch(%r{^test/test_helper\.rb$}) { 'test' }
37
+
38
+ # Rails < 4
39
+ # watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
40
+ # watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
41
+ # watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
42
+ end
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Forthic
2
+
3
+ A Forthic interpreter that runs within Ruby.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ ```bash
10
+ bundle add forthic
11
+ ```
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ ```bash
16
+ gem install forthic
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Here's a basic example of how to use the Forthic interpreter:
22
+
23
+ ```ruby
24
+ require 'forthic'
25
+
26
+ interp = Forthic::Interpreter.new
27
+ interp.run("[1 2 3] '8 *' MAP")
28
+ puts interp.stack_pop
29
+
30
+ # Output:
31
+ #
32
+ # [ 8, 16, 24 ]
33
+ ```
34
+
35
+ ## Contributing
36
+
37
+ Bug reports and pull requests are welcome on GitHub at https://github.com/linkedin/forthic.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "standard/rake"
9
+
10
+ task default: %i[test standard]
11
+
12
+ task :guard do
13
+ sh "bundle exec guard"
14
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Forthic
4
+ class CodeLocation
5
+ attr_accessor :screen_name, :line, :column, :start_pos, :end_pos
6
+
7
+ # @param [String] screen_name
8
+ # @param [Integer] line
9
+ # @param [Integer] column
10
+ # @param [Integer] start_pos
11
+ # @param [Integer] end_pos
12
+ def initialize(screen_name: "<ad-hoc>", line: 1, column: 1, start_pos: 0, end_pos: 0)
13
+ @screen_name = screen_name
14
+ @line = line
15
+ @column = column
16
+ @start_pos = start_pos
17
+ @end_pos = end_pos
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Forthic
4
+ class ForthicError < StandardError
5
+ attr_accessor :error_key, :title, :description, :location, :caught_error
6
+
7
+ # @param [String] error_key
8
+ # @param [String] title
9
+ # @param [String] description
10
+ # @param [CodeLocation, nil] location
11
+ def initialize(error_key, title, description, location = nil)
12
+ @error_key = error_key
13
+ @title = title
14
+ @description = description
15
+ @location = location
16
+ @caught_error = nil
17
+ puts "ForthicError: #{error_key}, #{title}, #{description}, #{location}"
18
+ end
19
+
20
+ # @param [ForthicError] error
21
+ def set_caught_error(error)
22
+ @caught_error = error
23
+ end
24
+
25
+ # @return [String]
26
+ def get_title
27
+ @title
28
+ end
29
+
30
+ # @return [String]
31
+ def get_description
32
+ @description
33
+ end
34
+
35
+ # @return [Array<ForthicError>]
36
+ def get_error_stack
37
+ max_depth = 100
38
+ cur_error = self
39
+ result = [cur_error]
40
+
41
+ max_depth.times do
42
+ break unless cur_error.caught_error
43
+
44
+ result << cur_error.caught_error
45
+ cur_error = cur_error.caught_error
46
+ end
47
+
48
+ result.reverse
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'words/word'
4
+ require_relative 'words/module_word'
5
+ require_relative 'words/module_memo_word'
6
+ require_relative 'words/module_memo_bang_word'
7
+ require_relative 'words/module_memo_bang_at_word'
8
+ require_relative 'words/imported_word'
9
+ require_relative 'variable'
10
+
11
+ module Forthic
12
+ class ForthicModule
13
+ attr_accessor :words, :exportable, :variables, :modules, :module_prefixes, :required_modules, :name, :forthic_code, :module_id
14
+
15
+ # @param [String] name
16
+ # @param [Interpreter, nil] interp
17
+ # @param [String] forthic_code
18
+ def initialize(name, interp = nil, forthic_code = "")
19
+ @words = []
20
+ @exportable = []
21
+ @variables = {}
22
+ @modules = {}
23
+ @module_prefixes = {}
24
+ @required_modules = []
25
+ @name = name
26
+ @forthic_code = forthic_code
27
+ @module_id = "#{name}-#{rand(1_000_000)}"
28
+ end
29
+
30
+ # @return [ForthicModule]
31
+ def dup
32
+ result = ForthicModule.new(@name, @interp, @forthic_code)
33
+ result.words = @words.dup
34
+ result.exportable = @exportable.dup
35
+ @variables.each { |key, var| result.variables[key] = var.dup }
36
+ @modules.each { |key, mod| result.modules[key] = mod }
37
+ result.required_modules = @required_modules.dup
38
+ result.forthic_code = @forthic_code
39
+ result
40
+ end
41
+
42
+ # @param [String] prefix
43
+ # @param [ForthicModule] mod
44
+ def require_module(prefix, mod)
45
+ @required_modules << { prefix: prefix, module: mod }
46
+ end
47
+
48
+ # @param [String] name
49
+ # @return [ForthicModule, nil]
50
+ def find_module(name)
51
+ @modules[name]
52
+ end
53
+
54
+ # @param [String] word_name
55
+ # @param [Proc] word_func
56
+ def add_module_word(word_name, word_func)
57
+ add_exportable_word(ModuleWord.new(word_name, word_func))
58
+ end
59
+
60
+ # @param [Word] word
61
+ def add_word(word)
62
+ @words << word
63
+ end
64
+
65
+ # @param [Word] word
66
+ # @return [ModuleMemoWord]
67
+ def add_memo_words(word)
68
+ memo_word = ModuleMemoWord.new(word)
69
+ @words << memo_word
70
+ @words << ModuleMemoBangWord.new(memo_word)
71
+ @words << ModuleMemoBangAtWord.new(memo_word)
72
+ memo_word
73
+ end
74
+
75
+ # @param [Array<String>] names
76
+ def add_exportable(names)
77
+ @exportable.concat(names)
78
+ end
79
+
80
+ # @return [Array<Word>]
81
+ def exportable_words
82
+ @words.select { |word| @exportable.include?(word.name) }
83
+ end
84
+
85
+ # @param [Word] word
86
+ def add_exportable_word(word)
87
+ @words << word
88
+ @exportable << word.name
89
+ end
90
+
91
+ # @param [String] name
92
+ # @param [Object, nil] value
93
+ def add_variable(name, value = nil)
94
+ @variables[name] ||= Variable.new(name, value)
95
+ end
96
+
97
+ # @param [Interpreter] interp
98
+ def initialize_modules(interp)
99
+ @required_modules.each do |rec|
100
+ import_module(rec[:prefix], rec[:module], interp)
101
+ end
102
+ end
103
+
104
+ # @param [String] module_name
105
+ # @param [String] prefix
106
+ # @param [ForthicModule] mod
107
+ def register_module(module_name, prefix, mod)
108
+ @modules[module_name] = mod
109
+ @module_prefixes[module_name] ||= Set.new
110
+ @module_prefixes[module_name] << prefix
111
+ end
112
+
113
+ # @param [String] prefix
114
+ # @param [ForthicModule] mod
115
+ # @param [Interpreter] interp
116
+ def import_module(prefix, mod, interp)
117
+ new_module = mod.dup
118
+ new_module.initialize_modules(interp)
119
+
120
+ new_module.exportable_words.each do |word|
121
+ add_word(ImportedWord.new(word, prefix, new_module))
122
+ end
123
+ register_module(mod.name, prefix, new_module)
124
+ end
125
+
126
+ # @param [String] name
127
+ # @return [Word, nil]
128
+ def find_word(name)
129
+ find_dictionary_word(name) || find_variable(name)
130
+ end
131
+
132
+ # @param [String] word_name
133
+ # @return [Word, nil]
134
+ def find_dictionary_word(word_name)
135
+ @words.reverse.find { |w| w.name == word_name }
136
+ end
137
+
138
+ # @param [String] varname
139
+ # @return [PushValueWord, nil]
140
+ def find_variable(varname)
141
+ var_result = @variables[varname]
142
+ var_result ? PushValueWord.new(varname, var_result) : nil
143
+ end
144
+ end
145
+ end