Skip to content

Commit 8a806f8

Browse files
authored
Optimising component compiler (#917)
2 parents 4921cbe + e8c4c29 commit 8a806f8

23 files changed

Lines changed: 1661 additions & 284 deletions

.rubocop-https---www-goodcop-style-base-yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# yaml-language-server: $schema=https://www.rubyschema.org/rubocop.json
12
---
23
AllCops:
34
DisabledByDefault: true
@@ -821,10 +822,6 @@ Style/QuotedSymbols:
821822
Enabled: true
822823
EnforcedStyle: same_as_string_literals
823824

824-
Style/RaiseArgs:
825-
Enabled: true
826-
EnforcedStyle: compact
827-
828825
Style/RandomWithOffset:
829826
Enabled: true
830827

lib/phlex/compiler.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
require "prism"
4+
5+
module Phlex::Compiler
6+
def self.compile(component)
7+
path, line = Object.const_source_location(component.name)
8+
return unless File.exist?(path)
9+
source = File.read(path)
10+
tree = Prism.parse(source).value
11+
Compilation.new(component, path, line, source, tree).compile
12+
end
13+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# frozen_string_literal: true
2+
3+
class Phlex::Compiler::ClassCompiler < Prism::Visitor
4+
def initialize(compiler)
5+
@compiler = compiler
6+
end
7+
8+
def compile(node)
9+
visit_all(node.child_nodes)
10+
end
11+
12+
def visit_def_node(node)
13+
return if node.name == :initialize
14+
15+
compiled_source = Phlex::Compiler::MethodCompiler.new(@compiler.component).compile(node)
16+
17+
if compiled_source
18+
puts compiled_source
19+
@compiler.redefine_method(compiled_source, node.location.start_line)
20+
end
21+
end
22+
23+
def visit_class_node(node)
24+
nil
25+
end
26+
27+
def visit_module_node(node)
28+
nil
29+
end
30+
31+
def visit_block_node(node)
32+
nil
33+
end
34+
end

lib/phlex/compiler/compilation.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module Phlex::Compiler
4+
class Compilation
5+
def initialize(component, path, line, source, tree)
6+
@component = component
7+
@path = path
8+
@line = line
9+
@source = source
10+
@tree = tree
11+
freeze
12+
end
13+
14+
attr_reader :component, :line, :source, :path
15+
16+
def compile
17+
FileCompiler.new(self).compile(@tree)
18+
end
19+
20+
def redefine_method(source, line)
21+
@component.class_eval("# frozen_string_literal: true\n#{source}", @path, line - 1)
22+
end
23+
end
24+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
3+
class Phlex::Compiler::FileCompiler < Prism::Visitor
4+
def initialize(compiler)
5+
@compiler = compiler
6+
end
7+
8+
def compile(node)
9+
visit(node)
10+
end
11+
12+
def visit_class_node(node)
13+
if @compiler.line == node.location.start_line
14+
Phlex::Compiler::ClassCompiler.new(@compiler).compile(node)
15+
end
16+
end
17+
18+
# def visit_module_node(node)
19+
# super
20+
# end
21+
22+
def visit_def_node(node)
23+
nil
24+
end
25+
26+
def visit_block_node(node)
27+
nil
28+
end
29+
end

lib/phlex/compiler/formatter.rb

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# frozen_string_literal: true
2+
3+
module Phlex::Compiler
4+
class Formatter < VerbatimFormatter
5+
def initialize
6+
super
7+
@new_line_character = "\n"
8+
@indentation_character = "\t"
9+
@level = 0
10+
end
11+
12+
def visit(node)
13+
case node
14+
when nil
15+
nil
16+
when Array
17+
visit_array(node)
18+
when Proc
19+
node.call(self)
20+
else
21+
super
22+
end
23+
end
24+
25+
def visit_array(nodes)
26+
nodes.each do |node|
27+
case node
28+
when Array
29+
indent { visit_array(node) }
30+
else
31+
visit(node)
32+
end
33+
end
34+
end
35+
36+
def format(node)
37+
@buffer.clear
38+
visit(node)
39+
[@buffer.join, @source_map]
40+
end
41+
42+
def visit_each(nodes)
43+
i, len = 0, nodes.length
44+
if block_given?
45+
while i < len
46+
node = nodes[i]
47+
i += 1
48+
if node
49+
visit node
50+
yield unless i == len
51+
end
52+
end
53+
else
54+
while i < len
55+
visit nodes[i]
56+
i += 1
57+
end
58+
end
59+
end
60+
61+
def space
62+
push " "
63+
end
64+
65+
def statement
66+
ensure_new_line
67+
yield
68+
end
69+
70+
def ensure_new_line
71+
new_line unless on_new_line?
72+
end
73+
74+
def on_new_line?
75+
@new_line_at == @buffer.length
76+
end
77+
78+
def new_line
79+
push "#{@new_line_character}#{@indentation_character * @level}"
80+
@new_line_at = @buffer.length
81+
end
82+
83+
def indent
84+
original_level = @level
85+
@level += 1
86+
ensure_new_line
87+
yield
88+
@level = original_level
89+
end
90+
91+
def visit_block_node(node)
92+
emit node.opening_loc
93+
indent do
94+
visit node.body
95+
end
96+
new_line
97+
emit node.closing_loc
98+
end
99+
100+
def visit_call_node(node)
101+
visit node.receiver
102+
emit node.call_operator_loc
103+
emit node.message_loc
104+
if node.opening_loc
105+
emit node.opening_loc
106+
else
107+
space
108+
end
109+
visit node.arguments
110+
emit node.closing_loc
111+
space
112+
visit node.block
113+
end
114+
115+
def visit_def_node(node)
116+
emit node.def_keyword_loc
117+
space
118+
push node.name
119+
120+
if node.parameters
121+
push "("
122+
visit node.parameters
123+
push ")"
124+
end
125+
126+
indent { visit node.body }
127+
128+
new_line
129+
emit node.end_keyword_loc
130+
end
131+
132+
def visit_statements_node(node)
133+
visit_each(node.compact_child_nodes) { ensure_new_line }
134+
end
135+
end
136+
end

0 commit comments

Comments
 (0)