Skip to content

Commit 2d0dc2f

Browse files
authored
Source mapping (#950)
2 parents 9a26f85 + d69d2cc commit 2d0dc2f

File tree

6 files changed

+148
-95
lines changed

6 files changed

+148
-95
lines changed

lib/phlex/compiler.rb

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,51 @@
44
require "refract"
55

66
module Phlex::Compiler
7+
MAP = {}
78
Error = Class.new(StandardError)
89

910
def self.compile(component)
1011
path, line = Object.const_source_location(component.name)
11-
return unless File.exist?(path)
12+
compile_file(path)
13+
end
14+
15+
def self.compile_file(path)
16+
unless File.exist?(path)
17+
raise ArgumentError, "Can’t compile #{path} because it doesn’t exist."
18+
end
19+
20+
require(path)
21+
1222
source = File.read(path)
1323
tree = Prism.parse(source).value
1424
refract = Refract::Converter.new.visit(tree)
1525

16-
Compilation.new(component, path, line, source, refract).compile
26+
last_line = source.count("\n")
27+
28+
starting_line = last_line + 1
29+
30+
results = FileCompiler.new.compile(refract)
31+
32+
result = Refract::StatementsNode.new(
33+
body: results.map do |result|
34+
result.namespace.reverse_each.reduce(
35+
result.compiled_snippets
36+
) do |body, scope|
37+
scope.copy(
38+
body: Refract::StatementsNode.new(
39+
body: [body]
40+
)
41+
)
42+
end
43+
end
44+
)
45+
46+
formatting_result = Refract::Formatter.new(starting_line:).format_node(result)
47+
48+
MAP[path] = formatting_result.source_map
49+
50+
puts formatting_result.source
51+
52+
eval("# frozen_string_literal: true\n#{formatting_result.source}", TOPLEVEL_BINDING, path, starting_line - 1)
1753
end
1854
end

lib/phlex/compiler/class_compiler.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# frozen_string_literal: true
22

33
class Phlex::Compiler::ClassCompiler < Refract::Visitor
4-
def initialize(compiler)
4+
def initialize(component)
55
super()
6-
@compiler = compiler
6+
@component = component
77
@compiled_snippets = []
88
end
99

@@ -17,7 +17,7 @@ def compile(node)
1717
return if node.receiver
1818

1919
@compiled_snippets << Phlex::Compiler::MethodCompiler.new(
20-
@compiler.component
20+
@component
2121
).compile(node)
2222
end
2323

lib/phlex/compiler/compilation.rb

Lines changed: 0 additions & 45 deletions
This file was deleted.

lib/phlex/compiler/file_compiler.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
class Phlex::Compiler::FileCompiler < Refract::Visitor
44
Result = Data.define(:namespace, :compiled_snippets)
55

6-
def initialize(compiler)
7-
super()
8-
@compiler = compiler
6+
def initialize
7+
super
98
@current_namespace = []
9+
@results = []
1010
end
1111

1212
def compile(node)
13-
catch(:phlex_compiler_result) do
14-
visit(node)
15-
end
13+
visit(node)
14+
@results.freeze
1615
end
1716

1817
visit Refract::ModuleNode do |node|
@@ -24,10 +23,16 @@ def compile(node)
2423
visit Refract::ClassNode do |node|
2524
@current_namespace.push(node)
2625

27-
if @compiler.line == node.start_line
28-
throw :phlex_compiler_result, Result.new(
26+
namespace = @current_namespace.map do |node|
27+
Refract::Formatter.new.format_node(node.constant_path).source
28+
end.join("::")
29+
30+
const = eval(namespace, TOPLEVEL_BINDING)
31+
32+
if Class === const && Phlex::SGML > const
33+
@results << Result.new(
2934
namespace: @current_namespace.dup.freeze,
30-
compiled_snippets: Phlex::Compiler::ClassCompiler.new(@compiler).compile(node)
35+
compiled_snippets: Phlex::Compiler::ClassCompiler.new(const).compile(node)
3136
)
3237
else
3338
super(node)

lib/phlex/compiler/method_compiler.rb

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@ def compile(node)
1616
end
1717

1818
def around_visit(node)
19-
result = super
20-
21-
# We want to clear the buffer when there’s a node that isn’t a statements node,
22-
# but we should ignore nils, which are usually other buffers.
23-
clear_buffer unless result in Refract::StatementsNode | nil
19+
unless node in Refract::StatementsNode | Refract::CallNode | nil
20+
clear_buffer
21+
end
2422

25-
result
23+
super
2624
end
2725

2826
visit Refract::ClassNode do |node|
@@ -36,14 +34,49 @@ def around_visit(node)
3634
visit Refract::DefNode do |node|
3735
if @stack.size == 1
3836
node.copy(
39-
body: Refract::StatementsNode.new(
40-
body: [
41-
Refract::StatementsNode.new(
42-
body: @preamble
37+
body: Refract::BeginNode.new(
38+
statements: Refract::StatementsNode.new(
39+
body: [
40+
Refract::StatementsNode.new(
41+
body: @preamble
42+
),
43+
Refract::NilNode.new,
44+
visit(node.body),
45+
]
46+
),
47+
rescue_clause: Refract::RescueNode.new(
48+
exceptions: [],
49+
reference: Refract::LocalVariableTargetNode.new(
50+
name: :__phlex_exception__
4351
),
44-
Refract::NilNode.new,
45-
visit(node.body),
46-
]
52+
statements: Refract::StatementsNode.new(
53+
body: [
54+
Refract::CallNode.new(
55+
receiver: Refract::ConstantReadNode.new(
56+
name: :Kernel
57+
),
58+
name: :raise,
59+
arguments: Refract::ArgumentsNode.new(
60+
arguments: [
61+
Refract::CallNode.new(
62+
name: :__map_exception__,
63+
arguments: Refract::ArgumentsNode.new(
64+
arguments: [
65+
Refract::LocalVariableReadNode.new(
66+
name: :__phlex_exception__
67+
),
68+
]
69+
)
70+
),
71+
]
72+
)
73+
),
74+
]
75+
),
76+
subsequent: nil
77+
),
78+
else_clause: nil,
79+
ensure_clause: nil
4780
)
4881
)
4982
else
@@ -72,6 +105,7 @@ def around_visit(node)
72105
end
73106
end
74107

108+
clear_buffer
75109
super(node)
76110
end
77111

@@ -134,7 +168,8 @@ def compile_phlex_attributes(node)
134168
return raw(
135169
Phlex::SGML::Attributes.generate_attributes(
136170
eval(
137-
"{#{Refract::Formatter.new.format_node(node)}}"
171+
"{#{Refract::Formatter.new.format_node(node).source}}",
172+
TOPLEVEL_BINDING
138173
)
139174
)
140175
)
@@ -253,9 +288,10 @@ def compile_doctype_helper(node)
253288
def compile_plain_helper(node)
254289
node => Refract::CallNode
255290

256-
if node.arguments in [Refract::StringNode]
291+
if node.arguments.arguments in [Refract::StringNode]
257292
raw(node.arguments.arguments.first.unescaped)
258293
else
294+
clear_buffer
259295
node
260296
end
261297
end
@@ -315,6 +351,8 @@ def compile_comment_helper(node)
315351

316352
def compile_raw_helper(node)
317353
node => Refract::CallNode
354+
355+
clear_buffer
318356
node
319357
end
320358

@@ -373,28 +411,33 @@ def compile_raw_helper(node)
373411
else
374412
@current_buffer = [node]
375413

376-
Refract::IfNode.new(
377-
inline: false,
378-
predicate: Refract::LocalVariableReadNode.new(
379-
name: should_render_local
380-
),
381-
statements: Refract::StatementsNode.new(
382-
body: [
383-
Refract::CallNode.new(
384-
receiver: Refract::CallNode.new(
385-
name: buffer_local,
386-
),
387-
name: :<<,
388-
arguments: Refract::ArgumentsNode.new(
389-
arguments: [
390-
Refract::InterpolatedStringNode.new(
391-
parts: @current_buffer
392-
),
393-
]
394-
)
414+
Refract::StatementsNode.new(
415+
body: [
416+
Refract::IfNode.new(
417+
inline: false,
418+
predicate: Refract::LocalVariableReadNode.new(
419+
name: should_render_local
395420
),
396-
]
397-
)
421+
statements: Refract::StatementsNode.new(
422+
body: [
423+
Refract::CallNode.new(
424+
receiver: Refract::CallNode.new(
425+
name: buffer_local,
426+
),
427+
name: :<<,
428+
arguments: Refract::ArgumentsNode.new(
429+
arguments: [
430+
Refract::InterpolatedStringNode.new(
431+
parts: @current_buffer
432+
),
433+
]
434+
)
435+
),
436+
]
437+
)
438+
),
439+
Refract::NilNode.new,
440+
]
398441
)
399442
end
400443
end

lib/phlex/sgml.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,4 +481,18 @@ def self.__compile__(method_name)
481481
path, line = instance_method(method_name).source_location
482482
Phlex::Compiler::Method.new(self, path, line, method_name).compile
483483
end
484+
485+
def __map_exception__(exception)
486+
exception.set_backtrace(
487+
exception.backtrace_locations.map do |loc|
488+
if ((map = Phlex::Compiler::MAP[loc.path]) && (line = map[loc.lineno]))
489+
"[Phlex] #{loc.path}:#{line}:#{loc.label}"
490+
else
491+
"#{loc.path}:#{loc.lineno}:#{loc.label}"
492+
end
493+
end
494+
)
495+
496+
exception
497+
end
484498
end

0 commit comments

Comments
 (0)