Module: CrystalRuby::SourceReader
- Defined in:
- lib/crystalruby/source_reader.rb
Class Method Summary collapse
-
.extract_args_and_source_from_method(method, raw: false) ⇒ Object
Given a method, extracts the source code of the block passed to it and also converts any keyword arguments given in the method definition as a named map of keyword names to Crystal types.
-
.extract_expr_from_source_location(source_location) ⇒ Object
Reads code line by line from a given source location and returns the first valid Ruby expression found.
-
.extract_source_from_proc(block, raw: false) ⇒ Object
Given a proc, extracts the source code of the block passed to it If raw is true, the source is expected to be Raw Crystal code captured in a string or Heredoc literal.
-
.node_to_s(node) ⇒ Object
Simple helper function to turn a SyntaxTree node back into a Ruby string The default formatter will turn a break/return of [1,2,3] into a brackless 1,2,3 Can’t have that in Crystal as it turns it into a Tuple.
Class Method Details
.extract_args_and_source_from_method(method, raw: false) ⇒ Object
Given a method, extracts the source code of the block passed to it and also converts any keyword arguments given in the method definition as a named map of keyword names to Crystal types. Also supports basic ffi symbol types.
E.g.
def add a: Int32 | Int64, b: :int
The above will be converted to:
a: Int32 | Int64, # Int32 | Int64 is a Crystal type
b: :int # :int is an FFI type shorthand
If raw is true, the source is expected to be Raw Crystal code captured in a string or Heredoc literal. Otherwise the Ruby code (assumed to be valid Crystal) is extracted.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/crystalruby/source_reader.rb', line 68 def extract_args_and_source_from_method(method, raw: false) method_source = extract_expr_from_source_location(method.source_location) params = SyntaxTree.search(method_source, :Params).first args = params ? params.keywords.map{|k,v| [k.value[0...-1].to_sym, node_to_s(v)] }.to_h : {} body_node = SyntaxTree.search(method_source, :BodyStmt).first || SyntaxTree.search(method_source, :Statements).to_a[1] body = node_to_s(body_node) body = SyntaxTree.search(body, :TStringContent).map(&method(:node_to_s)).join if raw args.transform_values! do |type_exp| if CrystalRuby::Typemaps::CRYSTAL_TYPE_MAP.key?(type_exp[1..-1].to_sym) type_exp[1..-1].to_sym else TypeBuilder.build_from_source(type_exp, context: method.owner) end end.to_h return args, body end |
.extract_expr_from_source_location(source_location) ⇒ Object
Reads code line by line from a given source location and returns the first valid Ruby expression found
6 7 8 9 10 11 12 13 14 |
# File 'lib/crystalruby/source_reader.rb', line 6 def extract_expr_from_source_location(source_location) lines = source_location.then{|f,l| IO.readlines(f)[l-1..]} lines[0] = lines[0][/CRType.*/] if lines[0] =~ /<\s+CRType/ || lines[0] =~ /= CRType/ lines.each.with_object("") do |line, expr_source| break expr_source if (SyntaxTree.parse(expr_source << line) rescue nil) end rescue raise "Failed to extract expression from source location: #{source_location}. Ensure the file exists and the line number is correct. Extraction from a REPL is not supported" end |
.extract_source_from_proc(block, raw: false) ⇒ Object
Given a proc, extracts the source code of the block passed to it If raw is true, the source is expected to be Raw Crystal code captured in a string or Heredoc literal. Otherwise the Ruby code (assumed to be valid Crystal) is extracted.
20 21 22 23 24 25 26 |
# File 'lib/crystalruby/source_reader.rb', line 20 def extract_source_from_proc(block, raw: false) block_source = extract_expr_from_source_location(block.source_location) body_node = SyntaxTree.search(block_source, :Statements).to_a[1] return raw ? SyntaxTree.search(node_to_s(body_node), :TStringContent).map(&method(:node_to_s)).join : node_to_s(body_node) end |
.node_to_s(node) ⇒ Object
Simple helper function to turn a SyntaxTree node back into a Ruby string The default formatter will turn a break/return of [1,2,3] into a brackless 1,2,3 Can’t have that in Crystal as it turns it into a Tuple
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/crystalruby/source_reader.rb', line 38 def node_to_s(node) @_syxt_transform ||= SyntaxTree::FlowControlFormatter.prepend(Module.new do def format(quer) first_arg = self.node.arguments.child_nodes if first_arg[0].kind_of?(SyntaxTree::ArrayLiteral) return format_arguments(quer, " [", "]") end super(quer) end end) SyntaxTree::Formatter.format("", node.accept(@visitor)) end |