add glossary tree generator

pull/1/head
an 2018-12-29 20:56:29 -05:00
parent 2a712d6933
commit d9758b1879
2 changed files with 128 additions and 60 deletions

50
tools/treegen.rb Normal file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env ruby
## Distributed under the CC0 public domain license.
## Originally by Alison Sanderson.
## <https://creativecommons.org/publicdomain/zero/1.0/legalcode>
## TreeGen: Generates the glossary files for classes.
require "./tools/zsclasstree.rb"
DIR = ARGV.shift
VER = ARGV.shift
si = ClassSiphon.new DIR
f = open "glossary-classes.md", "wb"
f.puts <<_end_
# Classes
Here is a full tree of all classes in ZScript as of GZDoom #{VER}. There are #{si.classes.count + 1} classes total.
```
Object
_end_
si.print_classes f
f.puts <<_end_
```
<!-- EOF -->
_end_
f = open "glossary-structures.md", "wb"
f.puts <<_end_
# Structures
Here is a full list of all structures in ZScript as of GZDoom #{VER}. There are #{si.structs.count} structures total. Note that some of these are merely implementation details and should not be used in code.
```
Struct
_end_
si.print_structs f
f.puts <<_end_
```
<!-- EOF -->
_end_
## EOF

View File

@ -6,84 +6,102 @@
require 'set' require 'set'
$c = {} class ClassSiphon
$s = Set[] attr_reader :structs, :classes, :clstree, :strtree, :caps
$st = Set[]
$cap = {}
def procm m def initialize dirs
k = (m[1] or "Object").strip @subcls = {}
v = m[0].strip @classes = Set[]
@structs = Set[]
@caps = {}
$cap[k.upcase] = k; k = k.upcase Dir.glob dirs do |f| proc_file File.read f end
$cap[v.upcase] = v; v = v.upcase
return if k == v @clstree = make_tree "OBJECT", @subcls["OBJECT"]
$c[k] ? $c[k] << v : $c[k] = [v] @strtree = @structs.map {|type| [type, nil]}
raise "duplicate class #{v}" if $s === v
$s << v
end
def procs m @classes.freeze
cl = m[0] @structs.freeze
$cap[cl.upcase] = cl @clstree.freeze
$st << [cl.upcase] @strtree.freeze
end @caps.freeze
end
def procf fp def print_classes out, filter = []
t = fp.read print_tree out, filter, "OBJECT", clstree
t.gsub! /\/\*(?!\*\/)+\*\//m, '' end
t.gsub! /\/\/.+/, ''
sc = t.scan /^class\s*(\w*)[\s\w]+(?::\s+(\w*))?/
sc.each do |cl| procm cl end unless sc.empty?
sc = t.scan /^struct\s*(\w*)/
sc.each do |cl| procs cl end unless sc.empty?
end
dir = ARGV.shift def print_structs out, filter = []
print_tree out, filter, "STRUCT", strtree
end
Dir.glob dir do |item| private
procf open item, "rt" def add_class m
end ctype = m[0] .strip
cbase = (m[1] or "Object").strip
type = ctype.upcase
base = cbase.upcase
@caps[type] = ctype
@caps[base] = cbase
$s1 = Set[] if base != type
if @subcls[base]
@subcls[base] << type
else
@subcls[base] = [type]
end
raise "duplicate class #{type}" if @classes === type
@classes << type
end
end
def procc bcl, t def add_struct m
raise "duplicate class #{bcl}" if $s1 === bcl ctype = m[0]
$s1 << bcl type = ctype.upcase
return nil unless t @caps[type] = ctype
m = {} @structs << type
for cl in t do m[cl] = procc cl, $c[cl] end end
m
end
ft = procc("OBJECT", $c["OBJECT"]) def proc_file f
f.gsub! /\/\*(?!\*\/)+\*\//m, ""
f.gsub! /\/\/.+/, ""
f.scan(/^class\s*(\w*)[\s\w]+(?::\s+(\w*))?/).each {|m| add_class m}
f.scan(/^struct\s*(\w*)/) .each {|m| add_struct m}
end
def printc bcl, t, tab = "" # work our way down the subclasses recursively, making them into a tree structure
return unless t def make_tree bcl, subs
return if ARGV.include? bcl return nil unless subs
node = {}
subs.each do |cl| node[cl] = make_tree cl, @subcls[cl] end
node
end
t = t.map do |k, v| [k, v] end def print_tree out, filter, base, tree, tab = ""
t.sort! return if !tree or filter.include? base
for k, v in t tree = tree.map do |type, base| [type, base] end
l = k == t[-1][0] tree.sort!
s = tab.clone for type, base in tree
s << (l ? "" : "") last = type == tree[-1][0]
s << " " sep = last ? "" : " "
s << $cap[k] nxt = last ? " " : ""
puts s out.puts tab + sep + @caps[type]
printc k, v, l ? tab + " " : tab + "" print_tree out, filter, type, base, tab + nxt
end
end end
end end
raise "missing classes: #{($s - $s1).to_a}" unless ($s - $s1).empty? if __FILE__ == $0
in_dirs = ARGV.shift
filters = ARGV
puts "Object" si = ClassSiphon.new in_dirs
printc "OBJECT", ft puts "Object"; si.print_classes STDOUT, filters
puts "Struct"; si.print_structs STDOUT, filters
end
puts "Struct" ## EOF
printc "STRUCT", $st