Skip to content
This repository has been archived by the owner on Jan 2, 2025. It is now read-only.

Commit

Permalink
support nested class and module
Browse files Browse the repository at this point in the history
  • Loading branch information
euglena1215 committed Jun 10, 2024
1 parent f040bba commit f6519c6
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 40 deletions.
82 changes: 51 additions & 31 deletions lib/rbs_inline_data/parser.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,47 @@
require 'prism'
require 'pp'

module RbsInlineData
class Parser
class DefineDataVisitor < Prism::Visitor
# @rbs @calls: Array[Prism::Node]
# @rbs @definitions: Array[RbsInlineData::Parser::TypedDefinition]
# @rbs @surronding_class_or_module: Array[Symbol]

def initialize(calls)
@calls = calls
def initialize(definitions)
@definitions = definitions
@surronding_class_or_module = []
end

# @rbs override
def visit_class_node(node)
record_surrounding_class_or_module(node) { super }
end

# @rbs override
def visit_module_node(node)
record_surrounding_class_or_module(node) { super }
end

# @rbs override
def visit_constant_write_node(node)
@calls << node if define_data?(node)
if define_data?(node)
definition = extract_definition(node)
@definitions << definition if definition
end

super
end

private

#:: (Prism::Node) -> bool
#:: (Prism::ClassNode | Prism::ModuleNode) { (Prism::ClassNode | Prism::ModuleNode) -> void } -> void
def record_surrounding_class_or_module(node)
@surronding_class_or_module.push(node.constant_path.name)
yield(node)
ensure
@surronding_class_or_module.pop
end

#:: (Prism::ConstantWriteNode) -> bool
def define_data?(node)
node in {
value: Prism::CallNode[
Expand All @@ -30,6 +53,24 @@ def define_data?(node)
]
}
end

#:: (Prism::ConstantWriteNode) -> RbsInlineData::Parser::TypedDefinition?
def extract_definition(node)
source = node.slice
_, class_name, field_text = source.match(/\A([a-zA-Z]+) = Data\.define\(([\n\s\w\W]+)\)\z/).to_a
return nil if field_text.nil? || class_name.nil?

class_name = @surronding_class_or_module.join("::") + "::" + class_name

fields = field_text.split("\n").map(&:strip).map do |str|
str.match(/:(\w+), #:: ([\w\[\]]+)/)&.to_a
end.compact.map { |_, field_name, type| TypedField.new(field_name: field_name, type: type) }

TypedDefinition.new(
class_name: class_name,
fields: fields,
)
end
end

# @rbs skip
Expand Down Expand Up @@ -58,32 +99,11 @@ def self.parse(file)
#:: () -> Array[RbsInlineData::Parser::TypedDefinition]
def parse
result = Prism.parse_file(@file.to_s)
program_node = result.value

# @type var nodes: Array[Prism::Node]
nodes = []
result.value.accept(DefineDataVisitor.new(nodes))

nodes.map do |constant_write_node|
extract_definition(constant_write_node.slice)
end.compact
end

private

#:: (String) -> RbsInlineData::Parser::TypedDefinition?
def extract_definition(source)
_, class_name, field_text = source.match(/\A([a-zA-Z]+) = Data\.define\(([\n\s\w\W]+)\)\z/).to_a
return nil if field_text.nil?

fields = field_text.split("\n").map(&:strip).map do |str|
str.match(/:(\w+), #:: ([\w\[\]]+)/)&.to_a
end.compact.map { |_, field_name, type| TypedField.new(field_name: field_name, type: type) }

TypedDefinition.new(
class_name: class_name,
fields: fields,
)
# @type var definitions: Array[RbsInlineData::Parser::TypedDefinition]
definitions = []
result.value.accept(DefineDataVisitor.new(definitions))
definitions
end
end
end
27 changes: 18 additions & 9 deletions sig/generated/rbs_inline_data/parser.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,31 @@
module RbsInlineData
class Parser
class DefineDataVisitor < Prism::Visitor
@calls: Array[Prism::Node]
@definitions: Array[RbsInlineData::Parser::TypedDefinition]

def initialize: (untyped calls) -> untyped
@surronding_class_or_module: Array[Symbol]

def initialize: (untyped definitions) -> untyped

# @rbs override
def visit_class_node: ...

# @rbs override
def visit_module_node: ...

# @rbs override
def visit_constant_write_node: ...

private

# :: (Prism::Node) -> bool
def define_data?: (Prism::Node) -> bool
# :: (Prism::ClassNode | Prism::ModuleNode) { (Prism::ClassNode | Prism::ModuleNode) -> void } -> void
def record_surrounding_class_or_module: (Prism::ClassNode | Prism::ModuleNode) { (Prism::ClassNode | Prism::ModuleNode) -> void } -> void

# :: (Prism::ConstantWriteNode) -> bool
def define_data?: (Prism::ConstantWriteNode) -> bool

# :: (Prism::ConstantWriteNode) -> RbsInlineData::Parser::TypedDefinition?
def extract_definition: (Prism::ConstantWriteNode) -> RbsInlineData::Parser::TypedDefinition?
end

@file: Pathname
Expand All @@ -26,10 +40,5 @@ module RbsInlineData

# :: () -> Array[RbsInlineData::Parser::TypedDefinition]
def parse: () -> Array[RbsInlineData::Parser::TypedDefinition]

private

# :: (String) -> RbsInlineData::Parser::TypedDefinition?
def extract_definition: (String) -> RbsInlineData::Parser::TypedDefinition?
end
end
11 changes: 11 additions & 0 deletions sig/patch.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Prism
class ClassNode
def constant_path: () -> Prism::ConstantReadNode
| ...
end

class ModuleNode
def constant_path: () -> Prism::ConstantReadNode
| ...
end
end

0 comments on commit f6519c6

Please sign in to comment.