koto 0.1.0 → 0.2.0

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