forthic 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/Guardfile +42 -0
- data/README.md +37 -0
- data/Rakefile +14 -0
- data/lib/forthic/code_location.rb +20 -0
- data/lib/forthic/forthic_error.rb +51 -0
- data/lib/forthic/forthic_module.rb +145 -0
- data/lib/forthic/global_module.rb +2341 -0
- data/lib/forthic/interpreter.rb +328 -0
- data/lib/forthic/positioned_string.rb +19 -0
- data/lib/forthic/token.rb +38 -0
- data/lib/forthic/tokenizer.rb +305 -0
- data/lib/forthic/variable.rb +34 -0
- data/lib/forthic/version.rb +5 -0
- data/lib/forthic/words/definition_word.rb +40 -0
- data/lib/forthic/words/end_array_word.rb +28 -0
- data/lib/forthic/words/end_module_word.rb +16 -0
- data/lib/forthic/words/imported_word.rb +27 -0
- data/lib/forthic/words/map_word.rb +169 -0
- data/lib/forthic/words/module_memo_bang_at_word.rb +22 -0
- data/lib/forthic/words/module_memo_bang_word.rb +21 -0
- data/lib/forthic/words/module_memo_word.rb +35 -0
- data/lib/forthic/words/module_word.rb +21 -0
- data/lib/forthic/words/push_value_word.rb +21 -0
- data/lib/forthic/words/start_module_word.rb +31 -0
- data/lib/forthic/words/word.rb +30 -0
- data/lib/forthic.rb +25 -0
- data/sig/forthic.rbs +4 -0
- metadata +72 -0
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
data/CHANGELOG.md
ADDED
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,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
|