koto 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35f8638e8720898b69f308522cb589e37fe193cf29d7e2213ad57db496be35f0
4
- data.tar.gz: bbf079b7f834fd6cc2893d7c20bfaa5797368f39f5caeb777de30346b2d6e8fd
3
+ metadata.gz: 91a8cdd2c571cf39f6b9c6c41b8d90cf2f6730a9ae8d407dabf6647ba92706a9
4
+ data.tar.gz: 41f6d7446084e509d3a88c8b9ca0d9b6097cb946dbf27a4effc5c514bb03c40d
5
5
  SHA512:
6
- metadata.gz: 102158ef6a82bb39e36a9ee2297e2fa09900ea620782e337baacadb78ff03569f1d787b1329d777640ff3d76b854ee291ec1e9386535a14d443b444ed2db10c7
7
- data.tar.gz: 050b1c6608e82feb2030935bc8568c8a3f342ca81af36c822289a98a8c3bfc8e466a9a96fd2f1a00ab8e954cf5121e6ed763d967f5b5ea9f1d55f76e5567aecb
6
+ metadata.gz: c1738575d73950ab26c65caf2286477fbc6e990c57cbdbf28da9f6e1b4da4a28471c97841ee1b0f9345789aeac7b78936692718476173b46df7e7854ac8bcfed
7
+ data.tar.gz: 58de4b49a75a1ab0805c81cf00ef7620c0465cecb3ad4731c48bef2001f56fffba011182b209099f90508ae5b73bfb2d248fbced1f984542ceeea6d6d8b4b11f
data/Gemfile CHANGED
@@ -8,3 +8,5 @@ gemspec
8
8
  gem "rake", "~> 13.0"
9
9
 
10
10
  gem "minitest", "~> 5.0"
11
+
12
+ gem "pry"
@@ -3,15 +3,22 @@ PATH
3
3
  specs:
4
4
  koto (0.1.0)
5
5
  parser (~> 3.0)
6
+ spaghetti_stack (~> 0.2.1)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
11
  ast (2.4.1)
12
+ coderay (1.1.3)
13
+ method_source (1.0.0)
11
14
  minitest (5.14.2)
12
15
  parser (3.0.0.0)
13
16
  ast (~> 2.4.1)
17
+ pry (0.13.1)
18
+ coderay (~> 1.1)
19
+ method_source (~> 1.0)
14
20
  rake (13.0.3)
21
+ spaghetti_stack (0.2.1)
15
22
 
16
23
  PLATFORMS
17
24
  x86_64-linux
@@ -19,6 +26,7 @@ PLATFORMS
19
26
  DEPENDENCIES
20
27
  koto!
21
28
  minitest (~> 5.0)
29
+ pry
22
30
  rake (~> 13.0)
23
31
 
24
32
  BUNDLED WITH
data/README.md CHANGED
@@ -26,7 +26,7 @@ Load *koto*.
26
26
  ```ruby
27
27
  require 'koto'
28
28
  ```
