Class: TerminologyParser::BigEndianParser

Inherits:
Object
  • Object
show all
Defined in:
lib/_appscript/terminology.rb

Direct Known Subclasses

LittleEndianParser

Constant Summary collapse

LegalFirst =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
LegalRest =
LegalFirst + '0123456789'
@@_name_cache =
{}
@@_reserved_keywords =

ersatz set

{}

Instance Method Summary collapse

Constructor Details

#initializeBigEndianParser

Returns a new instance of BigEndianParser.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/_appscript/terminology.rb', line 24

def initialize
  # terminology tables; order is significant where synonym definitions occur
  @commands = {}
  @properties = []
  @elements = []
  @classes = []
  @enumerators = []
  # use ersatz sets to record previously found definitions, and avoid adding duplicates to lists
  # (i.e. 'name+code not in <set>' is quicker than using 'name+code not in <list>')
  @_found_properties = {} # set
  @_found_elements = {} # set
  @_found_classes = {} # set
  @_found_enumerators = {} # set
  # ideally, aetes should define both singular and plural names for each class, but
  # some define only one or the other so we need to fill in any missing ones afterwards
  @_spare_class_names = {} # names by code
  @_found_class_codes = {} # set
  @_found_element_codes = {} # set
end

Instance Method Details

#_integerObject



44
45
46
47
48
# File 'lib/_appscript/terminology.rb', line 44

def _integer
  # Read a 2-byte integer.
  @_ptr += 2
  return @_str[@_ptr - 2, 2].unpack('S')[0]
end

#_lengthObject



59
60
61
# File 'lib/_appscript/terminology.rb', line 59

def _length
  return @_str[@_ptr]
end

#_nameObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/_appscript/terminology.rb', line 68

def _name
  # Read a MacRoman-encoded Pascal keyword string.
  count = _length
  @_ptr += 1 + count
  s = @_str[@_ptr - count, count]
  if not @@_name_cache.has_key?(s)
    legal = LegalFirst
    res = ''
    s.split(//).each do |c|
      if legal[c]
        res += c
      else
        case c
        when ' ', '-', '/'
          res += '_'
        when '&'
          res += 'and'
        else
          if res == ''
            res = '_'
          end
          res += "0x#{c.unpack('HXh')}"
        end
      end
      legal = LegalRest
    end
    if res[0, 3] == 'AS_' or @@_reserved_keywords.has_key?(res) or res[0, 1] == '_'
      res += '_'
    end
    @@_name_cache[s] = res
  end
  return @@_name_cache[s]
end

#_wordObject



50
51
52
53
54
# File 'lib/_appscript/terminology.rb', line 50

def _word
  # Read a 4-byte string (really a long, but represented as an 4-character 8-bit string for readability).
  @_ptr += 4
  return @_str[@_ptr - 4, 4] # big-endian
end

#parse(aetes) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/_appscript/terminology.rb', line 221

def parse(aetes)
  aetes.each do |aete|
    if aete.is_a?(AE::AEDesc) and aete.type == KAE::TypeAETE and aete.data != ''
      @_str = aete.data
      @_ptr = 6 # version, language, script integers
      _integer.times { parse_suite }
    end
  end
  # singular names are normally used in the classes table and plural names in the elements table. However, if an aete defines a singular name but not a plural name then the missing plural name is substituted with the singular name; and vice-versa if there's no singular equivalent for a plural name.
  missing_elements = @_found_class_codes.keys - @_found_element_codes.keys
  missing_classes = @_found_element_codes.keys - @_found_class_codes.keys
  missing_elements.each do |code|
    @elements.push([@_spare_class_names[code], code])
  end
  missing_classes.each do |code|
    @classes.push([@_spare_class_names[code], code])
  end
  return [@classes, @enumerators, @properties, @elements, @commands.values]
end

#parse_classObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/_appscript/terminology.rb', line 141

def parse_class
  name = _name
  @_ptr += @_ptr & 1 # align
  code = _word
  @_ptr += 1 + _length # description string
  @_ptr += @_ptr & 1 # align
  is_plural = false
  _integer.times do # properties
    propname = _name
    @_ptr += @_ptr & 1 # align
    propcode = _word
    @_ptr += 4 # datatype word
    @_ptr += 1 + _length # description string
    @_ptr += @_ptr & 1 # align
    flags = _integer
    if propcode != 'c@#^' # not a superclass definition (see kAEInheritedProperties)
      if flags & 1 == 1 # indicates class name is plural (see kAESpecialClassProperties)
        is_plural = true
      elsif not @_found_properties.has_key?(propname + propcode)
        @properties.push([propname, propcode]) # preserve ordering
        @_found_properties[propname + propcode] = nil # add to found set
      end
    end
  end
  _integer.times do # skip elements
    @_ptr += 4 # code word
    count = _integer
    @_ptr += 4 * count # reference forms
  end
  if is_plural
    if not @_found_elements.has_key?(name + code)
      @elements.push([name, code])
      @_found_elements[name + code] = nil # add to found set
      @_found_element_codes[code] = nil # add to found set
    end
  else
    if not @_found_classes.has_key?(name + code)
      @classes.push([name, code])
      @_found_classes[name + code] = nil # add to found set
      @_found_class_codes[code] = nil # add to found set
    end
  end
  @_spare_class_names[code] = name
end

#parse_commandObject



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/_appscript/terminology.rb', line 104

def parse_command
  name = _name
  @_ptr += 1 + _length # description string
  @_ptr += @_ptr & 1 # align
  code = _word + _word # event class + event id
  # skip result
  @_ptr += 4 # datatype word
  @_ptr += 1 + _length # description string
  @_ptr += @_ptr & 1 # align
  @_ptr += 2 # flags integer
  # skip direct parameter
  @_ptr += 4 # datatype word
  @_ptr += 1 + _length # description string
  @_ptr += @_ptr & 1 # align
  @_ptr += 2 # flags integer
  #
  current_command_args = []
  # Note: overlapping command definitions (e.g. InDesign) should be processed as follows:
  # - If their names and codes are the same, only the last definition is used; other definitions are ignored and will not compile.
  # - If their names are the same but their codes are different, only the first definition is used; other definitions are ignored and will not compile.
  # - If a dictionary-defined command has the same name but different code to a built-in definition, escape its name so it doesn't conflict with the default built-in definition.
  if not @commands.has_key?(name) or @commands[name][1] == code
    @commands[name] = [name, code, current_command_args]
  end
  # add labelled parameters
  _integer.times do
    parameter_name = _name
    @_ptr += @_ptr & 1 # align
    parameter_code = _word
    @_ptr += 4 # datatype word
    @_ptr += 1 + _length # description string
    @_ptr += @_ptr & 1 # align
    @_ptr += 2 # flags integer
    current_command_args.push([parameter_name, parameter_code])
  end
end

#parse_comparisonObject

comparison info isn’t used



186
187
188
189
190
191
192
# File 'lib/_appscript/terminology.rb', line 186

def parse_comparison # comparison info isn't used
  @_ptr += 1 + _length # name string
  @_ptr += @_ptr & 1 # align
  @_ptr += 4 # code word
  @_ptr += 1 + _length # description string
  @_ptr += @_ptr & 1 # align
end

#parse_enumerationObject



194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/_appscript/terminology.rb', line 194

def parse_enumeration
  @_ptr += 4 # code word
  _integer.times do # enumerators
    name = _name
    @_ptr += @_ptr & 1 # align
    code = _word
    @_ptr += 1 + _length # description string
    @_ptr += @_ptr & 1 # align
    if not @_found_enumerators.has_key?(name + code)
      @enumerators.push([name, code])
      @_found_enumerators[name + code] = nil # add to found set
    end
  end
end

#parse_suiteObject



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/_appscript/terminology.rb', line 209

def parse_suite
  @_ptr += 1 + _length # name string
  @_ptr += 1 + _length # description string
  @_ptr += @_ptr & 1 # align
  @_ptr += 4 # code word
  @_ptr += 4 # level, version integers
  _integer.times { parse_command }
  _integer.times { parse_class }
  _integer.times { parse_comparison }
  _integer.times { parse_enumeration }
end