class MODL::Parser::Parsed::ParsedPair

Class to represent a parsed grammar object

Attributes

array[RW]
final[RW]
key[RW]
key_lists[RW]
loaded[RW]
map[RW]
text[RW]
type[RW]
valueItem[RW]

Public Class Methods

new(global) click to toggle source
# File lib/modl/parser/parsed.rb, line 209
def initialize(global)
  @global = global
  @needs_defref = true
  @final = false
  @file_importer = FileImporter.instance
  @loaded = false
  @array = nil
  @map = nil
end

Public Instance Methods

enterModl_pair(ctx) click to toggle source
# File lib/modl/parser/parsed.rb, line 295
def enterModl_pair(ctx)
  @type = 'pair' # default the type to an ordinary pair

  ctx_string = ctx.STRING
  @key = ctx_string.to_s unless ctx_string.nil?
  ctx_quoted = ctx.QUOTED
  unless ctx_quoted.nil?
    @key = ctx_quoted.to_s
    @key = Sutil.toptail(@key) # remove the quotes
  end

  raise InterpreterError, 'Invalid key - null, true, or false keys are not allowed.' if @key.nil?

  if @key.include?('%') || @key.include?('`')
    @key, new_value = RefProcessor.deref @key, @global
    unless @key.is_a?(String)
      raise InterpreterError, "Error: '" + @key.to_s + "' is an invalid key." if new_value.nil?
      @key = new_value.is_a?(String) ? new_value : new_value.text
    end
    raise InterpreterError, "Error: '" + @key.to_s + "' should de-ref to a string." unless key.is_a?(String)
  end

  @final = true if @key.upcase == @key

  modl_array = ctx.modl_array
  modl_map = ctx.modl_map
  modl_value_item = ctx.modl_value_item

  if !modl_array.nil?
    @array = ParsedArray.new @global
    modl_array.enter_rule(@array)
  elsif !modl_map.nil?
    @map = ParsedMap.new @global
    modl_map.enter_rule(@map)
  elsif !modl_value_item.nil?
    @valueItem = ParsedValueItem.new @global
    modl_value_item.enter_rule(@valueItem)
  end

  set_pair_type
  raise InterpreterError, 'Invalid keyword: ' + @key if @type == 'pair' && @key.start_with?('*')

  validate_key if @type == 'pair' || @type == 'hidden'

  # Type-specific processing
  case @type
  when 'class'
    ClassExtractor.extract(self, @global)
  when 'id'
    extract_value
  when 'name'
    extract_value
  when 'superclass'
    extract_value
  when 'allow'
    extract_value
  when 'keylist'
    KeylistExtractor.extract(self, @valueItem) if @valueItem
    KeylistExtractor.extract(self, @array) if @array
  when 'version'
    extract_value

    raise InterpreterError, 'Invalid MODL version: ' + @valueItem.value.primitive.text if @valueItem.value.primitive.number.nil?
    raise InterpreterError, 'Invalid MODL version: ' + @valueItem.value.primitive.number.num.to_s if @valueItem.value.primitive.number.num.is_a? Float
    raise InterpreterError, 'Invalid MODL version: ' + @valueItem.value.primitive.number.num.to_s if @valueItem.value.primitive.number.num.zero?
    raise InterpreterError, 'MODL version should be on the first line if specified.' if @global.has_pairs?
    @global.syntax_version = @valueItem.value.primitive.number.num

  when 'method'
    MethodExtractor.extract(self, @global)
  when 'transform'
    extract_transform @valueItem
  when 'import'
    files = @valueItem.extract_hash if @valueItem
    files = @array.extract_hash if @array
    @file_importer.import_files files, @global unless @global.in_condition?
  when 'index'
    IndexExtractor.extract(self, @global)
  when 'hidden'
    extract_value
  else
    extract_value
  end

  return if @global.in_condition? # Don't store pairs in conditionals until we evaluate the conditions

  if @key.start_with? '_'
    k = Sutil.tail(@key)
    existing = @global.pair(k)
    raise InterpreterError, 'Already defined ' + k + ' as final.' if existing&.final && @type != "import"
    raise InterpreterError, 'Cannot load multiple files after *LOAD instruction' if existing&.final && @type == "import"

    @global.pair(k, self)
  end
  existing = @global.pair(@key)
  raise InterpreterError, 'Already defined ' + @key + ' as final.' if existing&.final && @type != "import"
  raise InterpreterError, 'Cannot load multiple files after *LOAD instruction' if existing&.final && @type == "import"

  @global.pair(@key, self)
end
extract_hash() click to toggle source

Convert this object to a simple hash ready for JSON.generate

# File lib/modl/parser/parsed.rb, line 274
def extract_hash

  value = @array.extract_hash if @array
  value = @valueItem.extract_hash if @valueItem
  value = @map.extract_hash if @map

  @text = value

  return if @type == 'index'
  return if @type == 'hidden'
  return if @type == 'version'
  return if @type == 'class'
  return if @type == 'method'
  return if @type == 'import'
  return if @type == 'allow'
  return if @type == 'expect'

  formatted_key = Substitutions.process UnicodeEscapes.process @key
  {formatted_key => @text}
end
find_property(key) click to toggle source
# File lib/modl/parser/parsed.rb, line 219
def find_property(key)
  return self if key == @key

  return @map.find_property(key) if @map
  return @array.find_property(key) if @array
  return @valueItem.find_property(key) if @valueItem
