Skip to content

Instantly share code, notes, and snippets.

@masked-rpgmaker
Created March 16, 2020 00:07
Show Gist options
  • Save masked-rpgmaker/8003f792c371a01117171cd8db17050e to your computer and use it in GitHub Desktop.
Save masked-rpgmaker/8003f792c371a01117171cd8db17050e to your computer and use it in GitHub Desktop.
Gerador automático de documentação para scripts do RPG Maker VX Ace.
#==============================================================================
# RGSS Doc | v0.2.1 | por Masked
#
# para RPG Maker VX Ace
#------------------------------------------------------------------------------
# Gera automaticamente documentação para todos os scripts do jogo e salva em
# uma pasta pré-definida (./docs por padrão) no formato Markdown.
#==============================================================================
#==============================================================================
# ** RGSSDoc
#------------------------------------------------------------------------------
# Este módulo concentra as funções de interface com a geração de documentação
# e serve como namespace para os demais módulos do script.
#==============================================================================
module RGSSDoc
#--------------------------------------------------------------------------
# * Pasta onde serão salvos os documentos
#--------------------------------------------------------------------------
OUTPUT_FOLDER = './docs'
#--------------------------------------------------------------------------
# * Gera documentação para um script
# name : Nome do script
# code : Código do script
#--------------------------------------------------------------------------
def self.generate(name, code)
doc = ScriptDoc.new(name, code)
name.gsub!(/[\x00\/\\:\*\?\"<>\|]/, '')
doc.save_doc File.join(OUTPUT_FOLDER, "#{name}.md")
doc.save_reference_graph File.join(OUTPUT_FOLDER, "#{name}.dot")
end
end
#==============================================================================
# ** RGSSDoc::Lexer
#------------------------------------------------------------------------------
# Esta classe processa um código Ruby e divide em tokens.
#==============================================================================
class RGSSDoc::Lexer
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :index
#--------------------------------------------------------------------------
# * Constantes
#--------------------------------------------------------------------------
OPERATORS = %w[& && || | + < > << >> ! = ^ - / * ** %]
OPERATORS_EQUALS = OPERATORS.map { |op| op + '='}
MISC = %w[\[ \] \[\] { } ( ) <=> =~ :: ~ ? : , . ;]
SYMBOLS = MISC + OPERATORS + OPERATORS_EQUALS
#--------------------------------------------------------------------------
# * Inicialização do objeto
# code : Código
#--------------------------------------------------------------------------
def initialize(code)
@code = code
@word = false
reset
end
#--------------------------------------------------------------------------
# * Reseta a iteração
# index : Posição inicial da iteração
#--------------------------------------------------------------------------
def reset(index = 0)
@index = index
end
#--------------------------------------------------------------------------
# * Itera sobre os tokens no código
# start : Posição inicial no código
#--------------------------------------------------------------------------
def tokens(start = 0)
token = ''
TokenEnumerator.new(self) do |enum|
reset(start)
while @index < @code.size
next_token(enum)
end
enum.yield Token.new(@index, :eos, :eof)
end
end
#--------------------------------------------------------------------------
# * Limpa e envia a token para o enumerador
# start : Posição de início da token atual
# enum : Enumerador de tokens
# token : Token
#--------------------------------------------------------------------------
def flush(start, enum, token)
return false if token.empty?
@word = true
number = token =~ /^\d/
enum.yield(Token.new(start, number ? :number : :word, token)) if enum
true
end
#--------------------------------------------------------------------------
# * Avança um token no código
# enum : Enumerador de tokens
#--------------------------------------------------------------------------
def next_token(enum)
token = ''
start = nil
word = @word
@word = false
while @index < @code.size
char = @code[@index]
if char == '#'
return if flush(start, enum, token)
return comment(enum)
elsif char =~ /["']/
return if flush(start, enum, token)
return string(enum)
elsif char =~ /[\n;]/
return if flush(start, enum, token)
@index += 1
return enum.yield Token.new(@index - 1, :eos, char)
elsif char =~ /\s/
return if flush(start, enum, token)
elsif char == '/' and not word
@word = true
return string(enum)
elsif char == '%' and not word
@word = true
return percent_string(enum)
elsif (char =~ /[?!]/ and ((not word and token.empty?) or (@index > 0 and @code[@index - 1] =~ /\s/))) or char =~ /[\[\]\{\}\(\)=\-\+\/%\*\.;,\|&<>\:]/
return if flush(start, enum, token)
return symbol(enum)
elsif char =~ /[?!]/
token << char
@index += 1
return flush(start, enum, token)
else
start ||= @index
token << char
end
@index += 1
end
flush(start, enum, token)
end
#--------------------------------------------------------------------------
# * Processa uma token de comentário
# enum : Enumerador de tokens
#--------------------------------------------------------------------------
def comment(enum)
while @code[@index] == '#'
start = @index + 1
@index += 1 until @index == @code.size or @code[@index] == "\n"
if enum
enum.yield Token.new(start, :comment, @code[start...@index].strip)
end
@index += 1
@index += 1 while @code[@index] =~ /\s/
end
return if @index >= @code.size
@index -= 1 until @code[@index] == "\n"
end
#--------------------------------------------------------------------------
# * Avança a leitura do código para o fim de uma string
# end_mark : Símbolo de fim da string
# simple : Se a string é simples (sem interpolação)
#--------------------------------------------------------------------------
def find_string_end(end_mark, simple = false)
escape = false
@index += 1
until @index >= @code.size or (@code[@index] == end_mark and not escape)
if not simple and @code[@index, 2] == '#{' and not escape
skip_string_interpolation
next
else
escape = (not escape and @code[@index] == "\\")
end
@index += 1
end
end
#--------------------------------------------------------------------------
# * Processa uma token de string
# enum : Enumerador de tokens
#--------------------------------------------------------------------------
def string(enum)
mark = @code[@index]
start = @index + 1
find_string_end(mark, mark == "'")
@index += 1
return unless enum
enum.yield Token.new(start - 1, :string, @code[start...@index - 1])
end
#--------------------------------------------------------------------------
# * Processa uma token de expressão %...
# enum : Enumerador de tokens
#--------------------------------------------------------------------------
def percent_string(enum)
@index += 1
@index += 1 if @code[@index] =~ /[iqrswx]/
mark = @code[@index]
if not mark =~ /[\(\[\{\<]/
end_mark = mark
elsif mark == '('
end_mark = ')'
else
end_mark = (mark.ord + 2).chr
end
start = @index + 1
find_string_end(end_mark)
@index += 1
return unless enum
enum.yield Token.new(start - 1, :string, @code[start...@index - 1])
end
#--------------------------------------------------------------------------
# * Pula interpolação de string
#--------------------------------------------------------------------------
def skip_string_interpolation
@index += 2
depth = 1
enum = Enumerator.new do |enum|
next_token(enum) until depth.zero?
end
for token in enum
depth -= 1 if token.value == '}'
end
end
#--------------------------------------------------------------------------
# * Processa uma token de símbolo
# enum : Enumerador de tokens
#--------------------------------------------------------------------------
def symbol(enum)
token = @code[@index]
start = @index
@index += 1
while @index < @code.size and SYMBOLS.include?(token + @code[@index])
token << @code[@index]
@index += 1
end
@word = %w<) ] }>.include?(token)
return unless enum
enum.yield Token.new(start, :symbol, token)
end
end
#==============================================================================
# ** RGSSDoc::BasicToken
#------------------------------------------------------------------------------
# Esta classe representa tokens de modo geral.
#==============================================================================
class RGSSDoc::BasicToken
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :index
attr_reader :type
attr_reader :value
#--------------------------------------------------------------------------
# * Inicialização do objeto
# index : Posição da token no código
# type : Tipo de token
# value : Texto da token
#--------------------------------------------------------------------------
def initialize(index, type, value)
@index = index
@type = type
@value = value
end
#--------------------------------------------------------------------------
# * Converte a token em string
#--------------------------------------------------------------------------
alias inspect to_s
#--------------------------------------------------------------------------
# * Converte a token em string
#--------------------------------------------------------------------------
def to_s
"<#{self.class}:#{@type}@#{@index} #{@value.inspect}>"
end
end
#==============================================================================
# ** RGSSDoc::Lexer::Token
#------------------------------------------------------------------------------
# Esta classe representa tokens de código Ruby.
#==============================================================================
class RGSSDoc::Lexer::Token < RGSSDoc::BasicToken
#--------------------------------------------------------------------------
# * Constantes
#--------------------------------------------------------------------------
BLOCK_KEYWORDS = %w(begin case for class def do if module unless until while)
OTHER_KEYWORDS = %w(alias and break next defined? else elsif ensure rescue
false in nil not or redo retry super self then return
true undef yield end when)
#--------------------------------------------------------------------------
# * Verifica se a token é uma palavra
#--------------------------------------------------------------------------
def word?
type == :word
end
#--------------------------------------------------------------------------
# * Verifica se a token é um símbolo
#--------------------------------------------------------------------------
def symbol?
type == :symbol
end
#--------------------------------------------------------------------------
# * Verifica se a token é um fim de sequência
#--------------------------------------------------------------------------
def eos?
type == :eos
end
#--------------------------------------------------------------------------
# * Verifica se a token é um comentário
#--------------------------------------------------------------------------
def comment?
type == :comment
end
#--------------------------------------------------------------------------
# * Verifica se a token é um nome de variável
#--------------------------------------------------------------------------
def name?
word? and not keyword?
end
#--------------------------------------------------------------------------
# * Verifica se a token é uma constante
#--------------------------------------------------------------------------
def const?
name? and value[0] =~ /[A-Z]/
end
#--------------------------------------------------------------------------
# * Verifica se a token é uma variável de instância
#--------------------------------------------------------------------------
def instance_variable?
name? and value[0] == /@/
end
#--------------------------------------------------------------------------
# * Verifica se a token é uma variável global
#--------------------------------------------------------------------------
def global_variable?
name? and value[0] == /$/
end
#--------------------------------------------------------------------------
# * Verifica se a token é uma variável
#--------------------------------------------------------------------------
def variable?
name? and not const?
end
#--------------------------------------------------------------------------
# * Verifica se a token é uma palavra chave
#--------------------------------------------------------------------------
def keyword?
return true if block?
return OTHER_KEYWORDS.include?(value)
end
#--------------------------------------------------------------------------
# * Verifica se a token é uma keyword de bloco
#--------------------------------------------------------------------------
def block?(last = :eos)
return false unless word?
return false unless BLOCK_KEYWORDS.include?(value)
return false unless last == :eos or value == 'do'
true
end
end
#==============================================================================
# ** RGSSDoc::Lexer::TokenEnumerator
#------------------------------------------------------------------------------
# Esta classe implementa um enumerador de tokens. Inclui funções úteis para
# leitura das tokens de forma estruturada.
#==============================================================================
class RGSSDoc::Lexer::TokenEnumerator < Enumerator
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :lexer
#--------------------------------------------------------------------------
# * Inicialização do objeto
# lexer : Lexer associado ao enumerador
# block : Bloco de iteração
#--------------------------------------------------------------------------
def initialize(lexer, &block)
super(&block)
@lexer = lexer
end
#--------------------------------------------------------------------------
# * Avança a iteração até a próxima token do tipo EOS
#--------------------------------------------------------------------------
def skip_to_eos
token = nil
loop do
token = self.next
break if token.eos?
end
lexer.reset(token.index)
end
#--------------------------------------------------------------------------
# * Pula uma token caso ela exista, não faz nada se não
#--------------------------------------------------------------------------
def skip(type, value = nil)
token = self.next
return token unless token.type == type
return self.next if token.value == value or value.nil?
token
end
end
#==============================================================================
# ** RGSSDoc::Parser
#------------------------------------------------------------------------------
# Esta classe processa blocos de código Ruby para composição da documentação.
#==============================================================================
class RGSSDoc::Parser
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :lexer
#--------------------------------------------------------------------------
# * Inicialização do objeto
# code : Código
#--------------------------------------------------------------------------
def initialize(code)
@code = code
@lexer = RGSSDoc::Lexer.new(code)
end
#--------------------------------------------------------------------------
# * Reseta a iteração
#--------------------------------------------------------------------------
def reset
@lexer.reset
end
#--------------------------------------------------------------------------
# * Itera sobre as sentenças no código
# start : Posição inicial no código
#--------------------------------------------------------------------------
def statements(start = 0)
Enumerator.new do |enum|
comment = []
last = :eos
for token in @lexer.tokens(start)
if token.block?(last)
@lexer.reset(token.index)
skip_code_block
text = @code[start...@lexer.index]
enum.yield Statement.new(start, :block, text, comment.join("\r\n"))
comment.clear
start = @lexer.index
elsif token.comment? and (last == :eos or last == :comment)
comment << token.value
start = @lexer.index
elsif token.eos?
text = @code[start...@lexer.index] || ''
unless text.strip.empty?
enum.yield Statement.new(start, :simple, text, comment.join("\r\n"))
comment.clear
end
start = @lexer.index
end
last = token.type
end
end
end
#--------------------------------------------------------------------------
# * Lê um bloco de código terminado em end.
#--------------------------------------------------------------------------
def skip_code_block
depth = 0
last = :eos
has_while = false
for token in @lexer.tokens(@lexer.index)
has_while ||= ['while', 'until'].include?(token.value)
if token.word?
if token.block?(last) and not (token.value == 'do' and has_while)
depth += 1
elsif token.value == 'end'
depth -= 1
end
elsif token.eos?
has_while = false
end
last = token.type
break if depth.zero?
end
end
end
#==============================================================================
# ** RGSSDoc::Parser::Statement
#------------------------------------------------------------------------------
# Esta classe representa sentenças de código Ruby. Uma sentença é o menor
# bloco de código com significado, que pode ser executado independente do
# código ao seu redor sem apresentar erros de sintaxe.
#==============================================================================
class RGSSDoc::Parser::Statement < RGSSDoc::BasicToken
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :comment
#--------------------------------------------------------------------------
# * Inicialização do objeto
# index : Posição da token no código
# type : Tipo de token
# value : Texto da token
# comment : Comentário
#--------------------------------------------------------------------------
def initialize(index, type, value, comment)
super(index, type, value)
@comment = comment.force_encoding('utf-8')
end
#--------------------------------------------------------------------------
# * Verifica se a sentença é um bloco de código
#--------------------------------------------------------------------------
def block?
type == :block
end
#--------------------------------------------------------------------------
# * Primeira token da sentença
#--------------------------------------------------------------------------
def tokens
@lexer ||= RGSSDoc::Lexer.new(value)
@lexer.tokens
end
#--------------------------------------------------------------------------
# * Lê o cabeçalho da sentença.
#--------------------------------------------------------------------------
def head
return value unless type == :block
return @head if @head
tokens.skip_to_eos
@head = value[0...@lexer.index].strip
end
#--------------------------------------------------------------------------
# * Lê o código interno da sentença.
#--------------------------------------------------------------------------
def inner_code
return value unless type == :block
return @inner if @inner
head
start = @lexer.index
depth = 0
has_while = false
last = :eos
for token in @lexer.tokens(start)
if token.word?
has_while ||= token.value == 'while'
if token.block?(last) and not (token.value == 'do' and has_while)
depth += 1
elsif token.value == 'end'
depth -= 1
if depth == -1
@lexer.reset(token.index)
break
end
end
elsif token.eos?
has_while = false
end
last = token.type
end
@inner = value[start...@lexer.index]
end
end
#==============================================================================
# ** RGSSDoc::ScriptDoc
#------------------------------------------------------------------------------
# Esta classe representa um script sendo documentado.
#==============================================================================
class RGSSDoc::ScriptDoc
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :name
attr_reader :code
attr_reader :modules
#--------------------------------------------------------------------------
# * Inicialização do objeto
# name : Nome do script
# code : Código do script
#--------------------------------------------------------------------------
def initialize(name, code)
@name = name
@code = code
parse_modules
end
#--------------------------------------------------------------------------
# * Processa o código e extrai os módulos do script
#--------------------------------------------------------------------------
def parse_modules
@modules = []
for m in RGSSDoc::ModuleDoc.parse(code)
@modules << m
end
end
#--------------------------------------------------------------------------
# * Salva a documentação
# file : Nome do arquivo de destino
#--------------------------------------------------------------------------
def save_doc(file)
directory = File.dirname(file)
Dir.mkdir(directory) unless FileTest.directory?(directory)
File.open(file, 'w') do |file|
file.puts "# #{name}"
for m in modules
file.puts "## `#{m.name}` <small style='color: grey;'>(#{m.type})</small>"
file.puts
file.puts "Herda de `#{m.super_class}`\r\n" if m.super_class
static_methods = m.methods.select(&:static?)
unless static_methods.empty?
file.puts '### Métodos estáticos'
file.puts
file.puts '<table>'
file.puts '<thead>'
file.puts '<tr><th>Nome</th><th>Descrição</th><th>Argumentos</th></tr>'
file.puts '</thead>'
file.puts '<tbody>'
for method in static_methods
file.print '<tr>'
file.print "<td><code>#{method.name}</code></td>"
file.print "<td>#{method.description}</td>"
file.print '<td><ul>'
for a in method.arguments
file.print '<li>'
file.print "<code>#{a.name}</code>: #{a.description}"
file.print '</li>'
end
file.print '</ul></td>'
file.puts '</tr>'
end
file.puts '</tbody>'
file.puts '</table>'
file.puts
end
instance_methods = m.methods.reject(&:static?)
unless instance_methods.empty?
file.puts '### Métodos de instância'
file.puts
file.puts '<table>'
file.puts '<thead>'
file.puts '<tr><th>Nome</th><th>Descrição</th><th>Argumentos</th></tr>'
file.puts '</thead>'
file.puts '<tbody>'
for method in instance_methods
file.print '<tr>'
file.print "<td><code>#{method.name}</code></td>"
file.print "<td>#{method.description}</td>"
file.print '<td><ul>'
for a in method.arguments
file.print '<li>'
file.print "<code>#{a.name}</code>: #{a.description}"
file.print '</li>'
end
file.print '</ul></td>'
file.puts '</tr>'
end
file.puts '</tbody>'
file.puts '</table>'
file.puts
end
file.puts
file.puts '---'
end
end
end
#--------------------------------------------------------------------------
# * Salva o grafo de referências das classes do script
# file : Nome do arquivo de destino
#--------------------------------------------------------------------------
def save_reference_graph(file)
File.open(file, 'w') do |file|
file.puts "digraph \"#{name}\" {"
for m in @modules
for ref in m.references
file.puts "\t\"#{m.name}\" -> \"#{ref}\""
end
next if (m.super_class || '').empty?
file.print "\"#{m.name}\" -> \"#{m.super_class}\""
file.puts " [style=dotted label=super]"
end
file.puts "}"
end
end
end
#==============================================================================
# ** RGSSDoc::RubyEntityDoc
#------------------------------------------------------------------------------
# Esta classe representa uma entidade sendo documentada. Uma entidade é
# um módulo, classe ou método.
#==============================================================================
class RGSSDoc::RubyEntityDoc
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :statement
#--------------------------------------------------------------------------
# * Inicialização do objeto
# statement : Sentença de declaração do módulo
#--------------------------------------------------------------------------
def initialize(statement)
@statement = statement
end
#--------------------------------------------------------------------------
# * Código da entidade
#--------------------------------------------------------------------------
def code
statement.value
end
#--------------------------------------------------------------------------
# * Descrição da entidade
#--------------------------------------------------------------------------
def description
raise NotImplementedError
end
end
#==============================================================================
# ** RGSSDoc::ModuleDoc
#------------------------------------------------------------------------------
# Esta classe representa um módulo sendo documentado.
#==============================================================================
class RGSSDoc::ModuleDoc < RGSSDoc::RubyEntityDoc
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :submodules
attr_reader :methods
#--------------------------------------------------------------------------
# * Inicialização do objeto
# statement : Sentença de declaração do módulo
#--------------------------------------------------------------------------
def initialize(statement)
super
parse_submodules
parse_methods
end
#--------------------------------------------------------------------------
# * Nome do módulo
#--------------------------------------------------------------------------
def name
return @name if @name
tokens = statement.tokens
tokens.next
token = tokens.skip(:symbol, '<<')
parts = []
loop do
break unless token.word?
parts << token.value
token = tokens.skip(:symbol, '::')
end
@name = parts.join('::')
end
#--------------------------------------------------------------------------
# * Tipo do módulo (class/module)
#--------------------------------------------------------------------------
def type
statement.tokens.first.value
end
#--------------------------------------------------------------------------
# * Verifica se o módulo é uma classe
#--------------------------------------------------------------------------
def class?
type == 'class'
end
#--------------------------------------------------------------------------
# * Superclasse
#--------------------------------------------------------------------------
def super_class
return @super_class if @super_class
tokens = statement.tokens
loop do
token = tokens.next
return nil if token.eos?
break if token.symbol? and token.value == '<'
end
parts = []
loop do
token = tokens.next
next if token.symbol? and token.value == '::'
break unless token.word?
parts << token.value
end
@super_class = parts.join('::')
end
#--------------------------------------------------------------------------
# * Lista de referências do módulo
#--------------------------------------------------------------------------
def references
begin
@methods.flat_map do |method|
method.references(eval(self.name))
end.uniq
rescue NameError
[]
end
end
#--------------------------------------------------------------------------
# * Descrição do módulo
#--------------------------------------------------------------------------
def description
statement.comment =~ /\s*===+\s*\n\*\* \S+\s*\n---+(.+)\s*\n===+/m
return $1.strip.gsub(/\r?\n/, '') if $1
nil
end
#--------------------------------------------------------------------------
# * Processa o código do módulo e extrai os submódulos declarados nele
#--------------------------------------------------------------------------
def parse_submodules
@submodules = []
for m in RGSSDoc::ModuleDoc.parse(statement.inner_code)
@submodules << m
end
end
#--------------------------------------------------------------------------
# * Processa o código do módulo e extrai os métodos declarados nele
#--------------------------------------------------------------------------
def parse_methods
@methods = []
for m in RGSSDoc::MethodDoc.parse(statement.inner_code)
@methods << m
end
end
#--------------------------------------------------------------------------
# * Processa código e extrai os módulos contidos nele
# code : Código do script
#--------------------------------------------------------------------------
def self.parse(code)
Enumerator.new do |enum|
doc = RGSSDoc::Parser.new(code)
for statement in doc.statements
token = statement.tokens.first
next unless token.word?
next unless ['module', 'class'].include?(token.value)
enum.yield(RGSSDoc::ModuleDoc.new(statement))
end
end
end
end
#==============================================================================
# ** RGSSDoc::MethodDoc
#------------------------------------------------------------------------------
# Esta classe representa um método sendo documentado.
#==============================================================================
class RGSSDoc::MethodDoc < RGSSDoc::RubyEntityDoc
#--------------------------------------------------------------------------
# * Verifica se o método é estático
#--------------------------------------------------------------------------
def static?
tokens = statement.tokens
tokens.next
token = tokens.next
token.word? and token.value == 'self'
end
#--------------------------------------------------------------------------
# * Nome do método
#--------------------------------------------------------------------------
def name
tokens = statement.tokens
tokens.next
token = tokens.next
if token.word? and token.value == 'self'
tokens.next
token = tokens.next
end
token.value
end
#--------------------------------------------------------------------------
# * Lista de referências do método
#--------------------------------------------------------------------------
def references(scope = Kernel)
Enumerator.new do |enum|
parser = RGSSDoc::Parser.new(statement.inner_code)
for statement in parser.statements
tokens = statement.tokens
begin
loop do
token = tokens.next
next unless token.const?
name = token.value
token = tokens.next
while token.symbol? and token.value == '::'
token = tokens.next
next 2 unless token.const?
name << "::" << token.value
token = tokens.next
end
begin
x = scope.module_eval(name)
is_module = x.is_a?(Module)
next unless is_module
enum.yield(x.name)
rescue NameError => e
end
end
rescue StopIteration
end
end
end.to_a.uniq
end
#--------------------------------------------------------------------------
# * Descrição do método
#--------------------------------------------------------------------------
def description
statement.comment =~ /\s*---+\s*\n\* ([^\n]+)\s*.*\n---+/m
return $1.strip.gsub(/\r?\n/, '') if $1
nil
end
#--------------------------------------------------------------------------
# * Argumentos documentados do método
#--------------------------------------------------------------------------
def arguments
statement.comment =~ /\s*---+\s*\n\* [^\n]+\s*(.*)\n---+/m
return {} unless $1
doc = $1
arg = nil
args = []
for line in doc.split(/(?:\r?\n)+/)
if line =~ /(\S+)\s*:\s*(.+)/
args << arg if arg
arg = ArgumentDoc.new($1, $2.strip)
elsif arg
arg.description << ' ' + line.strip
end
end
args << arg if arg
return args
end
#--------------------------------------------------------------------------
# * Processa código e extrai os métodos contidos nele
# code : Código do script
#--------------------------------------------------------------------------
def self.parse(code)
Enumerator.new do |enum|
doc = RGSSDoc::Parser.new(code)
for statement in doc.statements
token = statement.tokens.first
next unless token.word?
next unless token.value == 'def'
enum.yield(RGSSDoc::MethodDoc.new(statement))
end
end
end
end
#==============================================================================
# ** RGSSDoc::MethodDoc::ArgumentDoc
#------------------------------------------------------------------------------
# Esta classe representa um argumento de um método sendo documentado.
#==============================================================================
class RGSSDoc::MethodDoc::ArgumentDoc
#--------------------------------------------------------------------------
# * Atributos
#--------------------------------------------------------------------------
attr_reader :name
attr_accessor :description
#--------------------------------------------------------------------------
# * Inicialização do objeto
# name : Nome do argumento
# description : Descrição do argumento
#--------------------------------------------------------------------------
def initialize(name, description)
@name = name
@description = description
end
#--------------------------------------------------------------------------
# * Converte o objeto em string
#--------------------------------------------------------------------------
def to_s
"#{name} : #{description}"
end
end
#==============================================================================
# ** Main
#------------------------------------------------------------------------------
# Lê cada script do jogo e gera documentação para ele de acordo.
#==============================================================================
for id, name, compressed, code in $RGSS_SCRIPTS
next if name.empty? or code.strip.empty?
RGSSDoc.generate(name, code)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment