Skip to content

Commit

Permalink
Add RBI and RBS templates for Prism
Browse files Browse the repository at this point in the history
Co-authored-by: Stan Lo <st0012@users.noreply.github.com>
  • Loading branch information
vinistock and st0012 committed Oct 26, 2023
1 parent 1274464 commit b62305f
Show file tree
Hide file tree
Showing 5 changed files with 509 additions and 28 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ a.out
/src/serialize.c
/src/token_type.c
/src/**/*.o
/sig
/rbi

compile_commands.json
.cache/
Expand Down
2 changes: 2 additions & 0 deletions prism.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Gem::Specification.new do |spec|
"src/util/pm_strpbrk.c",
"src/prism.c",
"prism.gemspec",
"sig/prism.rbs",
"rbi/prism.rbi"
]

spec.extensions = ["ext/prism/extconf.rb"]
Expand Down
283 changes: 283 additions & 0 deletions templates/rbi/prism.rbi.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
<%
def rbs_to_rbi(type)
if type.end_with?("?")
"T.nilable(#{rbs_to_rbi(type.delete_suffix("?"))})"
elsif type.start_with?("Array[")
"T::Array[#{rbs_to_rbi(type.delete_prefix("Array[").delete_suffix("]"))}]"
else
type
end
end
%>
module Prism
class ParseResult
sig { returns(ProgramNode) }
def value; end

sig { returns(T::Array[Comment]) }
def comments; end

sig { returns(T::Array[ParseError]) }
def errors; end

sig { returns(T::Array[ParseWarning]) }
def warnings; end

sig { returns(Source) }
def source; end
end

class ParseError
sig { returns(String) }
def message; end

sig { returns(Location) }
def location; end
end

class ParseWarning
sig { returns(String) }
def message; end

sig { returns(Location) }
def location; end
end

class Node
sig { returns(T::Array[T.nilable(Node)]) }
def child_nodes; end

sig { returns(Location) }
def location; end

sig { returns(String) }
def slice; end
end

class Comment
sig { returns(Location) }
def location; end
end

class Location
sig { params(source: Source, start_offset: Integer, length: Integer).void }
def initialize(source, start_offset, length); end

sig { returns(String) }
def slice; end

sig { returns(T::Array[Comment]) }
def comments; end

sig { params(options: T.untyped).returns(Location) }
def copy(**options); end

sig { returns(Integer) }
def start_offset; end

sig { returns(Integer) }
def end_offset; end

sig { returns(Integer) }
def start_line; end

sig { returns(Integer) }
def end_line; end

sig { returns(Integer) }
def start_column; end

sig { returns(Integer) }
def end_column; end
end

class Source
sig { params(source: String, offsets: T::Array[Integer]).void }
def initialize(source, offsets); end

sig { params(offset: Integer, length: Integer).returns(String) }
def slice(offset, length); end

sig { params(value: Integer).returns(Integer) }
def line(value); end

sig { params(value: Integer).returns(Integer) }
def line_offset(value); end

sig { params(value: Integer).returns(Integer) }
def column(value); end

sig { returns(String) }
def source; end

sig { returns(T::Array[Integer]) }
def offsets; end
end

class Token
sig { params(type: T.untyped, value: String, location: Location).void }
def initialize(type, value, location); end

sig { params(keys: T.untyped).returns(T.untyped) }
def deconstruct_keys(keys); end

sig { params(q: T.untyped).returns(T.untyped) }
def pretty_print(q); end

sig { params(other: T.untyped).returns(T::Boolean) }
def ==(other); end

sig { returns(T.untyped) }
def type; end

sig { returns(String) }
def value; end

sig { returns(Location) }
def location; end
end

class NodeInspector
sig { params(prefix: String).void }
def initialize(prefix); end

sig { returns(String) }
def prefix; end

sig { returns(String) }
def output; end

# Appends a line to the output with the current prefix.
sig { params(line: String).void }
def <<(line); end

# This generates a string that is used as the header of the inspect output
# for any given node.
sig { params(node: Node).returns(String) }
def header(node); end

# Generates a string that represents a list of nodes. It handles properly
# using the box drawing characters to make the output look nice.
sig { params(prefix: String, nodes: T::Array[Node]).returns(String) }
def list(prefix, nodes); end