end
set_value(value) click to toggle source

Set the appropriate field base on the value type

# File lib/modl/parser/parsed.rb, line 228
def set_value(value)
  if value.is_a? Array
    @map = nil
    @array = ParsedArray.new @global
    @array.abstractArrayItems = []
    value.each do |item|
      array_item = ParsedArrayItem.new @global
      array_item.arrayValueItem = ParsedArrayValueItem.new @global
      array_item.arrayValueItem.primitive = ParsedPrimitive.new(@global)
      array_item.arrayValueItem.primitive.string = ParsedString.new(item)
      array_item.arrayValueItem.primitive.text = item
      @array.abstractArrayItems << array_item
    end
    @valueItem = nil
    @text = @array.extract_hash
    return
  elsif value.is_a?(ParsedPair)
    @map = value.map ? value.map : nil
    @array = value.array ? value.array : nil
    @valueItem = value.valueItem ? value.valueItem : nil
    return
  elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
    @map = nil
    @array = nil
    @valueItem = ParsedValueItem.new @global
    @valueItem.value = ParsedValue.new @global
    @valueItem.value.primitive = ParsedPrimitive.new(@global)
    if value
      @valueItem.value.primitive.trueVal = ParsedTrue.instance
    else
      @valueItem.value.primitive.falseVal = ParsedFalse.instance
    end
    @valueItem.value.primitive.text = value
    @valueItem.value.text = value
    @text = value
    return
  end
  value = value.extract_hash unless value.is_a?(String) || value.is_a?(Integer)
  @map = nil
  @array = nil
  @valueItem = ParsedValueItem.new @global
  @valueItem.value = ParsedString.new(value)
  @text = value
end

Private Instance Methods

extract_transform(item) click to toggle source
# File lib/modl/parser/parsed.rb, line 405
def extract_transform item
  @transform = item.value.primitive.string.string
end
extract_value() click to toggle source
# File lib/modl/parser/parsed.rb, line 398
def extract_value
  item = @valueItem
  @text = item.value.text if item.is_a?(ParsedValueItem) && item.value
  @text = item.valueItem.value.text if item.is_a?(ParsedPair)
  invoke_deref
end
invoke_deref() click to toggle source
# File lib/modl/parser/parsed.rb, line 421
def invoke_deref
  return unless @needs_defref && !@text.nil? && @text.is_a?(String) && @text.include?('%')

  @needs_defref = false
  @text, new_value = RefProcessor.deref @text, @global

  if new_value.is_a? ParsedMap
    @map = new_value
    @valueItem = nil
    @array = nil
  elsif new_value.is_a? ParsedArray
    @array = new_value
    @valueItem = nil
    @map = nil
  elsif new_value.is_a? ParsedValueItem
    @valueItem = new_value
  elsif new_value.nil?
    set_value @text
  elsif new_value.is_a? ParsedPair
    set_value @text if @text
    set_value new_value if @text.nil?
  elsif new_value.is_a? String
    set_value @text
  else
    set_value(new_value)
  end
end
set_pair_type() click to toggle source

Set the pair type if its a 'special' type

# File lib/modl/parser/parsed.rb, line 450
def set_pair_type
  @type = 'class' if @key == '*c' || @key == '*class'
  if @key == '*C' || @key == '*CLASS'
    @type = 'class'
    @key = @key.downcase
  end
  @type = 'id' if @key == '*i' || @key == '*id'
  @type = 'name' if @key == '*n' || @key == '*name'
  @type = 'name' if @key == '*N' || @key == '*NAME'
  @type = 'superclass' if @key == '*S' || @key == '*SUPERCLASS'
  @type = 'superclass' if @key == '*s' || @key == '*superclass'
  @type = 'keylist' if @key == '*a' || @key == '*assign'
  @type = 'version' if @key == '*V' || @key == '*VERSION'
  @type = 'method' if @key == '*m' || @key == '*method'
  @type = 'transform' if @key == '*t' || @key == '*transform'
  if @key == '*L' || @key == '*LOAD'
    @key = @key.downcase
    @type = 'import'
    if @array
      @global.freeze_max_files(@array.abstractArrayItems.length)
    elsif @valueItem&.value&.array
      @global.freeze_max_files(@valueItem&.value&.array.abstractArrayItems.length)
    elsif @valueItem&.value&.nbArray
      @global.freeze_max_files(@valueItem&.value&.nbArray.arrayItems.length)
    else
      @global.freeze_max_files(1)
    end
  end
  if @key == '*l' || @key == '*load'
    @type = 'import'
  end
  @type = 'index' if @key == '?'
  @type = 'hidden' if @key.start_with? '_'
  @type = 'allow' if @key.downcase == '*allow'
end
validate_key() click to toggle source
# File lib/modl/parser/parsed.rb, line 409
def validate_key
  invalid_chars = "£!$@-+'*#^&"
  invalid_chars.each_char do |c|
    next unless @key.include?(c)

    raise InterpreterError, 'Invalid key - "' + c + '" character not allowed: ' + @key
  end

  key = @key.start_with?('_') ? Sutil.tail(@key) : @key
  raise InterpreterError, 'Invalid key - "' + key + '" - entirely numeric keys are not allowed: ' + @key if key == key.to_i.to_s
end