require "indented_io" module FixtureFox class AstNode # Parent node. nil for the top-level Ast node attr_reader :parent # List of elements or members. Only non-empty for table and records nodes. # Assigned by the parser attr_reader :children # Type of this node or of the node referenced by this node. nil for the # top-level Ast node. Assigned by the analyzer attr_accessor :type def initialize(parent) @parent = parent @children = [] @parent.children << self if @parent end def dump puts self.to_s indent { children.each(&:dump) } end def inspect "#<#{self.class}:#{object_id} @type=#{type&.uid.inspect}>" # raise end end class Ast < AstNode alias_method :tables, :children attr_reader :file def initialize(file = nil) # file = nil is used in tests super(nil) @file = file end def to_s() "Ast" end end class AstTable < AstNode # Root table alias_method :ast, :parent alias_method :elements, :children # Initialized by the parser attr_reader :ident # Ident token attr_reader :schema # Ident token attr_reader :name # String attr_reader :dependent_materialized_views # Initialized by the analyzer def initialize(ast, schema, ident) super(ast) @ident = ident if @ident.value =~ /^(\w+)\.(\w+)$/ @schema = Ident.new(ident.file, ident.lineno, ident.initial_indent, ident.pos, $1) @name = $2 else @schema = schema @name = ident.value end end def to_s() "#{schema.value}.#{name}: AstTable" end end class AstElement < AstNode alias_method :table, :parent end # Make AstRecordElement and AstRecordMember act as they're instances of AstRecord module AstRecord; end class AstRecordElement < AstElement # Table record element include AstRecord alias_method :members, :children attr_reader :anchor # Anchor token. Can be nil attr_accessor :id # Assigned by the analyzer def initialize(table, anchor = nil) super(table) @anchor = anchor end def to_s() "AstRecordElement" + (anchor ? " #{anchor.litt}" : "") end end class AstReferenceElement < AstElement # Table reference element attr_reader :reference # attr_accessor :referenced_node # Initialized by the analyzer attr_accessor :referenced_anchor # Tuple of [table_uid, id] def initialize(table, reference) super(table) @reference = reference end def to_s() "AstReferenceElement #{reference.litt}" end end # A member of a record class AstMember < AstNode alias_method :record, :parent attr_reader :ident # Ident token attr_accessor :column # Initialized by the analyzer def initialize(record, ident) super(record) @ident = ident end end class AstTableMember < AstMember # Also root table alias_method :elements, :children # Initialized by the parser def to_s() "#{ident}: AstTableMember" end end class AstRecordMember < AstMember include AstRecord alias_method :members, :children attr_reader :anchor # Can be nil attr_accessor :id # Assigned by the analyzer def initialize(record, ident, anchor = nil) super(record, ident) @anchor = anchor end def to_s() "#{ident}: AstRecordMember" + (anchor ? " #{anchor.litt}" : "") end end class AstReferenceMember < AstMember attr_reader :reference # Reference token # attr_accessor :referenced_node # Initialized by the analyzer attr_accessor :referenced_anchor # Tuple of [table_uid, id] def initialize(record, ident, reference) super(record, ident) @reference = reference end def to_s() "#{ident}: AstReferenceMember #{reference.litt}" end end class AstFieldMember < AstMember attr_reader :value def initialize(record, ident, value) super(record, ident) @value = value end def to_s() "#{ident}: #{value}" end end end