# Generates a string that represents a location field on a node.
sig { params(value: Location).returns(String) }
def location(value); end

# Generates a string that represents a child node.
sig { params(node: Node, append: String).returns(String) }
def child_node(node, append); end

# Returns a new inspector that can be used to inspect a child node.
sig { params(append: String).returns(NodeInspector) }
def child_inspector(append); end

# Returns the output as a string.
sig { returns(String) }
def to_str; end
end

<%- nodes.each do |node| -%>
<%= "#{node.comment.split("\n").map { |line| line.empty? ? "#" : "# #{line}" }.join("\n ")}\n " if node.comment %>class <%= node.name -%> < Node
<%- node.fields.each do |field| -%>
sig { returns(<%= rbs_to_rbi(field.rbs_class) %>) }
attr_reader :<%= field.name %>
<%- end -%>
sig { params(<%= (node.fields.map { |field| "#{field.name}: #{rbs_to_rbi(field.rbs_class)}" } + ["location: Location"]).join(", ") %>).void }
def initialize(<%= (node.fields.map(&:name) + ["location"]).join(", ") %>); end

sig { params(visitor: Visitor).void }
def accept(visitor); end
<%- if node.newline == false -%>

def set_newline_flag(newline_marked); end
<%- elsif node.newline.is_a?(String) -%>

def set_newline_flag(newline_marked); end
<%- end -%>

sig { returns(T::Array[T.nilable(Node)]) }
def child_nodes; end

sig { returns(T::Array[T.nilable(Node)]) }
def deconstruct; end

sig { params(params: T.untyped).returns(<%= node.name %>) }
def copy(**params); end

sig { params(keys: T::Array[Symbol]).returns(T::Hash[Symbol, T.nilable(T.any(Node, T::Array[Node], String, Token, T::Array[Token], Location))]) }
def deconstruct_keys(keys); end
<%- node.fields.each do |field| -%>
<%- case field -%>
<%- when Prism::LocationField -%>
<%- raise unless field.name.end_with?("_loc") -%>
<%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%>

sig { returns(String) }
def <%= field.name.delete_suffix("_loc") %>; end
<%- when Prism::OptionalLocationField -%>
<%- raise unless field.name.end_with?("_loc") -%>
<%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%>

sig { returns(T.nilable(String)) }
def <%= field.name.delete_suffix("_loc") %>; end
<%- when Prism::FlagsField -%>
<%- flags.find { |flag| flag.name == field.kind }.tap { |flag| raise "Expected to find #{field.kind}" unless flag }.values.each do |value| -%>

sig { returns(T::Boolean) }
def <%= value.name.downcase %>?; end
<%- end -%>
<%- end -%>
<%- end -%>

sig { params(inspector: NodeInspector).returns(String) }
def inspect(inspector); end
end

<%- end -%>
<%- flags.each do |flag| -%>
module <%= flag.name %>
<%- flag.values.each_with_index do |value, index| -%>
# <%= value.comment %>
<%= value.name %> = T.let(1 << <%= index %>, Integer)
<%- end -%>
end

<%- end -%>
class BasicVisitor
sig { params(node: T.nilable(Node)).void }
def visit(node); end

sig { params(nodes: T::Array[T.nilable(Node)]).void }
def visit_all(nodes); end

sig { params(node: Node).void }
def visit_child_nodes(node); end
end

class Visitor < BasicVisitor
<%- nodes.each do |node| -%>
# Visit a <%= node.name %> node
sig { params(node: <%= node.name %>).void }
def visit_<%= node.human %>(node); end
<%= "\n" if node != nodes.last -%>
<%- end -%>
end

module DSL
private

# Create a new Location object
sig { params(source: T.nilable(Source), start_offset: Integer, length: Integer).returns(Location) }
def Location(source, start_offset, length); end

<%- nodes.each do |node| -%>
# Create a new <%= node.name %> node
sig { params(<%= (node.fields.map { |field| "#{field.name}: #{rbs_to_rbi(field.rbs_class)}" } + ["location: Location"]).join(", ") %>).returns(<%= node.name %>) }
def <%= node.name %>(<%= (node.fields.map(&:name) + ["location"]).join(", ") %>); end
<%- end -%>
end
end

0 comments on commit b62305f

Please sign in to comment.