зеркало из https://github.com/github/ruby.git
229 строки
5.9 KiB
Ruby
229 строки
5.9 KiB
Ruby
# frozen_string_literal: true
|
|
# typed: ignore
|
|
|
|
module Prism
|
|
# A parser for the pack template language.
|
|
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
|
|
|
|
# A directive in the pack template language.
|
|
class Directive
|
|
# A symbol representing the version of Ruby.
|
|
attr_reader :version
|
|
|
|
# A symbol representing whether or not we are packing or unpacking.
|
|
attr_reader :variant
|
|
|
|
# A byteslice of the source string that this directive represents.
|
|
attr_reader :source
|
|
|
|
# The type of the directive.
|
|
attr_reader :type
|
|
|
|
# The type of signedness of the directive.
|
|
attr_reader :signed
|
|
|
|
# The type of endianness of the directive.
|
|
attr_reader :endian
|
|
|
|
# The size of the directive.
|
|
attr_reader :size
|
|
|
|
# The length type of this directive (used for integers).
|
|
attr_reader :length_type
|
|
|
|
# The length of this directive (used for integers).
|
|
attr_reader :length
|
|
|
|
# Initialize a new directive with the given values.
|
|
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
|
|
|
|
# The descriptions of the various types of endianness.
|
|
ENDIAN_DESCRIPTIONS = {
|
|
AGNOSTIC_ENDIAN: "agnostic",
|
|
LITTLE_ENDIAN: "little-endian (VAX)",
|
|
BIG_ENDIAN: "big-endian (network)",
|
|
NATIVE_ENDIAN: "native-endian",
|
|
ENDIAN_NA: "n/a"
|
|
}
|
|
|
|
# The descriptions of the various types of signedness.
|
|
SIGNED_DESCRIPTIONS = {
|
|
UNSIGNED: "unsigned",
|
|
SIGNED: "signed",
|
|
SIGNED_NA: "n/a"
|
|
}
|
|
|
|
# The descriptions of the various types of sizes.
|
|
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"
|
|
}
|
|
|
|
# Provide a human-readable description of the directive.
|
|
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"
|
|
else
|
|
raise
|
|
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
|
|
|
|
# The result of parsing a pack template.
|
|
class Format
|
|
# A list of the directives in the template.
|
|
attr_reader :directives
|
|
|
|
# The encoding of the template.
|
|
attr_reader :encoding
|
|
|
|
# Create a new Format with the given directives and encoding.
|
|
def initialize(directives, encoding)
|
|
@directives = directives
|
|
@encoding = encoding
|
|
end
|
|
|
|
# Provide a human-readable description of the format.
|
|
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
|
|
# @type var source_width: Integer
|
|
" #{source.ljust(source_width)} #{directive.describe}"
|
|
end
|
|
|
|
(["Directives:"] + directive_lines + ["Encoding:", " #{encoding}"]).join("\n")
|
|
end
|
|
end
|
|
end
|
|
end
|