forthic 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|