29
- An code example with <code>Koto::Parser::AST::Processor</code> which extends <code>Parser::AST::Processor</code>.
29
+ A code example with `Koto::Parser::AST::Processor` which extends `Parser::AST::Processor`.
30
30
  ```ruby
31
31
  node = Parser::CurrentRuby.parse("Koto::Parser::AST::Processor::Resolver")
32
32
 
@@ -6,10 +6,41 @@ require "koto"
6
6
 
7
7
  # You can add fixtures and/or initialization code here to make experimenting
8
8
  # with your gem easier. You can also use a different console, if you like.
9
+ CODE = "module AST; class Context; private; def get_in(node); stack << node; end; end; end".freeze
10
+
11
+ def parser
12
+ builder = Koto::Parser::Builders::Default.new
13
+ ::Parser::CurrentRuby.new(builder)
14
+ end
15
+
16
+ def parse(source)
17
+ buffer = ::Parser::Source::Buffer.new('(string)', :source => source)
18
+ parser.parse(buffer)
19
+ end
20
+
21
+ def n(type, children)
22
+ Koto::Parser::AST::Node.new(type, children)
23
+ end
24
+
25
+ def processor
26
+ Koto::Parser::AST::Processor.new
27
+ end
28
+
29
+ def process(node)
30
+ processor.process(node)
31
+ end
32
+
33
+ def context
34
+ Koto::Parser::AST::Processor::Context.new
35
+ end
36
+
37
+ def name_processor
38
+ Koto::Parser::AST::Processor::NameProcessor.new
39
+ end
9
40
 
10
41
  # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
42
+ require "pry"
43
+ Pry.start
13
44
 
14
- require "irb"
15
- IRB.start(__FILE__)
45
+ #require "irb"
46
+ #IRB.start(__FILE__)
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.require_paths = ["lib"]
27
27
 
28
28
  spec.add_dependency 'parser', '~> 3.0'
29
+ spec.add_dependency 'spaghetti_stack', '~> 0.2.1'
29
30
 
30
31
  # For more information and examples about making a new gem, checkout our
31
32
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -6,9 +6,24 @@ module Koto
6
6
  require "koto/version"
7
7
 
8
8
  module Parser
9
+
9
10
  module AST
11
+ require 'koto/parser/ast/node'
10
12
  require 'koto/parser/ast/processor'
11
- require 'koto/parser/ast/processor/name_processor'
13
+
14
+ class Processor
15
+ require "koto/parser/ast/processor/context"
16
+ require 'koto/parser/ast/processor/name_processor'
17
+
18
+ class Context
19
+ require "spaghetti_stack"
20
+ require "koto/parser/ast/processor/context/symbol_table"
21
+ end
22
+ end
23
+ end
24
+
25
+ module Builders
26
+ require "koto/parser/builders/default"
12
27
  end
13
28
  end
14
29
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Koto
4
+ module Parser
5
+ module AST
6
+ class Node < ::Parser::AST::Node
7
+ attr_reader :name
8
+ attr_reader :context
9
+ attr_reader :symbols
10
+
11
+ def parent_scope
12
+ context.current_scope
13
+ end
14
+
15
+ def access
16
+ context.access
17
+ end
18
+
19
+ def assign_properties(properties)
20
+ super
21
+
22
+ if (name = properties[:name])
23
+ @name = name
24
+ end
25
+
26
+ if (context = properties[:context])
27
+ context = context.deep_dup if context.frozen?
28
+ @context = context
29
+ end
30
+
31
+ if (symbols = properties[:symbols])
32
+ symbols = symbols.dup if symbols.frozen?
33
+ @symbols = symbols
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -4,21 +4,212 @@ module Koto
4
4
  module Parser
5
5
  module AST
6
6
  class Processor < ::Parser::AST::Processor
7
- attr_reader :name_processor
7
+
8
+ OBJECT_METHODS = [
9
+ *(Object.methods - [:class])
10
+ ]
11
+
12
+ KERNEL_METHODS = [
13
+ *(Kernel.methods - OBJECT_METHODS)
14
+ ]
15
+
16
+ ACCESS_METHODS = [
17
+ :private,
18
+ :protected,
19
+ :public,
20
+ ]
21
+
22
+ MODULE_METHODS = [
23
+ *(Module.methods - OBJECT_METHODS)
24
+ ]
25
+
26
+ PREDEFINED_METHODS = [
27
+ *OBJECT_METHODS,
28
+ *KERNEL_METHODS,
29
+ *ACCESS_METHODS,
30
+ *MODULE_METHODS
31
+ ]
32
+
33
+ BUILTIN_TYPES = [
34
+ *Object.constants
35
+ ]
36
+
37
+ attr_reader :context
8
38
 
9
39
  def initialize
10
- @name_processor = NameProcessor.new
40
+ @context = Context.new
41
+ end
42
+
43
+ def switch_context_to(context)
44
+ @context = context
45
+ end
46
+
47
+ def name_processor
48
+ @name_processor ||= NameProcessor.new
49
+ end
50
+
51
+ def required_files
52
+ @required_files ||= []
53
+ end
54
+
55
+ # Methods for processing nodes
56
+
57
+ def on_class(node)
58
+ name, superclass, body = *node
59
+
60
+ name = name_processor.process(name)
61
+ superclass = process superclass
62
+
63
+ node =
64
+ node.updated nil,
65
+ [name, superclass, body],
66
+ :name => name
67
+
68
+ access = context.access
69
+ current_context = context.get_in(node)
70
+
71
+ switch_context_to current_context
72
+
73
+ node =
74
+ node.updated nil,
75
+ [name, superclass, process(body)],
76
+ :name => name
77
+
78
+ symbols, current_context = context.get_out
79
+
80
+ current_context =
81
+ current_context.switch_access_to access
82
+
83
+ node =
84
+ node.updated :casgn,
85
+ [nil, name, node],
86
+ :name => name, :context => current_context, :symbols => symbols
87
+
88
+ current_context = current_context.save(node)
89
+
90
+ switch_context_to current_context
91
+
92
+ node
93
+ end
94
+
95
+ def on_module(node)
96
+ name, body = *node
97
+
98
+ name = name_processor.process(name)
99
+
100
+ node =
101
+ node.updated nil,
102
+ [name, body],
103
+ :name => name
104
+
105
+ access = context.access
106
+ current_context = context.get_in(node)
107
+
108
+ switch_context_to current_context
109
+
110
+ node =
111
+ node.updated nil,
112
+ [name, process(body)],
113
+ :name => name
114
+
115
+ symbols, current_context = context.get_out
116
+
117
+ current_context =
118
+ current_context.switch_access_to access
119
+
120
+ node =
121
+ node.updated :casgn,
122
+ [nil, name, node],
123
+ :name => name, :context => current_context, :symbols => symbols
124
+
125
+ current_context = current_context.save(node)
126
+
127
+ switch_context_to current_context
128
+
129
+ node
130
+ end
131
+
132
+ def on_def(node)
133
+ name, args, body = *node
134
+
135
+ args = process_all(args)
136
+
137
+ node =
138
+ node.updated nil,
139
+ [name, *args, body],
140
+ :name => name
141
+
142
+ access = context.access
143
+ current_context = context.get_in(node)
144
+
145
+ switch_context_to current_context
146
+
147
+ node =
148
+ node.updated nil,
149
+ [name, *args, (body = process(body))],
150
+ :name => name
151
+
152
+ symbols, current_context = context.get_out
153
+
154
+ current_context =
155
+ current_context.switch_access_to access
156
+
157
+ node =
158
+ node.updated nil,
159
+ [name, *args, body],
160
+ :name => name, :context => current_context, :symbols => symbols
161
+
162
+ current_context = current_context.save(node)
163
+
164
+ switch_context_to current_context
165
+
166
+ node
11
167
  end
12
168
 
13
169
  def on_const(node)
14
170
  scope, name = *node
15
171
 
16
172
  if scope
17
- name = name_processor.process node
173
+ name = name_processor.process(node)
174
+ end
175
+
176
+ node.updated nil,
177
+ [nil, name],
178
+ :name => name
179
+ end
180
+
181
+ def on_send(node)
182
+ _, name, _ = *node
183
+
184
+ # Invokes specific handlers for predefined methods
185
+ if PREDEFINED_METHODS.include? name
186
+ on_method = :"on_#{name}"
187
+
188
+ if respond_to? on_method
189
+ node = node.updated :"#{name}", node.children
190
+ return send on_method, node
191
+ else
192
+ return handler_missing(node)
193
+ end
18
194
  end
19
195
 
20
- node.updated nil, [nil, name]
196
+ node.updated nil,
197
+ [nil, name],
198
+ :name => name
199
+
200
+ super
21
201
  end
202
+
203
+ def on_access(node)
204
+ access = node.type
205
+ current_context = context.switch_access_to access
206
+
207
+ switch_context_to current_context
208
+ end
209
+
210
+ alias on_private on_access
211
+ alias on_protected on_access
212
+ alias on_public on_access
22
213
  end
23
214
  end
24
215
  end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Koto
4
+ module Parser
5
+ module AST
6
+ class Processor
7
+
8
+ class Context
9
+ attr_reader :scopes
10
+ attr_reader :stack
11
+ attr_reader :access
12
+
13
+ def initialize
14
+ @scopes = SpaghettiStack.new(SymbolTable.new)
15
+ @stack = []
16
+ @access = :public
17
+
18
+ freeze
19
+ end
20
+
21
+ def get_in(node)
22
+ copy = self.deep_dup
23
+ copy.get_in!(node)
24
+ end
25
+
26
+ def get_in!(node)
27
+ @scopes << SymbolTable.new
28
+ @stack << node
29
+
30
+ self
31
+ end
32
+
33
+ protected :get_in!
34
+
35
+ def get_out
36
+ copy = self.deep_dup
37
+ copy.get_out!
38
+ end
39
+
40
+ def get_out!
41
+ symbol_table = symbols.freeze
42
+ active_scope.update(symbol_table)
43
+
44
+ unless top_level?
45
+ @scopes.pop
46
+ @stack.pop
47
+ end
48
+
49
+ return symbol_table, self
50
+ end
51
+
52
+ protected :get_out!
53
+
54
+ def save(node)
55
+ copy = self.deep_dup
56
+ copy.save! node
57
+ end
58
+
59
+ def save!(node)
60
+ symbol_table = symbols.record(node)
61
+ active_scope.update(symbol_table)
62
+
63
+ self
64
+ end
65
+
66
+ protected :save!
67
+
68
+ def symbols
69
+ active_scope.data
70
+ end
71
+
72
+ def active_scope
73
+ @scopes.top
74
+ end
75
+
76
+ def current_scope
77
+ @stack.last
78
+ end
79
+
80
+ def switch_access_to(access)
81
+ return self unless ACCESS_METHODS.include?(access) &&
82
+ access != @access
83
+
84
+ copy = self.dup
85
+ copy.instance_variable_set(:@access, access)
86
+
87
+ copy
88
+ end
89
+
90
+ def top_level?
91
+ @stack.empty?
92
+ end
93
+
94
+ def in_class?
95
+ return false if top_level?
96
+ current_scope.type == :class
97
+ end
98
+
99
+ def in_sclass?
100
+ return false if top_level?
101
+ current_scope.type == :sclass
102
+ end
103
+
104
+ def in_module?
105
+ return false if top_level?
106
+ current_scope.type == :module
107
+ end
108
+
109
+ def in_def?
110
+ return false if top_level?
111
+ current_scope.type == :def
112
+ end
113
+
114
+ def in_block?
115
+ return false if top_level?
116
+ current_scope.type == :block
117
+ end
118
+
119
+ def in_lambda?
120
+ return false if top_level?
121
+ current_scope.type == :lambda
122
+ end
123
+
124
+ def in_dynamic_block?
125
+ in_block? || in_lambda?
126
+ end
127
+
128
+ def deep_dup
129
+ copy = self.dup
130
+
131
+ copy.instance_variables.each do |attr|
132
+ value = instance_variable_get(attr).dup
133
+ copy.instance_variable_set attr, value
134
+ end
135
+
136
+ copy
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Koto
4
+ module Parser
5
+ module AST
6
+ class Processor::Context
7
+
8
+ class SymbolTable < Hash
9
+ def initialize(symbol = nil)
10
+ record symbol if symbol
11
+ end
12
+
13
+ def record(symbol)
14
+ self[symbol.name] = symbol
15
+ self
16
+ end
17
+
18
+ def variables
19
+ select do |_, symbol|
20
+ [:lvasgn, :ivasgn, :cvasgn, :gvasgn].includes? symbol.type
21
+ end
22
+ end
23
+
24
+ def instance_variables
25
+ variables.select do |_, variable|
26
+ variable.type == :ivasgn
27
+ end
28
+ end
29
+
30
+ def private_instance_variables
31
+ instance_variables.select do |_, instance_variable|
32
+ instance_variable.access == :private
33
+ end
34
+ end
35
+
36
+ def constants
37
+ select do |_, symbol|
38
+ symbol.type == :casgn
39
+ end
40
+ end
41
+
42
+ def methods
43
+ select do |_, symbol|
44
+ [:def, :defs].include? symbol.type
45
+ end
46
+ end
47
+
48
+ def instance_methods
49
+ methods.select do |_, method|
50
+ context = method.context
51
+
52
+ method.type == :def and
53
+ context.in_class? or context.in_module?
54
+ end
55
+ end
56
+
57
+ def singleton_methods
58
+ methods.select do |_, method|
59
+ method.type == :defs
60
+ end
61
+ end
62
+
63
+ def private_methods
64
+ methods.select do |_, method|
65
+ method.access == :private
66
+ end
67
+ end
68
+
69
+ def private_instance_methods
70
+ instance_methods.select do |_, instance_method|
71
+ instance_method.access == :private
72
+ end
73
+ end
74
+
75
+ def classes
76
+ constants.select do |_, constant|
77
+ _, value = *constant
78
+ value.type == :class
79
+ end
80
+ end
81
+
82
+ def modules
83
+ constants.select do |_, constant|
84
+ _, value = *constant
85
+ value.type == :module
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -3,21 +3,24 @@
3
3
  module Koto
4
4
  module Parser
5
5
  module AST
6
- class NameProcessor < ::Parser::AST::Processor
7
- def on_const(node)
8
- scope, name = *node
9
- return name unless scope
6
+ class Processor
10
7
 
11
- top_level = ::Parser::AST::Node.new(:cbase)
8
+ class NameProcessor < ::Parser::AST::Processor
9
+ def on_const(node)
10
+ scope, name = *node
11
+ return name unless scope
12
12
 
13
- if scope == top_level
14
- name = "::#{name}"
15
- else
16
- scope = process scope
17
- name = "#{scope}::#{name}"
18
- end
13
+ top_level = ::Parser::AST::Node.new(:cbase)
14
+
15
+ if scope == top_level
16
+ name = "::#{name}"
17
+ else
18
+ scope = process scope
19
+ name = "#{scope}::#{name}"
20
+ end
19
21
 
20
- name.to_sym
22
+ name.to_sym
23
+ end
21
24
  end
22
25
  end
23
26
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Koto
4
+ module Parser
5
+ class Builders::Default < ::Parser::Builders::Default
6
+ def n(type, children, location)
7
+ AST::Node.new(type, children, :location => location)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Koto
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: koto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mansakondo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-28 00:00:00.000000000 Z
11
+ date: 2021-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: spaghetti_stack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.1
27
41
  description:
28
42
  email:
29
43
  - mansakondo22@gmail.com
@@ -42,8 +56,12 @@ files:
42
56
  - bin/setup
43
57
  - koto.gemspec
44
58
  - lib/koto.rb
59
+ - lib/koto/parser/ast/node.rb
45
60
  - lib/koto/parser/ast/processor.rb
61
+ - lib/koto/parser/ast/processor/context.rb
62
+ - lib/koto/parser/ast/processor/context/symbol_table.rb
46
63
  - lib/koto/parser/ast/processor/name_processor.rb
64
+ - lib/koto/parser/builders/default.rb
47
65
  - lib/koto/version.rb
48
66
  homepage: https://github.com/mansakondo/koto.git
49
67
  licenses: