diff options
Diffstat (limited to 'lib/prism/pack.rb')
-rw-r--r-- | lib/prism/pack.rb | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/lib/prism/pack.rb b/lib/prism/pack.rb new file mode 100644 index 0000000000..83f5569923 --- /dev/null +++ b/lib/prism/pack.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true + +module YARP + module Pack + %i[ + SPACE + COMMENT + INTEGER + UTF8 + BER + FLOAT + STRING_SPACE_PADDED + STRING_NULL_PADDED + STRING_NULL_TERMINATED + STRING_MSB + STRING_LSB + STRING_HEX_HIGH + STRING_HEX_LOW + STRING_UU + STRING_MIME + STRING_BASE64 + STRING_FIXED + STRING_POINTER + MOVE + BACK + NULL + + UNSIGNED + SIGNED + SIGNED_NA + + AGNOSTIC_ENDIAN + LITTLE_ENDIAN + BIG_ENDIAN + NATIVE_ENDIAN + ENDIAN_NA + + SIZE_SHORT + SIZE_INT + SIZE_LONG + SIZE_LONG_LONG + SIZE_8 + SIZE_16 + SIZE_32 + SIZE_64 + SIZE_P + SIZE_NA + + LENGTH_FIXED + LENGTH_MAX + LENGTH_RELATIVE + LENGTH_NA + ].each do |const| + const_set(const, const) + end + + class Directive + attr_reader :version, :variant, :source, :type, :signed, :endian, :size, :length_type, :length + + def initialize(version, variant, source, type, signed, endian, size, length_type, length) + @version = version + @variant = variant + @source = source + @type = type + @signed = signed + @endian = endian + @size = size + @length_type = length_type + @length = length + end + + ENDIAN_DESCRIPTIONS = { + AGNOSTIC_ENDIAN: 'agnostic', + LITTLE_ENDIAN: 'little-endian (VAX)', + BIG_ENDIAN: 'big-endian (network)', + NATIVE_ENDIAN: 'native-endian', + ENDIAN_NA: 'n/a' + } + + SIGNED_DESCRIPTIONS = { + UNSIGNED: 'unsigned', + SIGNED: 'signed', + SIGNED_NA: 'n/a' + } + + SIZE_DESCRIPTIONS = { + SIZE_SHORT: 'short', + SIZE_INT: 'int-width', + SIZE_LONG: 'long', + SIZE_LONG_LONG: 'long long', + SIZE_8: '8-bit', + SIZE_16: '16-bit', + SIZE_32: '32-bit', + SIZE_64: '64-bit', + SIZE_P: 'pointer-width' + } + + def describe + case type + when SPACE + 'whitespace' + when COMMENT + 'comment' + when INTEGER + if size == SIZE_8 + base = "#{SIGNED_DESCRIPTIONS[signed]} #{SIZE_DESCRIPTIONS[size]} integer" + else + base = "#{SIGNED_DESCRIPTIONS[signed]} #{SIZE_DESCRIPTIONS[size]} #{ENDIAN_DESCRIPTIONS[endian]} integer" + end + case length_type + when LENGTH_FIXED + if length > 1 + base + ", x#{length}" + else + base + end + when LENGTH_MAX + base + ', as many as possible' + end + when UTF8 + 'UTF-8 character' + when BER + 'BER-compressed integer' + when FLOAT + "#{SIZE_DESCRIPTIONS[size]} #{ENDIAN_DESCRIPTIONS[endian]} float" + when STRING_SPACE_PADDED + 'arbitrary binary string (space padded)' + when STRING_NULL_PADDED + 'arbitrary binary string (null padded, count is width)' + when STRING_NULL_TERMINATED + 'arbitrary binary string (null padded, count is width), except that null is added with *' + when STRING_MSB + 'bit string (MSB first)' + when STRING_LSB + 'bit string (LSB first)' + when STRING_HEX_HIGH + 'hex string (high nibble first)' + when STRING_HEX_LOW + 'hex string (low nibble first)' + when STRING_UU + 'UU-encoded string' + when STRING_MIME + 'quoted printable, MIME encoding' + when STRING_BASE64 + 'base64 encoded string' + when STRING_FIXED + 'pointer to a structure (fixed-length string)' + when STRING_POINTER + 'pointer to a null-terminated string' + when MOVE + 'move to absolute position' + when BACK + 'back up a byte' + when NULL + 'null byte' + else + raise + end + end + end + + class Format + attr_reader :directives, :encoding + + def initialize(directives, encoding) + @directives = directives + @encoding = encoding + end + + def describe + source_width = directives.map { |d| d.source.inspect.length }.max + directive_lines = directives.map do |directive| + if directive.type == SPACE + source = directive.source.inspect + else + source = directive.source + end + " #{source.ljust(source_width)} #{directive.describe}" + end + + (['Directives:'] + directive_lines + ['Encoding:', " #{encoding}"]).join("\n") + end + end + end +end |