scripts/generate_json_outfuncs.rb (83 lines of code) (raw):
#!/usr/bin/env ruby
# rubocop:disable Metrics/MethodLength, Style/WordArray, Metrics/LineLength, Style/Documentation, Style/PerlBackrefs, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
require 'bundler'
require 'json'
class Generator
def initialize
@nodetypes = JSON.parse(File.read('./srcdata/nodetypes.json'))
@struct_defs = JSON.parse(File.read('./srcdata/struct_defs.json'))
@typedefs = JSON.parse(File.read('./srcdata/typedefs.json'))
end
TYPE_OVERRIDES = {
['Query', 'queryId'] => :skip, # we intentionally do not print the queryId field
['RangeVar', 'catalogname'] => :skip, # presently not semantically meaningful
}
def generate_outmethods!
@outmethods = {}
['nodes/parsenodes', 'nodes/primnodes'].each do |group|
@struct_defs[group].each do |node_type, struct_def|
@outmethods[node_type] = format(" WRITE_NODE_TYPE(\"%s\");\n\n", node_type)
struct_def['fields'].each do |field_def|
name = field_def['name']
orig_type = field_def['c_type']
next unless name && orig_type
type = TYPE_OVERRIDES[[node_type, name]] || orig_type
if type == :skip || type == 'Expr'
# Ignore
elsif type == 'NodeTag'
# Nothing
elsif ['bool', 'long', 'char'].include?(type)
@outmethods[node_type] += format(" WRITE_%s_FIELD(%s);\n", type.upcase, name)
elsif ['int', 'int16', 'int32', 'AttrNumber'].include?(type)
@outmethods[node_type] += format(" WRITE_INT_FIELD(%s);\n", name)
elsif ['uint', 'uint16', 'uint32', 'Index', 'bits32', 'Oid'].include?(type)
@outmethods[node_type] += format(" WRITE_UINT_FIELD(%s);\n", name)
elsif type == 'char*'
@outmethods[node_type] += format(" WRITE_STRING_FIELD(%s);\n", name)
elsif ['float', 'double', 'Cost', 'Selectivity'].include?(type)
@outmethods[node_type] += format(" WRITE_FLOAT_FIELD(%s);\n", name)
elsif ['Bitmapset*', 'Relids'].include?(type)
@outmethods[node_type] += format(" WRITE_BITMAPSET_FIELD(%s);\n", name)
elsif ['Value'].include?(type)
@outmethods[node_type] += format(" WRITE_NODE_FIELD(%s);\n", name)
elsif ['CreateStmt'].include?(type)
# Special case where the node is embedded but with the wrong tag
@outmethods[node_type] += format(" WRITE_NODE_FIELD_WITH_TYPE(%s, %s);\n", name, type)
elsif type == 'Node*' || @nodetypes.include?(type[0..-2])
@outmethods[node_type] += format(" WRITE_NODE_PTR_FIELD(%s);\n", name)
elsif type.end_with?('*')
puts format('ERR: %s %s', name, type)
else # Enum
@outmethods[node_type] += format(" WRITE_ENUM_FIELD(%s);\n", name)
end
end
end
end
@typedefs.each do |typedef|
next unless @outmethods[typedef['source_type']]
@outmethods[typedef['new_type_name']] = @outmethods[typedef['source_type']]
end
end
IGNORE_LIST = [
'Expr', # Unclear why this isn't needed (FIXME)
'Value', # Special case
'Const', # Only needed in post-parse analysis (and it introduces Datums, which we can't output)
]
def generate!
generate_outmethods!
defs = ''
conds = ''
@nodetypes.each do |type|
next if IGNORE_LIST.include?(type)
outmethod = @outmethods[type]
next unless outmethod
defs += "static void\n"
defs += format("_out%s(StringInfo str, const %s *node)\n", type, type)
defs += "{\n"
defs += outmethod
defs += "}\n"
defs += "\n"
conds += format("case T_%s:\n", type)
conds += format(" _out%s(str, obj);\n", type)
conds += " break;\n"
end
File.write('./src/pg_query_json_defs.c', defs)
File.write('./src/pg_query_json_conds.c', conds)
end
end
Generator.new.generate!