diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..7ee1ffb --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,19 @@ +# Auto-generated by Cimas: Do not edit it manually! +# See https://github.com/metanorma/cimas +inherit_from: + - https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml + - .rubocop_todo.yml + +inherit_mode: + merge: + - Exclude + +# local repo-specific modifications +# ... +plugins: + - rubocop-rspec + - rubocop-performance + - rubocop-rake + +AllCops: + TargetRubyVersion: 3.0 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..1901c0c --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,323 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2026-07-27 10:07:24 UTC using RuboCop version 1.88.2. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 16 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_argument, with_fixed_indentation +Layout/ArgumentAlignment: + Exclude: + - 'lib/postscript/color.rb' + - 'lib/postscript/format_number.rb' + - 'lib/postscript/model/operators/control_flow.rb' + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'spec/postscript/color_spec.rb' + - 'spec/postscript/serializer_spec.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_element, with_fixed_indentation +Layout/ArrayAlignment: + Exclude: + - 'lib/postscript/source/lexer.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: IndentationWidth. +Layout/AssignmentIndentation: + Exclude: + - 'lib/postscript/source/ast_builder.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleAlignWith. +# SupportedStylesAlignWith: either, start_of_block, start_of_line +Layout/BlockAlignment: + Exclude: + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'spec/postscript/format_number_spec.rb' + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +Layout/BlockEndNewline: + Exclude: + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'spec/postscript/format_number_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: IndentationWidth. +Layout/ClosingParenthesisIndentation: + Exclude: + - 'spec/postscript/serializer_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses +Layout/FirstArgumentIndentation: + Exclude: + - 'spec/postscript/serializer_spec.rb' + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. +# SupportedHashRocketStyles: key, separator, table +# SupportedColonStyles: key, separator, table +# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit +Layout/HashAlignment: + Exclude: + - 'lib/postscript/model/operators/control_flow.rb' + - 'lib/postscript/source/lexer.rb' + - 'spec/postscript/serializer_spec.rb' + +# Offense count: 10 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns. +# SupportedStylesAlignWith: start_of_line, relative_to_receiver +Layout/IndentationWidth: + Exclude: + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'spec/postscript/format_number_spec.rb' + +# Offense count: 10 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: aligned, indented +Layout/LineEndStringConcatenationIndentation: + Exclude: + - 'lib/postscript/serializer.rb' + +# Offense count: 51 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Exclude: + - 'lib/postscript/color.rb' + - 'lib/postscript/format_number.rb' + - 'lib/postscript/model/operators/control_flow.rb' + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'lib/postscript/source/lexer.rb' + - 'lib/postscript/source/operand_stack.rb' + - 'postscript.gemspec' + - 'spec/postscript/color_spec.rb' + - 'spec/postscript/format_number_spec.rb' + - 'spec/postscript/model/operator_spec.rb' + - 'spec/postscript/serializer_spec.rb' + - 'spec/postscript/source/ast_builder_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Exclude: + - 'spec/postscript/serializer_spec.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: aligned, indented, indented_relative_to_receiver +Layout/MultilineMethodCallIndentation: + Exclude: + - 'lib/postscript/serializer.rb' + +# Offense count: 18 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowInHeredoc. +Layout/TrailingWhitespace: + Exclude: + - 'lib/postscript/color.rb' + - 'lib/postscript/format_number.rb' + - 'lib/postscript/model/operators/control_flow.rb' + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'lib/postscript/source/lexer.rb' + - 'spec/postscript/color_spec.rb' + - 'spec/postscript/serializer_spec.rb' + +# Offense count: 4 +# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. +Lint/DuplicateBranch: + Exclude: + - 'lib/postscript/color.rb' + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + +# Offense count: 87 +# Configuration parameters: AllowedParentClasses. +Lint/MissingSuper: + Exclude: + - 'lib/postscript/model/operators.rb' + - 'lib/postscript/model/operators/arithmetic.rb' + - 'lib/postscript/model/operators/boolean.rb' + - 'lib/postscript/model/operators/color.rb' + - 'lib/postscript/model/operators/container.rb' + - 'lib/postscript/model/operators/control_flow.rb' + - 'lib/postscript/model/operators/dictionary.rb' + - 'lib/postscript/model/operators/font.rb' + - 'lib/postscript/model/operators/graphics_state.rb' + - 'lib/postscript/model/operators/path.rb' + - 'lib/postscript/model/operators/stack.rb' + - 'lib/postscript/model/operators/transformations.rb' + +# Offense count: 1 +Lint/UselessConstantScoping: + Exclude: + - 'lib/postscript/source/lexer.rb' + +# Offense count: 12 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. +Metrics/AbcSize: + Exclude: + - 'lib/postscript/color.rb' + - 'lib/postscript/matrix.rb' + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'lib/postscript/source/lexer.rb' + +# Offense count: 7 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. +Metrics/CyclomaticComplexity: + Exclude: + - 'lib/postscript/color.rb' + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'lib/postscript/source/lexer.rb' + +# Offense count: 17 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Max: 36 + +# Offense count: 4 +# Configuration parameters: CountKeywordArgs, MaxOptionalParameters. +Metrics/ParameterLists: + Max: 10 + +# Offense count: 2 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. +Metrics/PerceivedComplexity: + Exclude: + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/lexer.rb' + +# Offense count: 97 +# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. +# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to +Naming/MethodParameterName: + Exclude: + - 'lib/postscript/color.rb' + - 'lib/postscript/matrix.rb' + - 'lib/postscript/model/operators/font.rb' + - 'lib/postscript/model/operators/path.rb' + - 'lib/postscript/model/operators/transformations.rb' + - 'lib/postscript/serializer.rb' + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: SafeMultiline. +Performance/DeletePrefix: + Exclude: + - 'lib/postscript/source/ast_builder.rb' + +# Offense count: 2 +RSpec/BeforeAfterAll: + Exclude: + - 'spec/postscript/model/operator_spec.rb' + - 'spec/postscript/model/operators_spec.rb' + +# Offense count: 28 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. +# SupportedStyles: described_class, explicit +RSpec/DescribedClass: + Exclude: + - 'spec/postscript/color_spec.rb' + - 'spec/postscript/format_number_spec.rb' + - 'spec/postscript/matrix_spec.rb' + - 'spec/postscript/model/operators_spec.rb' + +# Offense count: 6 +# Configuration parameters: CountAsOne. +RSpec/ExampleLength: + Max: 14 + +# Offense count: 1 +RSpec/IdenticalEqualityAssertion: + Exclude: + - 'spec/postscript/color_spec.rb' + +# Offense count: 26 +RSpec/MultipleExpectations: + Max: 6 + +# Offense count: 6 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. +# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces +# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object +# FunctionalMethods: let, let!, subject, watch +# AllowedMethods: lambda, proc, it +Style/BlockDelimiters: + Exclude: + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + - 'spec/postscript/format_number_spec.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, Mode, AllowedMethods, AllowedPatterns. +# SupportedStyles: annotated, template, unannotated +Style/FormatStringToken: + EnforcedStyle: unannotated + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/KeywordArgumentsMerging: + Exclude: + - 'lib/postscript/model/program.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +Style/MultilineIfModifier: + Exclude: + - 'lib/postscript/format_number.rb' + - 'lib/postscript/serializer.rb' + - 'lib/postscript/source/ast_builder.rb' + +# Offense count: 398 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Enabled: false + +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma +Style/TrailingCommaInArguments: + Exclude: + - 'lib/postscript/color.rb' + - 'spec/postscript/serializer_spec.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma +Style/TrailingCommaInHashLiteral: + Exclude: + - 'lib/postscript/matrix.rb' diff --git a/lib/postscript/color.rb b/lib/postscript/color.rb index 07b9c35..21642b5 100644 --- a/lib/postscript/color.rb +++ b/lib/postscript/color.rb @@ -10,7 +10,7 @@ class Color attr_reader :red, :green, :blue def self.clamp_byte(value) - Integer === value ? value.clamp(0, 255) : value.to_i.clamp(0, 255) + value.is_a?(Integer) ? value.clamp(0, 255) : value.to_i.clamp(0, 255) end def self.scale_unit_to_byte(value) @@ -58,9 +58,11 @@ def self.parse(text) hex = ::Regexp.last_match(1) new((hex[0] * 2).to_i(16), (hex[1] * 2).to_i(16), (hex[2] * 2).to_i(16)) when /\argb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/ - new(::Regexp.last_match(1).to_i, ::Regexp.last_match(2).to_i, ::Regexp.last_match(3).to_i) + new(::Regexp.last_match(1).to_i, ::Regexp.last_match(2).to_i, + ::Regexp.last_match(3).to_i) when /\argb\(\s*(\d+)\s+(\d+)\s+(\d+)/ - new(::Regexp.last_match(1).to_i, ::Regexp.last_match(2).to_i, ::Regexp.last_match(3).to_i) + new(::Regexp.last_match(1).to_i, ::Regexp.last_match(2).to_i, + ::Regexp.last_match(3).to_i) when "none", "transparent" nil else @@ -123,7 +125,7 @@ def eql?(other) "teal" => [0, 128, 128], "lime" => [0, 255, 0], "aqua" => [0, 255, 255], "fuchsia" => [255, 0, 255], "orange" => [255, 165, 0], "pink" => [255, 192, 203], - "brown" => [165, 42, 42], + "brown" => [165, 42, 42] }.freeze BLACK = Color.new(0, 0, 0).freeze diff --git a/lib/postscript/format_number.rb b/lib/postscript/format_number.rb index 70d5f6e..651a0bf 100644 --- a/lib/postscript/format_number.rb +++ b/lib/postscript/format_number.rb @@ -11,7 +11,10 @@ module FormatNumber module_function def call(value) - raise ArgumentError, "non-finite number: #{value.inspect}" unless value.finite? + unless value.finite? + raise ArgumentError, + "non-finite number: #{value.inspect}" + end normalized = value.zero? ? 0.0 : value return normalized.to_i.to_s if normalized == normalized.to_i diff --git a/lib/postscript/model/operator.rb b/lib/postscript/model/operator.rb index 9ec87ec..817cb5d 100644 --- a/lib/postscript/model/operator.rb +++ b/lib/postscript/model/operator.rb @@ -49,7 +49,7 @@ def operator? = true # output of +findfont+, +add+, +dup+). Carries no semantic value; # its purpose is to keep the parse-stack shape in sync with the # runtime stack so chained operators parse cleanly. - class Computed < ::Struct.new(:operator_keyword, keyword_init: true) + Computed = ::Struct.new(:operator_keyword, keyword_init: true) do def to_s "" end diff --git a/lib/postscript/model/operators/arithmetic.rb b/lib/postscript/model/operators/arithmetic.rb index a82f55e..d9277a9 100644 --- a/lib/postscript/model/operators/arithmetic.rb +++ b/lib/postscript/model/operators/arithmetic.rb @@ -15,6 +15,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -30,6 +31,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -45,6 +47,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -60,6 +63,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -75,6 +79,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -90,6 +95,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -100,10 +106,12 @@ def initialize(operand_a:, operand_b:) class Neg < Operator register_as "neg", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -112,10 +120,12 @@ def self.from_operands(stack) class Abs < Operator register_as "abs", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -124,10 +134,12 @@ def self.from_operands(stack) class Ceiling < Operator register_as "ceiling", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -136,10 +148,12 @@ def self.from_operands(stack) class Floor < Operator register_as "floor", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -148,10 +162,12 @@ def self.from_operands(stack) class Round < Operator register_as "round", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -160,10 +176,12 @@ def self.from_operands(stack) class Truncate < Operator register_as "truncate", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -172,10 +190,12 @@ def self.from_operands(stack) class Sqrt < Operator register_as "sqrt", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -189,6 +209,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -199,10 +220,12 @@ def initialize(operand_a:, operand_b:) class Cos < Operator register_as "cos", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -211,10 +234,12 @@ def self.from_operands(stack) class Sin < Operator register_as "sin", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -223,10 +248,12 @@ def self.from_operands(stack) class Ln < Operator register_as "ln", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -235,10 +262,12 @@ def self.from_operands(stack) class Log < Operator register_as "log", **UNARY_DEFAULT attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop_number) end @@ -252,6 +281,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b diff --git a/lib/postscript/model/operators/boolean.rb b/lib/postscript/model/operators/boolean.rb index 62c0ca2..806f853 100644 --- a/lib/postscript/model/operators/boolean.rb +++ b/lib/postscript/model/operators/boolean.rb @@ -23,6 +23,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -38,6 +39,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -53,6 +55,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -68,6 +71,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -83,6 +87,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -98,6 +103,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -113,6 +119,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -128,6 +135,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -143,6 +151,7 @@ def self.from_operands(stack) new(operand_a: a, operand_b: b) end attr_reader :operand_a, :operand_b + def initialize(operand_a:, operand_b:) @operand_a = operand_a @operand_b = operand_b @@ -153,10 +162,12 @@ def initialize(operand_a:, operand_b:) class Not < Operator register_as "not" attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop) end @@ -170,6 +181,7 @@ def self.from_operands(stack) new(operand: value, shift: shift) end attr_reader :operand, :shift + def initialize(operand:, shift:) @operand = operand @shift = shift diff --git a/lib/postscript/model/operators/color.rb b/lib/postscript/model/operators/color.rb index 3cf7469..35910a1 100644 --- a/lib/postscript/model/operators/color.rb +++ b/lib/postscript/model/operators/color.rb @@ -7,10 +7,12 @@ module Color class Setgray < Operator register_as "setgray" attr_reader :gray + def initialize(gray:) @gray = gray freeze end + def self.from_operands(stack) new(gray: stack.pop_number) end @@ -19,12 +21,14 @@ def self.from_operands(stack) class Setrgbcolor < Operator register_as "setrgbcolor" attr_reader :red, :green, :blue + def initialize(red:, green:, blue:) @red = red @green = green @blue = blue freeze end + def self.from_operands(stack) blue = stack.pop_number green = stack.pop_number @@ -36,6 +40,7 @@ def self.from_operands(stack) class Setcmykcolor < Operator register_as "setcmykcolor" attr_reader :cyan, :magenta, :yellow, :key + def initialize(cyan:, magenta:, yellow:, key:) @cyan = cyan @magenta = magenta @@ -43,6 +48,7 @@ def initialize(cyan:, magenta:, yellow:, key:) @key = key freeze end + def self.from_operands(stack) key = stack.pop_number yellow = stack.pop_number @@ -55,12 +61,14 @@ def self.from_operands(stack) class Sethsbcolor < Operator register_as "sethsbcolor" attr_reader :hue, :saturation, :brightness + def initialize(hue:, saturation:, brightness:) @hue = hue @saturation = saturation @brightness = brightness freeze end + def self.from_operands(stack) brightness = stack.pop_number saturation = stack.pop_number diff --git a/lib/postscript/model/operators/container.rb b/lib/postscript/model/operators/container.rb index 3a21f0e..be3f8cf 100644 --- a/lib/postscript/model/operators/container.rb +++ b/lib/postscript/model/operators/container.rb @@ -7,10 +7,12 @@ module Container class Length < Operator register_as "length", consumes: 1, produces: 1 attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop) end @@ -19,11 +21,13 @@ def self.from_operands(stack) class Get < Operator register_as "get", consumes: 2, produces: 1 attr_reader :operand, :key + def initialize(operand:, key:) @operand = operand @key = key freeze end + def self.from_operands(stack) key = stack.pop coll = stack.pop @@ -34,12 +38,14 @@ def self.from_operands(stack) class Put < Operator register_as "put", consumes: 3, produces: 0 attr_reader :operand, :key, :value + def initialize(operand:, key:, value:) @operand = operand @key = key @value = value freeze end + def self.from_operands(stack) value = stack.pop key = stack.pop @@ -51,12 +57,14 @@ def self.from_operands(stack) class Getinterval < Operator register_as "getinterval", consumes: 3, produces: 1 attr_reader :operand, :start, :count + def initialize(operand:, start:, count:) @operand = operand @start = start @count = count freeze end + def self.from_operands(stack) count = stack.pop_number.to_i start = stack.pop_number.to_i @@ -68,12 +76,14 @@ def self.from_operands(stack) class Putinterval < Operator register_as "putinterval", consumes: 3, produces: 0 attr_reader :operand, :start, :source + def initialize(operand:, start:, source:) @operand = operand @start = start @source = source freeze end + def self.from_operands(stack) source = stack.pop start = stack.pop_number.to_i @@ -85,11 +95,13 @@ def self.from_operands(stack) class Forall < Operator register_as "forall", consumes: 2, produces: 0 attr_reader :collection, :body + def initialize(collection:, body:) @collection = collection @body = body freeze end + def self.from_operands(stack) body = stack.pop coll = stack.pop @@ -100,11 +112,13 @@ def self.from_operands(stack) class Astore < Operator register_as "astore", consumes: 1, produces: 1 attr_reader :array, :length + def initialize(array:, length:) @array = array @length = length freeze end + def self.from_operands(stack) arr = stack.pop length = stack.pop_number.to_i @@ -115,11 +129,13 @@ def self.from_operands(stack) class Search < Operator register_as "search", consumes: 2, produces: 1 attr_reader :target, :pattern + def initialize(target:, pattern:) @target = target @pattern = pattern freeze end + def self.from_operands(stack) pattern = stack.pop target = stack.pop @@ -130,11 +146,13 @@ def self.from_operands(stack) class Anchorsearch < Operator register_as "anchorsearch", consumes: 2, produces: 1 attr_reader :target, :pattern + def initialize(target:, pattern:) @target = target @pattern = pattern freeze end + def self.from_operands(stack) pattern = stack.pop target = stack.pop @@ -145,10 +163,12 @@ def self.from_operands(stack) class Token < Operator register_as "token", consumes: 1, produces: 1 attr_reader :target + def initialize(target:) @target = target freeze end + def self.from_operands(stack) new(target: stack.pop) end @@ -157,10 +177,12 @@ def self.from_operands(stack) class String < Operator register_as "string", consumes: 1, produces: 1 attr_reader :count + def initialize(count:) @count = count freeze end + def self.from_operands(stack) new(count: stack.pop_number.to_i) end @@ -169,11 +191,13 @@ def self.from_operands(stack) class Cvs < Operator register_as "cvs", consumes: 2, produces: 1 attr_reader :value, :target + def initialize(value:, target:) @value = value @target = target freeze end + def self.from_operands(stack) target = stack.pop value = stack.pop diff --git a/lib/postscript/model/operators/control_flow.rb b/lib/postscript/model/operators/control_flow.rb index fd6fd36..f29f672 100644 --- a/lib/postscript/model/operators/control_flow.rb +++ b/lib/postscript/model/operators/control_flow.rb @@ -7,11 +7,13 @@ module ControlFlow class If < Operator register_as "if" attr_reader :condition, :body + def initialize(condition:, body:) @condition = condition @body = body freeze end + def self.from_operands(stack) body = stack.pop condition = stack.pop @@ -22,12 +24,14 @@ def self.from_operands(stack) class Ifelse < Operator register_as "ifelse" attr_reader :condition, :if_body, :else_body + def initialize(condition:, if_body:, else_body:) @condition = condition @if_body = if_body @else_body = else_body freeze end + def self.from_operands(stack) else_body = stack.pop if_body = stack.pop @@ -39,11 +43,13 @@ def self.from_operands(stack) class Repeat < Operator register_as "repeat" attr_reader :count, :body + def initialize(count:, body:) @count = count @body = body freeze end + def self.from_operands(stack) body = stack.pop count = stack.pop_number.to_i @@ -54,10 +60,12 @@ def self.from_operands(stack) class Loop < Operator register_as "loop" attr_reader :body + def initialize(body:) @body = body freeze end + def self.from_operands(stack) new(body: stack.pop) end @@ -66,6 +74,7 @@ def self.from_operands(stack) class For < Operator register_as "for" attr_reader :initial, :increment, :limit, :body + def initialize(initial:, increment:, limit:, body:) @initial = initial @increment = increment @@ -73,12 +82,14 @@ def initialize(initial:, increment:, limit:, body:) @body = body freeze end + def self.from_operands(stack) body = stack.pop limit = stack.pop_number increment = stack.pop_number initial = stack.pop_number - new(initial: initial, increment: increment, limit: limit, body: body) + new(initial: initial, increment: increment, limit: limit, + body: body) end end @@ -93,10 +104,12 @@ class Quit < Operator class Exec < Operator register_as "exec" attr_reader :operand + def initialize(operand:) @operand = operand freeze end + def self.from_operands(stack) new(operand: stack.pop) end @@ -105,10 +118,12 @@ def self.from_operands(stack) class Stopped < Operator register_as "stopped" attr_reader :body + def initialize(body:) @body = body freeze end + def self.from_operands(stack) new(body: stack.pop) end diff --git a/lib/postscript/model/operators/dictionary.rb b/lib/postscript/model/operators/dictionary.rb index ecc6b29..6831828 100644 --- a/lib/postscript/model/operators/dictionary.rb +++ b/lib/postscript/model/operators/dictionary.rb @@ -31,11 +31,13 @@ class End < Operator class Def < Operator register_as "def", consumes: 2, produces: 0 attr_reader :key, :value + def initialize(key:, value:) @key = key @value = value freeze end + def self.from_operands(stack) value = stack.pop key = stack.pop @@ -46,10 +48,12 @@ def self.from_operands(stack) class Load < Operator register_as "load", consumes: 1, produces: 1 attr_reader :key + def initialize(key:) @key = key freeze end + def self.from_operands(stack) new(key: stack.pop) end @@ -58,11 +62,13 @@ def self.from_operands(stack) class Store < Operator register_as "store", consumes: 2, produces: 0 attr_reader :key, :value + def initialize(key:, value:) @key = key @value = value freeze end + def self.from_operands(stack) value = stack.pop key = stack.pop @@ -78,6 +84,7 @@ def self.from_operands(stack) new(key: key, dict: dict) end attr_reader :key, :dict + def initialize(key:, dict:) @key = key @dict = dict @@ -107,6 +114,7 @@ def self.from_operands(stack) new(operand: stack.pop) end attr_reader :operand + def initialize(operand:) @operand = operand freeze diff --git a/lib/postscript/model/operators/font.rb b/lib/postscript/model/operators/font.rb index e34432b..da6594d 100644 --- a/lib/postscript/model/operators/font.rb +++ b/lib/postscript/model/operators/font.rb @@ -14,10 +14,12 @@ module Font class Findfont < Operator register_as "findfont", consumes: 1, produces: 1 attr_reader :name + def initialize(name:) @name = name freeze end + def self.from_operands(stack) new(name: stack.pop) end @@ -27,11 +29,13 @@ def self.from_operands(stack) class Scalefont < Operator register_as "scalefont", consumes: 2, produces: 1 attr_reader :font, :size + def initialize(font:, size:) @font = font @size = size freeze end + def self.from_operands(stack) size = stack.pop_number font = stack.pop @@ -43,10 +47,12 @@ def self.from_operands(stack) class Setfont < Operator register_as "setfont", consumes: 1, produces: 0 attr_reader :font + def initialize(font:) @font = font freeze end + def self.from_operands(stack) new(font: stack.pop) end @@ -56,10 +62,12 @@ def self.from_operands(stack) class Show < Operator register_as "show", consumes: 1, produces: 0 attr_reader :text + def initialize(text:) @text = text freeze end + def self.from_operands(stack) new(text: stack.pop) end @@ -70,12 +78,14 @@ def self.from_operands(stack) class Xyshow < Operator register_as "xyshow" attr_reader :text, :dx, :dy + def initialize(text:, dx:, dy:) @text = text @dx = dx @dy = dy freeze end + def self.from_operands(stack) dy = stack.pop dx = stack.pop @@ -90,10 +100,12 @@ def self.from_operands(stack) class Stringwidth < Operator register_as "stringwidth" attr_reader :text + def initialize(text:) @text = text freeze end + def self.from_operands(stack) new(text: stack.pop) end @@ -104,11 +116,13 @@ def self.from_operands(stack) class Charpath < Operator register_as "charpath" attr_reader :text, :stroke + def initialize(text:, stroke:) @text = text @stroke = stroke freeze end + def self.from_operands(stack) stroke = stack.pop text = stack.pop diff --git a/lib/postscript/model/operators/graphics_state.rb b/lib/postscript/model/operators/graphics_state.rb index 1513bf0..60e5308 100644 --- a/lib/postscript/model/operators/graphics_state.rb +++ b/lib/postscript/model/operators/graphics_state.rb @@ -19,10 +19,12 @@ class Grestoreall < Operator class Setlinewidth < Operator register_as "setlinewidth" attr_reader :width + def initialize(width:) @width = width freeze end + def self.from_operands(stack) new(width: stack.pop_number) end @@ -31,10 +33,12 @@ def self.from_operands(stack) class Setlinecap < Operator register_as "setlinecap" attr_reader :cap_code + def initialize(cap_code:) @cap_code = cap_code.to_i freeze end + def self.from_operands(stack) new(cap_code: stack.pop_number.to_i) end @@ -43,10 +47,12 @@ def self.from_operands(stack) class Setlinejoin < Operator register_as "setlinejoin" attr_reader :join_code + def initialize(join_code:) @join_code = join_code.to_i freeze end + def self.from_operands(stack) new(join_code: stack.pop_number.to_i) end @@ -55,10 +61,12 @@ def self.from_operands(stack) class Setmiterlimit < Operator register_as "setmiterlimit" attr_reader :limit + def initialize(limit:) @limit = limit freeze end + def self.from_operands(stack) new(limit: stack.pop_number) end @@ -67,11 +75,13 @@ def self.from_operands(stack) class Setdash < Operator register_as "setdash" attr_reader :pattern, :offset + def initialize(pattern:, offset:) @pattern = pattern @offset = offset freeze end + def self.from_operands(stack) offset = stack.pop_number pattern = stack.pop diff --git a/lib/postscript/model/operators/path.rb b/lib/postscript/model/operators/path.rb index d9e4bf3..b5ad493 100644 --- a/lib/postscript/model/operators/path.rb +++ b/lib/postscript/model/operators/path.rb @@ -11,11 +11,13 @@ class Newpath < Operator class Moveto < Operator register_as "moveto" attr_reader :x, :y + def initialize(x:, y:) @x = x @y = y freeze end + def self.from_operands(stack) y = stack.pop_number x = stack.pop_number @@ -26,11 +28,13 @@ def self.from_operands(stack) class Rmoveto < Operator register_as "rmoveto" attr_reader :dx, :dy + def initialize(dx:, dy:) @dx = dx @dy = dy freeze end + def self.from_operands(stack) dy = stack.pop_number dx = stack.pop_number @@ -41,11 +45,13 @@ def self.from_operands(stack) class Lineto < Operator register_as "lineto" attr_reader :x, :y + def initialize(x:, y:) @x = x @y = y freeze end + def self.from_operands(stack) y = stack.pop_number x = stack.pop_number @@ -56,11 +62,13 @@ def self.from_operands(stack) class Rlineto < Operator register_as "rlineto" attr_reader :dx, :dy + def initialize(dx:, dy:) @dx = dx @dy = dy freeze end + def self.from_operands(stack) dy = stack.pop_number dx = stack.pop_number @@ -71,6 +79,7 @@ def self.from_operands(stack) class Curveto < Operator register_as "curveto" attr_reader :x1, :y1, :x2, :y2, :x3, :y3 + def initialize(x1:, y1:, x2:, y2:, x3:, y3:) @x1 = x1 @y1 = y1 @@ -80,6 +89,7 @@ def initialize(x1:, y1:, x2:, y2:, x3:, y3:) @y3 = y3 freeze end + def self.from_operands(stack) y3 = stack.pop_number x3 = stack.pop_number @@ -94,6 +104,7 @@ def self.from_operands(stack) class Rcurveto < Operator register_as "rcurveto" attr_reader :dx1, :dy1, :dx2, :dy2, :dx3, :dy3 + def initialize(dx1:, dy1:, dx2:, dy2:, dx3:, dy3:) @dx1 = dx1 @dy1 = dy1 @@ -103,6 +114,7 @@ def initialize(dx1:, dy1:, dx2:, dy2:, dx3:, dy3:) @dy3 = dy3 freeze end + def self.from_operands(stack) dy3 = stack.pop_number dx3 = stack.pop_number @@ -117,6 +129,7 @@ def self.from_operands(stack) class Arc < Operator register_as "arc" attr_reader :x, :y, :radius, :angle1, :angle2 + def initialize(x:, y:, radius:, angle1:, angle2:) @x = x @y = y @@ -125,6 +138,7 @@ def initialize(x:, y:, radius:, angle1:, angle2:) @angle2 = angle2 freeze end + def self.from_operands(stack) angle2 = stack.pop_number angle1 = stack.pop_number @@ -138,6 +152,7 @@ def self.from_operands(stack) class Arcn < Operator register_as "arcn" attr_reader :x, :y, :radius, :angle1, :angle2 + def initialize(x:, y:, radius:, angle1:, angle2:) @x = x @y = y @@ -146,6 +161,7 @@ def initialize(x:, y:, radius:, angle1:, angle2:) @angle2 = angle2 freeze end + def self.from_operands(stack) angle2 = stack.pop_number angle1 = stack.pop_number diff --git a/lib/postscript/model/operators/stack.rb b/lib/postscript/model/operators/stack.rb index f5f7e76..d2f6981 100644 --- a/lib/postscript/model/operators/stack.rb +++ b/lib/postscript/model/operators/stack.rb @@ -35,10 +35,12 @@ def self.from_operands(stack) class Index < Operator register_as "index", consumes: 1, produces: 1 attr_reader :index + def initialize(index:) @index = index freeze end + def self.from_operands(stack) new(index: stack.pop_number.to_i) end @@ -47,11 +49,13 @@ def self.from_operands(stack) class Roll < Operator register_as "roll", consumes: 2, produces: 0 attr_reader :count, :positions + def initialize(count:, positions:) @count = count @positions = positions freeze end + def self.from_operands(stack) positions = stack.pop_number.to_i count = stack.pop_number.to_i diff --git a/lib/postscript/model/operators/transformations.rb b/lib/postscript/model/operators/transformations.rb index 3606734..e3ac93f 100644 --- a/lib/postscript/model/operators/transformations.rb +++ b/lib/postscript/model/operators/transformations.rb @@ -7,11 +7,13 @@ module Transformations class Translate < Operator register_as "translate" attr_reader :tx, :ty + def initialize(tx:, ty:) @tx = tx @ty = ty freeze end + def self.from_operands(stack) ty = stack.pop_number tx = stack.pop_number @@ -22,11 +24,13 @@ def self.from_operands(stack) class Scale < Operator register_as "scale" attr_reader :sx, :sy + def initialize(sx:, sy:) @sx = sx @sy = sy freeze end + def self.from_operands(stack) sy = stack.pop_number sx = stack.pop_number @@ -37,10 +41,12 @@ def self.from_operands(stack) class Rotate < Operator register_as "rotate" attr_reader :angle + def initialize(angle:) @angle = angle freeze end + def self.from_operands(stack) new(angle: stack.pop_number) end @@ -49,10 +55,12 @@ def self.from_operands(stack) class Concat < Operator register_as "concat" attr_reader :matrix + def initialize(matrix:) @matrix = matrix freeze end + def self.from_operands(stack) new(matrix: stack.pop) end @@ -69,10 +77,12 @@ class Currentmatrix < Operator class Setmatrix < Operator register_as "setmatrix" attr_reader :matrix + def initialize(matrix:) @matrix = matrix freeze end + def self.from_operands(stack) new(matrix: stack.pop) end diff --git a/lib/postscript/model/program.rb b/lib/postscript/model/program.rb index 6d705d6..6e12bed 100644 --- a/lib/postscript/model/program.rb +++ b/lib/postscript/model/program.rb @@ -51,7 +51,7 @@ def initialize(bounding_box: nil, hires_bounding_box: nil, title: nil, end def with(**overrides) - Header.new(**to_h.merge(overrides)) + Header.new(**to_h, **overrides) end def to_h @@ -59,7 +59,7 @@ def to_h bounding_box: bounding_box, hires_bounding_box: hires_bounding_box, title: title, creator: creator, creation_date: creation_date, pages: pages, page_count: page_count, epsf: epsf, - language_level: language_level, custom: custom, + language_level: language_level, custom: custom } end end diff --git a/lib/postscript/serializer.rb b/lib/postscript/serializer.rb index 141512e..248f4fc 100644 --- a/lib/postscript/serializer.rb +++ b/lib/postscript/serializer.rb @@ -19,7 +19,10 @@ def self.call(program, eps: false, creator: DEFAULT_CREATOR, **_options) attr_reader :program, :eps, :creator def initialize(program, eps:, creator:) - raise ArgumentError, "program must be a Model::Program" unless program.is_a?(Model::Program) + unless program.is_a?(Model::Program) + raise ArgumentError, + "program must be a Model::Program" + end @program = program @eps = eps @@ -45,11 +48,11 @@ def emit_header(buffer) buffer << "%%Creator: #{creator}\n" bbox = program.header.bounding_box if bbox && !bbox.empty? - buffer << "%%BoundingBox: " << bbox.map { |v| FormatNumber.call(v) }.join(" ") << "\n" - end - if program.header.title - buffer << "%%Title: #{program.header.title}\n" + buffer << "%%BoundingBox: " << bbox.map { |v| + FormatNumber.call(v) + }.join(" ") << "\n" end + buffer << "%%Title: #{program.header.title}\n" if program.header.title buffer << "%%LanguageLevel: #{DEFAULT_LANGUAGE_LEVEL}\n" buffer << "%%EndComments\n" end @@ -73,7 +76,10 @@ def emit_statement(statement, buffer) buffer << "<" << statement.value << ">\n" when Model::Literals::ArrayLiteral buffer << "[ " - statement.elements.each { |e| emit_inline(e, buffer); buffer << " " } + statement.elements.each do |e| + emit_inline(e, buffer) + buffer << " " + end buffer << "]\n" when Model::Literals::Procedure buffer << "{\n" @@ -140,26 +146,26 @@ def emit_rlineto(op, buf) def emit_curveto(op, buf) buf << "#{FormatNumber.call(op.x1)} #{FormatNumber.call(op.y1)} " \ - "#{FormatNumber.call(op.x2)} #{FormatNumber.call(op.y2)} " \ - "#{FormatNumber.call(op.x3)} #{FormatNumber.call(op.y3)} curveto\n" + "#{FormatNumber.call(op.x2)} #{FormatNumber.call(op.y2)} " \ + "#{FormatNumber.call(op.x3)} #{FormatNumber.call(op.y3)} curveto\n" end def emit_rcurveto(op, buf) buf << "#{FormatNumber.call(op.dx1)} #{FormatNumber.call(op.dy1)} " \ - "#{FormatNumber.call(op.dx2)} #{FormatNumber.call(op.dy2)} " \ - "#{FormatNumber.call(op.dx3)} #{FormatNumber.call(op.dy3)} rcurveto\n" + "#{FormatNumber.call(op.dx2)} #{FormatNumber.call(op.dy2)} " \ + "#{FormatNumber.call(op.dx3)} #{FormatNumber.call(op.dy3)} rcurveto\n" end def emit_arc(op, buf) buf << "#{FormatNumber.call(op.x)} #{FormatNumber.call(op.y)} " \ - "#{FormatNumber.call(op.radius)} " \ - "#{FormatNumber.call(op.angle1)} #{FormatNumber.call(op.angle2)} arc\n" + "#{FormatNumber.call(op.radius)} " \ + "#{FormatNumber.call(op.angle1)} #{FormatNumber.call(op.angle2)} arc\n" end def emit_arcn(op, buf) buf << "#{FormatNumber.call(op.x)} #{FormatNumber.call(op.y)} " \ - "#{FormatNumber.call(op.radius)} " \ - "#{FormatNumber.call(op.angle1)} #{FormatNumber.call(op.angle2)} arcn\n" + "#{FormatNumber.call(op.radius)} " \ + "#{FormatNumber.call(op.angle1)} #{FormatNumber.call(op.angle2)} arcn\n" end def emit_closepath(_op, buf) @@ -188,12 +194,12 @@ def emit_setgray(op, buf) def emit_setcmykcolor(op, buf) buf << "#{FormatNumber.call(op.cyan)} #{FormatNumber.call(op.magenta)} " \ - "#{FormatNumber.call(op.yellow)} #{FormatNumber.call(op.key)} setcmykcolor\n" + "#{FormatNumber.call(op.yellow)} #{FormatNumber.call(op.key)} setcmykcolor\n" end def emit_sethsbcolor(op, buf) buf << "#{FormatNumber.call(op.hue)} #{FormatNumber.call(op.saturation)} " \ - "#{FormatNumber.call(op.brightness)} sethsbcolor\n" + "#{FormatNumber.call(op.brightness)} sethsbcolor\n" end # Graphics state @@ -220,7 +226,9 @@ def emit_setmiterlimit(op, buf) def emit_setdash(op, buf) pattern_str = case op.pattern - when Array then "[#{op.pattern.map { |v| FormatNumber.call(v.to_f) }.join(' ')}]" + when Array then "[#{op.pattern.map do |v| + FormatNumber.call(v.to_f) + end.join(' ')}]" when Numeric then FormatNumber.call(op.pattern.to_f) else "[]" end @@ -247,10 +255,10 @@ def emit_concat(op, buf) end def escape_string(text) - escaped = text.to_s.gsub(/[\(\)\\]/) { |c| "\\#{c}" } - .gsub("\n", "\\n") - .gsub("\r", "\\r") - .gsub("\t", "\\t") + escaped = text.to_s.gsub(/[()\\]/) { |c| "\\#{c}" } + .gsub("\n", '\\n') + .gsub("\r", '\\r') + .gsub("\t", '\\t') "(#{escaped})" end diff --git a/lib/postscript/source/ast_builder.rb b/lib/postscript/source/ast_builder.rb index 07fb82f..7a3047a 100644 --- a/lib/postscript/source/ast_builder.rb +++ b/lib/postscript/source/ast_builder.rb @@ -33,7 +33,10 @@ def build private def consume_until(end_index, terminators: [], depth: 0) - raise RecursionLimitError, "procedure depth exceeded #{MAX_PROC_DEPTH}" if depth > MAX_PROC_DEPTH + if depth > MAX_PROC_DEPTH + raise RecursionLimitError, + "procedure depth exceeded #{MAX_PROC_DEPTH}" + end i = 0 collected = [] @@ -65,7 +68,8 @@ def consume_token(token, collected) append_statement(node, collected) 1 when :name - node = Model::Literals::Name.new(token.value, literal: token.literal || false) + node = Model::Literals::Name.new(token.value, + literal: token.literal || false) @stack.push(node) append_statement(node, collected) 1 @@ -101,11 +105,12 @@ def token_position(token) def build_number(token) raw = token.value value = - if raw =~ /\A[-+]?\d+\z/ + case raw + when /\A[-+]?\d+\z/ raw.to_i - elsif raw =~ /\A[-+]?\d+\.\d+\z/ || raw =~ /\A[-+]?(\.\d+|\d+\.)\z/ + when /\A[-+]?\d+\.\d+\z/, /\A[-+]?(\.\d+|\d+\.)\z/ raw.to_f - elsif raw =~ /\A[-+]?(\d+\.?\d*|\.\d+)[eE][-+]?\d+\z/ + when /\A[-+]?(\d+\.?\d*|\.\d+)[eE][-+]?\d+\z/ raw.to_f else raw.to_f @@ -129,9 +134,12 @@ def consume_procedure(after:, collected:) def consume_array(after:, collected:) start_index = find_index(after) - body_tokens, end_index = match_until(start_index + 1, :array_close, :array_open) + body_tokens, end_index = match_until(start_index + 1, :array_close, + :array_open) body_nodes = sub_build(body_tokens) - array = Model::Literals::ArrayLiteral.new(body_nodes.select { |n| literal?(n) }) + array = Model::Literals::ArrayLiteral.new(body_nodes.select do |n| + literal?(n) + end) @stack.push(array) append_statement(array, collected) end_index - start_index + 1 @@ -139,7 +147,8 @@ def consume_array(after:, collected:) def consume_dict(after:, collected:) start_index = find_index(after) - body_tokens, end_index = match_until(start_index + 1, :dict_close, :dict_open) + body_tokens, end_index = match_until(start_index + 1, :dict_close, + :dict_open) entries = {} pending_key = nil sub_nodes = sub_build(body_tokens) @@ -221,13 +230,11 @@ def consume_operator(token, collected) keyword = token.value # Resolve user-defined names to InvokeProcedure. - if (proc_value = lookup_user_definition(keyword)) - if proc_value.is_a?(Model::Literals::Procedure) - inv = Model::InvokeProcedure.new(name: keyword, procedure: proc_value) - @body << inv - collected << inv - return 1 - end + if (proc_value = lookup_user_definition(keyword)) && proc_value.is_a?(Model::Literals::Procedure) + inv = Model::InvokeProcedure.new(name: keyword, procedure: proc_value) + @body << inv + collected << inv + return 1 end operator_class = Model::Operators[keyword] @@ -262,7 +269,8 @@ def consume_operator(token, collected) def apply_side_effects(operator) case operator when Model::Operators::Dictionary::Def - @dict_stack.last[operator.key.to_s.sub(/\A\//, "")] = operator.value + @dict_stack.last[operator.key.to_s.delete_prefix("/")] = + operator.value end end @@ -278,14 +286,14 @@ def apply_dsc(token) case text when /\ABoundingBox:\s*(-?[\d.eE+-]+)\s+(-?[\d.eE+-]+)\s+(-?[\d.eE+-]+)\s+(-?[\d.eE+-]+)\z/ @header = @header.with(bounding_box: [ - ::Regexp.last_match(1).to_f, ::Regexp.last_match(2).to_f, - ::Regexp.last_match(3).to_f, ::Regexp.last_match(4).to_f, - ]) + ::Regexp.last_match(1).to_f, ::Regexp.last_match(2).to_f, + ::Regexp.last_match(3).to_f, ::Regexp.last_match(4).to_f + ]) when /\AHiResBoundingBox:\s*(-?[\d.eE+-]+)\s+(-?[\d.eE+-]+)\s+(-?[\d.eE+-]+)\s+(-?[\d.eE+-]+)\z/ @header = @header.with(hires_bounding_box: [ - ::Regexp.last_match(1).to_f, ::Regexp.last_match(2).to_f, - ::Regexp.last_match(3).to_f, ::Regexp.last_match(4).to_f, - ]) + ::Regexp.last_match(1).to_f, ::Regexp.last_match(2).to_f, + ::Regexp.last_match(3).to_f, ::Regexp.last_match(4).to_f + ]) when /\ATitle:\s*(.+)\z/ @header = @header.with(title: ::Regexp.last_match(1)) when /\ACreator:\s*(.+)\z/ diff --git a/lib/postscript/source/lexer.rb b/lib/postscript/source/lexer.rb index 629d61e..c780f56 100644 --- a/lib/postscript/source/lexer.rb +++ b/lib/postscript/source/lexer.rb @@ -121,18 +121,15 @@ def scan_dsc_body start_pos = @position start_line = @line start_col = @column - until eos? || @source.getbyte(@position) == 10 - advance - end + advance until eos? || @source.getbyte(@position) == 10 text = @source.byteslice(start_pos, @position - start_pos) - @tokens << Model::Token.new(:dsc, text.chomp, line: start_line, column: start_col) + @tokens << Model::Token.new(:dsc, text.chomp, line: start_line, + column: start_col) @state = :top end def scan_comment_body - until eos? || @source.getbyte(@position) == 10 - advance - end + advance until eos? || @source.getbyte(@position) == 10 @state = :top end @@ -148,16 +145,14 @@ def start_string def scan_string_body until eos? byte = @source.getbyte(@position) + advance case byte when 92 # backslash - advance handle_escape when 40 # ( - advance @string_depth += 1 @string_start[:bytes] << "(" when 41 # ) - advance @string_depth -= 1 if @string_depth.zero? emit_string @@ -165,12 +160,12 @@ def scan_string_body end @string_start[:bytes] << ")" else - advance @string_start[:bytes] << byte.chr end end raise LexError.new("unterminated string literal", - source_position: [@string_start[:line], @string_start[:column]]) + source_position: [@string_start[:line], + @string_start[:column]]) end def handle_escape @@ -193,17 +188,17 @@ def handle_escape else char end - if replacement.is_a?(String) - advance - @string_start[:bytes] << replacement - end + return unless replacement.is_a?(String) + + advance + @string_start[:bytes] << replacement end def consume_octal_escape digits = +"" 3.times do byte = peek - break unless byte && (byte >= 48 && byte <= 55) # 0..7 + break unless byte && byte >= 48 && byte <= 55 # 0..7 digits << byte.chr advance @@ -244,7 +239,8 @@ def scan_hexstring_body end end raise LexError.new("unterminated hex string literal", - source_position: [@hex_start[:line], @hex_start[:column]]) + source_position: [@hex_start[:line], + @hex_start[:column]]) end def emit_hexstring @@ -266,7 +262,7 @@ def maybe_close_dict end end - def start_name_with_slash(prefix) + def start_name_with_slash(_prefix) start_line = @line start_col = @column advance @@ -278,7 +274,8 @@ def start_name_with_slash(prefix) bytes << byte.chr advance end - @tokens << Model::Token.new(:name, bytes, line: start_line, column: start_col, literal: true) + @tokens << Model::Token.new(:name, bytes, line: start_line, + column: start_col, literal: true) end def delimiter_byte?(byte) @@ -312,7 +309,7 @@ def emit_word(bytes, line, col) @tokens << Model::Token.new(type, bytes, line: line, column: col) end - NUMBER_RE = /\A[-+]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][-+]?\d+)?\z/.freeze + NUMBER_RE = /\A[-+]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][-+]?\d+)?\z/ def emit(type, value) @tokens << Model::Token.new(type, value, line: @line, column: @column) diff --git a/spec/postscript/color_spec.rb b/spec/postscript/color_spec.rb index ab77ab1..c6b6c41 100644 --- a/spec/postscript/color_spec.rb +++ b/spec/postscript/color_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Postscript::Color do describe ".rgb" do it "converts PS [0,1] floats to integer [0,255]" do - c = Postscript::Color.rgb(1, 0.5, 0) + c = described_class.rgb(1, 0.5, 0) expect(c.red).to eq(255) expect(c.green).to eq(128) expect(c.blue).to eq(0) @@ -14,7 +14,7 @@ describe ".gray" do it "produces equal channels" do - c = Postscript::Color.gray(0.5) + c = described_class.gray(0.5) expect(c.red).to eq(c.green) expect(c.green).to eq(c.blue) expect(c.gray?).to be true @@ -23,48 +23,50 @@ describe ".cmyk" do it "converts CMYK to RGB" do - c = Postscript::Color.cmyk(0, 0, 0, 0) + c = described_class.cmyk(0, 0, 0, 0) expect(c).to eq(Postscript::Color::WHITE) - c2 = Postscript::Color.cmyk(1, 1, 1, 0) + c2 = described_class.cmyk(1, 1, 1, 0) expect(c2).to eq(Postscript::Color::BLACK) end end describe ".parse" do it "parses #rrggbb" do - expect(Postscript::Color.parse("#aabbcc").to_svg).to eq("#aabbcc") + expect(described_class.parse("#aabbcc").to_svg).to eq("#aabbcc") end it "parses #rgb (3-digit)" do - expect(Postscript::Color.parse("#abc").to_svg).to eq("#aabbcc") + expect(described_class.parse("#abc").to_svg).to eq("#aabbcc") end it "parses named colors" do - expect(Postscript::Color.parse("red")).to eq(Postscript::Color.rgb(1, 0, 0)) + expect(described_class.parse("red")).to eq(described_class.rgb(1, 0, + 0)) end it "returns nil for none" do - expect(Postscript::Color.parse("none")).to be_nil + expect(described_class.parse("none")).to be_nil end end describe "#to_ps_rgb" do it "emits space-separated [0,1] floats" do - c = Postscript::Color.rgb(1, 0, 0) + c = described_class.rgb(1, 0, 0) expect(c.to_ps_rgb).to eq("1 0 0") end end describe "#to_svg" do it "emits #rrggbb" do - expect(Postscript::Color.rgb(0.5, 0.5, 0.5).to_svg).to eq("#808080") + expect(described_class.rgb(0.5, 0.5, 0.5).to_svg).to eq("#808080") end end describe "value equality" do it "treats equal colors as ==" do - expect(Postscript::Color.rgb(1, 0, 0)).to eq(Postscript::Color.rgb(1, 0, 0)) + expect(described_class.rgb(1, 0, + 0)).to eq(described_class.rgb(1, 0, 0)) end end end diff --git a/spec/postscript/format_number_spec.rb b/spec/postscript/format_number_spec.rb index 3d14981..5b1c799 100644 --- a/spec/postscript/format_number_spec.rb +++ b/spec/postscript/format_number_spec.rb @@ -4,21 +4,25 @@ RSpec.describe Postscript::FormatNumber do it "formats integers without decimals" do - expect(Postscript::FormatNumber.call(10)).to eq("10") + expect(described_class.call(10)).to eq("10") end it "formats floats to 4 decimals with trailing zeros stripped" do - expect(Postscript::FormatNumber.call(3.14)).to eq("3.14") - expect(Postscript::FormatNumber.call(3.14159265)).to eq("3.1416") - expect(Postscript::FormatNumber.call(2.5)).to eq("2.5") + expect(described_class.call(3.14)).to eq("3.14") + expect(described_class.call(3.14159265)).to eq("3.1416") + expect(described_class.call(2.5)).to eq("2.5") end it "normalizes negative zero" do - expect(Postscript::FormatNumber.call(-0.0)).to eq("0") + expect(described_class.call(-0.0)).to eq("0") end it "raises on non-finite numbers" do - expect { Postscript::FormatNumber.call(Float::NAN) }.to raise_error(ArgumentError) - expect { Postscript::FormatNumber.call(Float::INFINITY) }.to raise_error(ArgumentError) + expect do + described_class.call(Float::NAN) + end.to raise_error(ArgumentError) + expect do + described_class.call(Float::INFINITY) + end.to raise_error(ArgumentError) end end diff --git a/spec/postscript/matrix_spec.rb b/spec/postscript/matrix_spec.rb index bde4088..56dd345 100644 --- a/spec/postscript/matrix_spec.rb +++ b/spec/postscript/matrix_spec.rb @@ -5,24 +5,24 @@ RSpec.describe Postscript::Matrix do describe "#identity?" do it "is true for a default matrix" do - expect(Postscript::Matrix.new).to be_identity + expect(described_class.new).to be_identity end it "is false after translate" do - expect(Postscript::Matrix.new.translate(10, 20)).not_to be_identity + expect(described_class.new.translate(10, 20)).not_to be_identity end end describe "#translate / #scale / #rotate" do it "compose via multiply" do - m = Postscript::Matrix.new.translate(5, 5).scale(2, 2) + m = described_class.new.translate(5, 5).scale(2, 2) p = m.apply_point(1, 1) expect(p[:x]).to eq(7) expect(p[:y]).to eq(7) end it "rotate by 90deg maps (1,0) to (0,1)" do - m = Postscript::Matrix.new.rotate(90) + m = described_class.new.rotate(90) p = m.apply_point(1, 0) expect(p[:x]).to be_within(1e-9).of(0) expect(p[:y]).to be_within(1e-9).of(1) @@ -31,7 +31,7 @@ describe "#invert" do it "round-trips through invert" do - m = Postscript::Matrix.new.translate(5, 7).scale(2, 3).rotate(45) + m = described_class.new.translate(5, 7).scale(2, 3).rotate(45) inv = m.invert composed = m.multiply(inv) expect(composed.apply_point(10, 20)[:x]).to be_within(1e-9).of(10) diff --git a/spec/postscript/model/operators_spec.rb b/spec/postscript/model/operators_spec.rb index df37c79..f509d68 100644 --- a/spec/postscript/model/operators_spec.rb +++ b/spec/postscript/model/operators_spec.rb @@ -3,18 +3,18 @@ require "postscript" RSpec.describe Postscript::Model::Operators do - before(:all) { Postscript::Model::Operators.load_all! } + before(:all) { described_class.load_all! } describe "registry" do it "registers moveto, lineto, stroke, etc." do %w[moveto lineto curveto closepath stroke fill setrgbcolor setgray gsave grestore translate scale rotate].each do |kw| - expect(Postscript::Model::Operators[kw]).not_to be_nil + expect(described_class[kw]).not_to be_nil end end it "returns nil for unknown keywords" do - expect(Postscript::Model::Operators["nonsense_12345"]).to be_nil + expect(described_class["nonsense_12345"]).to be_nil end end @@ -33,8 +33,12 @@ it "pops operands in reverse-source order" do stack = Postscript::Source::OperandStack.new # x1 y1 x2 y2 x3 y3 - stack.push(1); stack.push(2); stack.push(3) - stack.push(4); stack.push(5); stack.push(6) + stack.push(1) + stack.push(2) + stack.push(3) + stack.push(4) + stack.push(5) + stack.push(6) op = described_class.from_operands(stack) expect(op.x1).to eq(1) expect(op.y1).to eq(2) @@ -58,15 +62,15 @@ end end -describe Postscript::Model::Operators::Arithmetic::Sub do - it "pops operand_b first then operand_a from the parse stack" do - stack = Postscript::Source::OperandStack.new - stack.push(5) # operand_a (source order) - stack.push(3) # operand_b - op = described_class.from_operands(stack) - expect(op.operand_a).to eq(5) - expect(op.operand_b).to eq(3) - expect(stack.empty?).to be true + describe Postscript::Model::Operators::Arithmetic::Sub do + it "pops operand_b first then operand_a from the parse stack" do + stack = Postscript::Source::OperandStack.new + stack.push(5) # operand_a (source order) + stack.push(3) # operand_b + op = described_class.from_operands(stack) + expect(op.operand_a).to eq(5) + expect(op.operand_b).to eq(3) + expect(stack.empty?).to be true + end end end -end diff --git a/spec/postscript/serializer_spec.rb b/spec/postscript/serializer_spec.rb index 49b822c..c899ba6 100644 --- a/spec/postscript/serializer_spec.rb +++ b/spec/postscript/serializer_spec.rb @@ -9,7 +9,9 @@ def serialize(program, **opts) it "emits a header with EPSF-3.0 marker when eps: true" do program = Postscript::Model::Program.new( - header: Postscript::Model::Program::Header.new(bounding_box: [0, 0, 100, 100], epsf: true), + header: Postscript::Model::Program::Header.new( + bounding_box: [0, 0, 100, 100], epsf: true, + ), body: [], ) out = serialize(program, eps: true) @@ -29,13 +31,15 @@ def serialize(program, **opts) it "emits setrgbcolor with three floats" do program = Postscript::Model::Program.new( - body: [Postscript::Model::Operators::Color::Setrgbcolor.new(red: 1, green: 0.5, blue: 0)], + body: [Postscript::Model::Operators::Color::Setrgbcolor.new(red: 1, + green: 0.5, blue: 0)], ) expect(serialize(program)).to include("1 0.5 0 setrgbcolor") end it "emits arc with operands" do - op = Postscript::Model::Operators::Path::Arc.new(x: 0, y: 0, radius: 10, angle1: 0, angle2: 360) + op = Postscript::Model::Operators::Path::Arc.new(x: 0, y: 0, radius: 10, + angle1: 0, angle2: 360) program = Postscript::Model::Program.new(body: [op]) expect(serialize(program)).to include("0 0 10 0 360 arc") end diff --git a/spec/postscript/source/lexer_spec.rb b/spec/postscript/source/lexer_spec.rb index 493d5dd..21c3049 100644 --- a/spec/postscript/source/lexer_spec.rb +++ b/spec/postscript/source/lexer_spec.rb @@ -10,7 +10,7 @@ def lex(src) it "tokenizes numbers and operators" do tokens = lex("10 20 moveto") expect(tokens.map(&:type)).to eq(%i[number number operator]) - expect(tokens.map(&:value)).to eq(["10", "20", "moveto"]) + expect(tokens.map(&:value)).to eq(%w[10 20 moveto]) end it "preserves % characters inside string literals" do