postsvg 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +105 -0
- data/CLAUDE.md +173 -0
- data/Gemfile +2 -3
- data/README.adoc +456 -179
- data/Rakefile +100 -0
- data/TODO.roadmap/00-architecture.md +139 -0
- data/TODO.roadmap/01-autoload-migration.md +39 -0
- data/TODO.roadmap/02-isolate-dormant-code.md +51 -0
- data/TODO.roadmap/03-domain-model.md +66 -0
- data/TODO.roadmap/04-lexer.md +40 -0
- data/TODO.roadmap/05-parser.md +43 -0
- data/TODO.roadmap/06-graphics-state.md +45 -0
- data/TODO.roadmap/07-matrix-and-color.md +37 -0
- data/TODO.roadmap/08-svg-builder.md +51 -0
- data/TODO.roadmap/09-renderer.md +52 -0
- data/TODO.roadmap/10-visitor.md +58 -0
- data/TODO.roadmap/11-operator-coverage.md +103 -0
- data/TODO.roadmap/12-svg-domain.md +62 -0
- data/TODO.roadmap/13-translation-handlers.md +69 -0
- data/TODO.roadmap/14-ps-serializer.md +49 -0
- data/TODO.roadmap/15-cli-and-public-api.md +47 -0
- data/TODO.roadmap/16-specs.md +84 -0
- data/TODO.roadmap/17-docs-sync.md +47 -0
- data/TODO.roadmap/18-performance-and-determinism.md +47 -0
- data/TODO.roadmap/19-error-model.md +45 -0
- data/TODO.roadmap/20-font-and-text.md +46 -0
- data/TODO.roadmap/21-images.md +45 -0
- data/TODO.roadmap/22-forms-and-resources.md +25 -0
- data/TODO.roadmap/23-level2-level3.md +52 -0
- data/TODO.roadmap/24-ci-and-release.md +42 -0
- data/TODO.roadmap/README.md +77 -0
- data/docs/.gitignore +29 -0
- data/docs/CHANGELOG.md +114 -0
- data/docs/COMPLETE_DOCUMENTATION_STATUS.md +376 -0
- data/docs/DEPLOYMENT.md +456 -0
- data/docs/DEPLOYMENT_INSTRUCTIONS.md +229 -0
- data/docs/DOCUMENTATION_PLAN.md +425 -0
- data/docs/FINAL_SUMMARY.md +657 -0
- data/docs/Gemfile +15 -0
- data/docs/README.md +327 -0
- data/docs/_config.yml +99 -0
- data/docs/advanced-topics.adoc +370 -0
- data/docs/api-reference/colors.adoc +705 -0
- data/docs/api-reference/converter.adoc +699 -0
- data/docs/api-reference/execution-context.adoc +1210 -0
- data/docs/api-reference/graphics-state.adoc +1070 -0
- data/docs/api-reference/interpreter.adoc +810 -0
- data/docs/api-reference/matrix.adoc +1179 -0
- data/docs/api-reference/path-builder.adoc +1284 -0
- data/docs/api-reference/postsvg-module.adoc +388 -0
- data/docs/api-reference/svg-generator.adoc +891 -0
- data/docs/api-reference/tokenizer.adoc +925 -0
- data/docs/api-reference.adoc +221 -0
- data/docs/architecture/command-registry.adoc +1191 -0
- data/docs/architecture/conversion-pipeline.adoc +746 -0
- data/docs/architecture/design-decisions.adoc +999 -0
- data/docs/architecture/generator-stage.adoc +1115 -0
- data/docs/architecture/graphics-state-model.adoc +1089 -0
- data/docs/architecture/interpreter-stage.adoc +1125 -0
- data/docs/architecture/parser-stage.adoc +1051 -0
- data/docs/architecture.adoc +354 -0
- data/docs/cli-reference/batch-command.adoc +616 -0
- data/docs/cli-reference/check-command.adoc +677 -0
- data/docs/cli-reference/cli-options.adoc +802 -0
- data/docs/cli-reference/convert-command.adoc +462 -0
- data/docs/cli-reference/version-command.adoc +296 -0
- data/docs/cli-reference.adoc +317 -0
- data/docs/concepts/conversion-pipeline.adoc +903 -0
- data/docs/concepts/coordinate-systems.adoc +836 -0
- data/docs/concepts/graphics-state.adoc +861 -0
- data/docs/concepts/path-operations.adoc +1076 -0
- data/docs/concepts/postscript-language.adoc +859 -0
- data/docs/concepts/svg-generation.adoc +937 -0
- data/docs/concepts.adoc +198 -0
- data/docs/contributing.adoc +443 -0
- data/docs/development.adoc +420 -0
- data/docs/faq.adoc +493 -0
- data/docs/getting-started/basic-usage.adoc +538 -0
- data/docs/getting-started/common-workflows.adoc +577 -0
- data/docs/getting-started/first-conversion.adoc +492 -0
- data/docs/getting-started/installation.adoc +534 -0
- data/docs/getting-started.adoc +94 -0
- data/docs/index.adoc +248 -0
- data/docs/optimization.adoc +196 -0
- data/docs/ps2svg_compatibility.adoc +149 -0
- data/docs/quick-reference.adoc +453 -0
- data/docs/sitemap.adoc +337 -0
- data/docs/troubleshooting.adoc +486 -0
- data/docs/validation.adoc +772 -0
- data/exe/postsvg +1 -0
- data/lib/postsvg/cli.rb +104 -57
- data/lib/postsvg/color.rb +132 -0
- data/lib/postsvg/errors.rb +68 -3
- data/lib/postsvg/format_number.rb +22 -0
- data/lib/postsvg/graphics_context.rb +80 -0
- data/lib/postsvg/graphics_stack.rb +43 -0
- data/lib/postsvg/model/literals/array.rb +41 -0
- data/lib/postsvg/model/literals/dictionary.rb +34 -0
- data/lib/postsvg/model/literals/hex.rb +37 -0
- data/lib/postsvg/model/literals/name.rb +40 -0
- data/lib/postsvg/model/literals/number.rb +36 -0
- data/lib/postsvg/model/literals/procedure.rb +41 -0
- data/lib/postsvg/model/literals/string.rb +30 -0
- data/lib/postsvg/model/literals.rb +19 -0
- data/lib/postsvg/model/operator.rb +58 -0
- data/lib/postsvg/model/operators/arithmetic.rb +264 -0
- data/lib/postsvg/model/operators/boolean.rb +182 -0
- data/lib/postsvg/model/operators/color.rb +74 -0
- data/lib/postsvg/model/operators/container.rb +186 -0
- data/lib/postsvg/model/operators/control_flow.rb +119 -0
- data/lib/postsvg/model/operators/device.rb +21 -0
- data/lib/postsvg/model/operators/dictionary.rb +118 -0
- data/lib/postsvg/model/operators/font.rb +121 -0
- data/lib/postsvg/model/operators/graphics_state.rb +84 -0
- data/lib/postsvg/model/operators/painting.rb +29 -0
- data/lib/postsvg/model/operators/path.rb +169 -0
- data/lib/postsvg/model/operators/stack.rb +72 -0
- data/lib/postsvg/model/operators/transformations.rb +103 -0
- data/lib/postsvg/model/operators.rb +89 -0
- data/lib/postsvg/model/program.rb +68 -0
- data/lib/postsvg/model/token.rb +43 -0
- data/lib/postsvg/model.rb +17 -0
- data/lib/postsvg/options.rb +29 -0
- data/lib/postsvg/renderer.rb +85 -0
- data/lib/postsvg/serializer.rb +325 -0
- data/lib/postsvg/source/ast_builder.rb +308 -0
- data/lib/postsvg/source/lexer.rb +322 -0
- data/lib/postsvg/source/operand_stack.rb +55 -0
- data/lib/postsvg/source.rb +21 -0
- data/lib/postsvg/svg/attribute_parser.rb +45 -0
- data/lib/postsvg/svg/clip_path_registry.rb +44 -0
- data/lib/postsvg/svg/document.rb +22 -0
- data/lib/postsvg/svg/element.rb +84 -0
- data/lib/postsvg/svg/elements/circle.rb +36 -0
- data/lib/postsvg/svg/elements/clip_path.rb +26 -0
- data/lib/postsvg/svg/elements/defs.rb +24 -0
- data/lib/postsvg/svg/elements/ellipse.rb +38 -0
- data/lib/postsvg/svg/elements/group.rb +37 -0
- data/lib/postsvg/svg/elements/image.rb +35 -0
- data/lib/postsvg/svg/elements/line.rb +36 -0
- data/lib/postsvg/svg/elements/path.rb +32 -0
- data/lib/postsvg/svg/elements/polygon.rb +12 -0
- data/lib/postsvg/svg/elements/polyline.rb +32 -0
- data/lib/postsvg/svg/elements/rect.rb +44 -0
- data/lib/postsvg/svg/elements/svg.rb +39 -0
- data/lib/postsvg/svg/elements/text.rb +42 -0
- data/lib/postsvg/svg/elements.rb +31 -0
- data/lib/postsvg/svg/paint.rb +34 -0
- data/lib/postsvg/svg/parser.rb +39 -0
- data/lib/postsvg/svg/path_data/command.rb +27 -0
- data/lib/postsvg/svg/path_data/parser.rb +82 -0
- data/lib/postsvg/svg/path_data.rb +17 -0
- data/lib/postsvg/svg/stroke.rb +31 -0
- data/lib/postsvg/svg/transform_list.rb +59 -0
- data/lib/postsvg/svg.rb +22 -0
- data/lib/postsvg/svg_builder.rb +249 -0
- data/lib/postsvg/translation/arc_converter.rb +86 -0
- data/lib/postsvg/translation/bounding_box.rb +59 -0
- data/lib/postsvg/translation/context.rb +34 -0
- data/lib/postsvg/translation/handler_registry.rb +38 -0
- data/lib/postsvg/translation/handlers/circle_handler.rb +28 -0
- data/lib/postsvg/translation/handlers/clip_path_handler.rb +13 -0
- data/lib/postsvg/translation/handlers/defs_handler.rb +15 -0
- data/lib/postsvg/translation/handlers/ellipse_handler.rb +31 -0
- data/lib/postsvg/translation/handlers/group_handler.rb +21 -0
- data/lib/postsvg/translation/handlers/image_handler.rb +20 -0
- data/lib/postsvg/translation/handlers/line_handler.rb +23 -0
- data/lib/postsvg/translation/handlers/open_handler.rb +18 -0
- data/lib/postsvg/translation/handlers/path_handler.rb +356 -0
- data/lib/postsvg/translation/handlers/polygon_handler.rb +33 -0
- data/lib/postsvg/translation/handlers/polyline_handler.rb +31 -0
- data/lib/postsvg/translation/handlers/rect_handler.rb +27 -0
- data/lib/postsvg/translation/handlers/shared.rb +110 -0
- data/lib/postsvg/translation/handlers/svg_handler.rb +25 -0
- data/lib/postsvg/translation/handlers/text_handler.rb +56 -0
- data/lib/postsvg/translation/handlers.rb +25 -0
- data/lib/postsvg/translation/ps_renderer.rb +105 -0
- data/lib/postsvg/translation/record_emitter.rb +35 -0
- data/lib/postsvg/translation.rb +17 -0
- data/lib/postsvg/version.rb +1 -1
- data/lib/postsvg/visitors/ps_visitor/arithmetic.rb +125 -0
- data/lib/postsvg/visitors/ps_visitor/boolean.rb +105 -0
- data/lib/postsvg/visitors/ps_visitor/color.rb +53 -0
- data/lib/postsvg/visitors/ps_visitor/common.rb +66 -0
- data/lib/postsvg/visitors/ps_visitor/container.rb +164 -0
- data/lib/postsvg/visitors/ps_visitor/control_flow.rb +110 -0
- data/lib/postsvg/visitors/ps_visitor/device.rb +20 -0
- data/lib/postsvg/visitors/ps_visitor/dictionary.rb +89 -0
- data/lib/postsvg/visitors/ps_visitor/font.rb +93 -0
- data/lib/postsvg/visitors/ps_visitor/graphics_state.rb +55 -0
- data/lib/postsvg/visitors/ps_visitor/painting.rb +90 -0
- data/lib/postsvg/visitors/ps_visitor/path.rb +112 -0
- data/lib/postsvg/visitors/ps_visitor/stack.rb +47 -0
- data/lib/postsvg/visitors/ps_visitor/transformations.rb +101 -0
- data/lib/postsvg/visitors/ps_visitor.rb +208 -0
- data/lib/postsvg/visitors.rb +9 -0
- data/lib/postsvg.rb +93 -59
- data/lychee.toml +86 -0
- metadata +216 -11
- data/postsvg.gemspec +0 -38
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Literals
|
|
6
|
+
# A PostScript name. +literal:+ distinguishes +/foo+ (literal,
|
|
7
|
+
# pushed onto the stack) from +foo+ (executable, looked up and
|
|
8
|
+
# executed).
|
|
9
|
+
class Name
|
|
10
|
+
attr_reader :value, :literal
|
|
11
|
+
|
|
12
|
+
def initialize(value, literal: false)
|
|
13
|
+
@value = value.to_s
|
|
14
|
+
@literal = literal
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def literal? = literal
|
|
19
|
+
def executable? = !literal
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
@value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def accept(visitor, ctx)
|
|
26
|
+
visitor.visit_name(self, ctx)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def ==(other)
|
|
30
|
+
other.is_a?(Name) && other.value == value && other.literal == literal
|
|
31
|
+
end
|
|
32
|
+
alias eql? ==
|
|
33
|
+
|
|
34
|
+
def hash
|
|
35
|
+
[value, literal].hash
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Literals
|
|
6
|
+
# A PostScript numeric literal. Stored as a Ruby Numeric so
|
|
7
|
+
# arithmetic Just Works; class carries the original source
|
|
8
|
+
# representation choice (int vs float) when known.
|
|
9
|
+
class Number
|
|
10
|
+
attr_reader :value
|
|
11
|
+
|
|
12
|
+
def initialize(value)
|
|
13
|
+
@value = value.is_a?(Numeric) ? value : Float(value)
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_i = value.to_i
|
|
18
|
+
def to_f = value.to_f
|
|
19
|
+
def integer? = value.is_a?(Integer)
|
|
20
|
+
|
|
21
|
+
def accept(visitor, ctx)
|
|
22
|
+
visitor.visit_number(self, ctx)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def ==(other)
|
|
26
|
+
other.is_a?(Number) && other.value == value
|
|
27
|
+
end
|
|
28
|
+
alias eql? ==
|
|
29
|
+
|
|
30
|
+
def hash
|
|
31
|
+
value.hash
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Literals
|
|
6
|
+
# Procedure body: +{ ... }+. Carries the inner token stream as a
|
|
7
|
+
# list of literal/operator nodes. Procedures are not executed by
|
|
8
|
+
# the parser; the renderer descends into them on InvokeProcedure.
|
|
9
|
+
class Procedure
|
|
10
|
+
include Enumerable
|
|
11
|
+
|
|
12
|
+
attr_reader :body
|
|
13
|
+
|
|
14
|
+
def initialize(body = [])
|
|
15
|
+
@body = body.freeze
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def each(&block)
|
|
20
|
+
@body.each(&block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def length = @body.length
|
|
24
|
+
def empty? = @body.empty?
|
|
25
|
+
|
|
26
|
+
def accept(visitor, ctx)
|
|
27
|
+
visitor.visit_procedure(self, ctx)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ==(other)
|
|
31
|
+
other.is_a?(Procedure) && other.body == @body
|
|
32
|
+
end
|
|
33
|
+
alias eql? ==
|
|
34
|
+
|
|
35
|
+
def hash
|
|
36
|
+
@body.hash
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Literals
|
|
6
|
+
# Parenthesized string literal: +(foo bar baz)+.
|
|
7
|
+
class StringLiteral
|
|
8
|
+
attr_reader :value
|
|
9
|
+
|
|
10
|
+
def initialize(value)
|
|
11
|
+
@value = value.to_s
|
|
12
|
+
freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def accept(visitor, ctx)
|
|
16
|
+
visitor.visit_string_literal(self, ctx)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ==(other)
|
|
20
|
+
other.is_a?(StringLiteral) && other.value == value
|
|
21
|
+
end
|
|
22
|
+
alias eql? ==
|
|
23
|
+
|
|
24
|
+
def hash
|
|
25
|
+
value.hash
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
# PS literal value objects. Each is immutable, has value equality,
|
|
6
|
+
# and knows how to (a) emit itself to a serializer and (b) be
|
|
7
|
+
# visited by a renderer. They never carry executable behaviour —
|
|
8
|
+
# they are data.
|
|
9
|
+
module Literals
|
|
10
|
+
autoload :Number, "postsvg/model/literals/number"
|
|
11
|
+
autoload :Name, "postsvg/model/literals/name"
|
|
12
|
+
autoload :StringLiteral, "postsvg/model/literals/string"
|
|
13
|
+
autoload :HexLiteral, "postsvg/model/literals/hex"
|
|
14
|
+
autoload :ArrayLiteral, "postsvg/model/literals/array"
|
|
15
|
+
autoload :Procedure, "postsvg/model/literals/procedure"
|
|
16
|
+
autoload :Dictionary, "postsvg/model/literals/dictionary"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
# Common base for every PS operator. Subclasses live under
|
|
6
|
+
# Model::Operators::* grouped by PLRM chapter.
|
|
7
|
+
#
|
|
8
|
+
# Each operator declares:
|
|
9
|
+
# - +keyword+ (the PS source token, e.g. "moveto")
|
|
10
|
+
# - typed operand fields via +attr_reader+
|
|
11
|
+
# - +self.from_operands(stack)+ that pops operands and returns a new
|
|
12
|
+
# frozen instance
|
|
13
|
+
# - +self.visit_name+ (defaults to the keyword)
|
|
14
|
+
# - +self.consumes+ / +self.produces+ (stack-arity, for parser
|
|
15
|
+
# placeholders)
|
|
16
|
+
#
|
|
17
|
+
# The visitor calls +operator.accept(visitor, ctx)+ which routes to
|
|
18
|
+
# +visitor.visit_<visit_name>(operator, ctx)+ via +public_send+.
|
|
19
|
+
class Operator
|
|
20
|
+
class << self
|
|
21
|
+
# Register this class under +keyword+ in the global Registry.
|
|
22
|
+
# Called at the bottom of each subclass definition.
|
|
23
|
+
def register_as(keyword, consumes: 0, produces: 0)
|
|
24
|
+
Operators.register(keyword, self)
|
|
25
|
+
define_method(:keyword) { keyword }
|
|
26
|
+
define_method(:visit_name) { keyword }
|
|
27
|
+
@consumes = consumes
|
|
28
|
+
@produces = produces
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_reader :consumes, :produces
|
|
32
|
+
|
|
33
|
+
# Default: a no-op factory that pops nothing. Subclasses
|
|
34
|
+
# override to consume operands off the parse stack.
|
|
35
|
+
def from_operands(_stack)
|
|
36
|
+
new
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def accept(visitor, ctx)
|
|
41
|
+
visitor.dispatch(:"visit_#{visit_name}", self, ctx)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def operator? = true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Sentinel value the parser pushes onto the operand stack when an
|
|
48
|
+
# operator's runtime result is unknown at parse time (e.g. the
|
|
49
|
+
# output of +findfont+, +add+, +dup+). Carries no semantic value;
|
|
50
|
+
# its purpose is to keep the parse-stack shape in sync with the
|
|
51
|
+
# runtime stack so chained operators parse cleanly.
|
|
52
|
+
class Computed < ::Struct.new(:operator_keyword, keyword_init: true)
|
|
53
|
+
def to_s
|
|
54
|
+
"<computed:#{operator_keyword}>"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Operators
|
|
6
|
+
module Arithmetic
|
|
7
|
+
BINARY_DEFAULT = { consumes: 2, produces: 1 }.freeze
|
|
8
|
+
UNARY_DEFAULT = { consumes: 1, produces: 1 }.freeze
|
|
9
|
+
|
|
10
|
+
class Add < Operator
|
|
11
|
+
register_as "add", **BINARY_DEFAULT
|
|
12
|
+
def self.from_operands(stack)
|
|
13
|
+
b = stack.pop_number
|
|
14
|
+
a = stack.pop_number
|
|
15
|
+
new(operand_a: a, operand_b: b)
|
|
16
|
+
end
|
|
17
|
+
attr_reader :operand_a, :operand_b
|
|
18
|
+
def initialize(operand_a:, operand_b:)
|
|
19
|
+
@operand_a = operand_a
|
|
20
|
+
@operand_b = operand_b
|
|
21
|
+
freeze
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Sub < Operator
|
|
26
|
+
register_as "sub", **BINARY_DEFAULT
|
|
27
|
+
def self.from_operands(stack)
|
|
28
|
+
b = stack.pop_number
|
|
29
|
+
a = stack.pop_number
|
|
30
|
+
new(operand_a: a, operand_b: b)
|
|
31
|
+
end
|
|
32
|
+
attr_reader :operand_a, :operand_b
|
|
33
|
+
def initialize(operand_a:, operand_b:)
|
|
34
|
+
@operand_a = operand_a
|
|
35
|
+
@operand_b = operand_b
|
|
36
|
+
freeze
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Mul < Operator
|
|
41
|
+
register_as "mul", **BINARY_DEFAULT
|
|
42
|
+
def self.from_operands(stack)
|
|
43
|
+
b = stack.pop_number
|
|
44
|
+
a = stack.pop_number
|
|
45
|
+
new(operand_a: a, operand_b: b)
|
|
46
|
+
end
|
|
47
|
+
attr_reader :operand_a, :operand_b
|
|
48
|
+
def initialize(operand_a:, operand_b:)
|
|
49
|
+
@operand_a = operand_a
|
|
50
|
+
@operand_b = operand_b
|
|
51
|
+
freeze
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class Div < Operator
|
|
56
|
+
register_as "div", **BINARY_DEFAULT
|
|
57
|
+
def self.from_operands(stack)
|
|
58
|
+
b = stack.pop_number
|
|
59
|
+
a = stack.pop_number
|
|
60
|
+
new(operand_a: a, operand_b: b)
|
|
61
|
+
end
|
|
62
|
+
attr_reader :operand_a, :operand_b
|
|
63
|
+
def initialize(operand_a:, operand_b:)
|
|
64
|
+
@operand_a = operand_a
|
|
65
|
+
@operand_b = operand_b
|
|
66
|
+
freeze
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class Idiv < Operator
|
|
71
|
+
register_as "idiv", **BINARY_DEFAULT
|
|
72
|
+
def self.from_operands(stack)
|
|
73
|
+
b = stack.pop_number.to_i
|
|
74
|
+
a = stack.pop_number.to_i
|
|
75
|
+
new(operand_a: a, operand_b: b)
|
|
76
|
+
end
|
|
77
|
+
attr_reader :operand_a, :operand_b
|
|
78
|
+
def initialize(operand_a:, operand_b:)
|
|
79
|
+
@operand_a = operand_a
|
|
80
|
+
@operand_b = operand_b
|
|
81
|
+
freeze
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class Mod < Operator
|
|
86
|
+
register_as "mod", **BINARY_DEFAULT
|
|
87
|
+
def self.from_operands(stack)
|
|
88
|
+
b = stack.pop_number.to_i
|
|
89
|
+
a = stack.pop_number.to_i
|
|
90
|
+
new(operand_a: a, operand_b: b)
|
|
91
|
+
end
|
|
92
|
+
attr_reader :operand_a, :operand_b
|
|
93
|
+
def initialize(operand_a:, operand_b:)
|
|
94
|
+
@operand_a = operand_a
|
|
95
|
+
@operand_b = operand_b
|
|
96
|
+
freeze
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class Neg < Operator
|
|
101
|
+
register_as "neg", **UNARY_DEFAULT
|
|
102
|
+
attr_reader :operand
|
|
103
|
+
def initialize(operand:)
|
|
104
|
+
@operand = operand
|
|
105
|
+
freeze
|
|
106
|
+
end
|
|
107
|
+
def self.from_operands(stack)
|
|
108
|
+
new(operand: stack.pop_number)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class Abs < Operator
|
|
113
|
+
register_as "abs", **UNARY_DEFAULT
|
|
114
|
+
attr_reader :operand
|
|
115
|
+
def initialize(operand:)
|
|
116
|
+
@operand = operand
|
|
117
|
+
freeze
|
|
118
|
+
end
|
|
119
|
+
def self.from_operands(stack)
|
|
120
|
+
new(operand: stack.pop_number)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class Ceiling < Operator
|
|
125
|
+
register_as "ceiling", **UNARY_DEFAULT
|
|
126
|
+
attr_reader :operand
|
|
127
|
+
def initialize(operand:)
|
|
128
|
+
@operand = operand
|
|
129
|
+
freeze
|
|
130
|
+
end
|
|
131
|
+
def self.from_operands(stack)
|
|
132
|
+
new(operand: stack.pop_number)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
class Floor < Operator
|
|
137
|
+
register_as "floor", **UNARY_DEFAULT
|
|
138
|
+
attr_reader :operand
|
|
139
|
+
def initialize(operand:)
|
|
140
|
+
@operand = operand
|
|
141
|
+
freeze
|
|
142
|
+
end
|
|
143
|
+
def self.from_operands(stack)
|
|
144
|
+
new(operand: stack.pop_number)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class Round < Operator
|
|
149
|
+
register_as "round", **UNARY_DEFAULT
|
|
150
|
+
attr_reader :operand
|
|
151
|
+
def initialize(operand:)
|
|
152
|
+
@operand = operand
|
|
153
|
+
freeze
|
|
154
|
+
end
|
|
155
|
+
def self.from_operands(stack)
|
|
156
|
+
new(operand: stack.pop_number)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
class Truncate < Operator
|
|
161
|
+
register_as "truncate", **UNARY_DEFAULT
|
|
162
|
+
attr_reader :operand
|
|
163
|
+
def initialize(operand:)
|
|
164
|
+
@operand = operand
|
|
165
|
+
freeze
|
|
166
|
+
end
|
|
167
|
+
def self.from_operands(stack)
|
|
168
|
+
new(operand: stack.pop_number)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
class Sqrt < Operator
|
|
173
|
+
register_as "sqrt", **UNARY_DEFAULT
|
|
174
|
+
attr_reader :operand
|
|
175
|
+
def initialize(operand:)
|
|
176
|
+
@operand = operand
|
|
177
|
+
freeze
|
|
178
|
+
end
|
|
179
|
+
def self.from_operands(stack)
|
|
180
|
+
new(operand: stack.pop_number)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
class Atan < Operator
|
|
185
|
+
register_as "atan", **BINARY_DEFAULT
|
|
186
|
+
def self.from_operands(stack)
|
|
187
|
+
b = stack.pop_number
|
|
188
|
+
a = stack.pop_number
|
|
189
|
+
new(operand_a: a, operand_b: b)
|
|
190
|
+
end
|
|
191
|
+
attr_reader :operand_a, :operand_b
|
|
192
|
+
def initialize(operand_a:, operand_b:)
|
|
193
|
+
@operand_a = operand_a
|
|
194
|
+
@operand_b = operand_b
|
|
195
|
+
freeze
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class Cos < Operator
|
|
200
|
+
register_as "cos", **UNARY_DEFAULT
|
|
201
|
+
attr_reader :operand
|
|
202
|
+
def initialize(operand:)
|
|
203
|
+
@operand = operand
|
|
204
|
+
freeze
|
|
205
|
+
end
|
|
206
|
+
def self.from_operands(stack)
|
|
207
|
+
new(operand: stack.pop_number)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
class Sin < Operator
|
|
212
|
+
register_as "sin", **UNARY_DEFAULT
|
|
213
|
+
attr_reader :operand
|
|
214
|
+
def initialize(operand:)
|
|
215
|
+
@operand = operand
|
|
216
|
+
freeze
|
|
217
|
+
end
|
|
218
|
+
def self.from_operands(stack)
|
|
219
|
+
new(operand: stack.pop_number)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
class Ln < Operator
|
|
224
|
+
register_as "ln", **UNARY_DEFAULT
|
|
225
|
+
attr_reader :operand
|
|
226
|
+
def initialize(operand:)
|
|
227
|
+
@operand = operand
|
|
228
|
+
freeze
|
|
229
|
+
end
|
|
230
|
+
def self.from_operands(stack)
|
|
231
|
+
new(operand: stack.pop_number)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
class Log < Operator
|
|
236
|
+
register_as "log", **UNARY_DEFAULT
|
|
237
|
+
attr_reader :operand
|
|
238
|
+
def initialize(operand:)
|
|
239
|
+
@operand = operand
|
|
240
|
+
freeze
|
|
241
|
+
end
|
|
242
|
+
def self.from_operands(stack)
|
|
243
|
+
new(operand: stack.pop_number)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
class Exp < Operator
|
|
248
|
+
register_as "exp", **BINARY_DEFAULT
|
|
249
|
+
def self.from_operands(stack)
|
|
250
|
+
b = stack.pop_number
|
|
251
|
+
a = stack.pop_number
|
|
252
|
+
new(operand_a: a, operand_b: b)
|
|
253
|
+
end
|
|
254
|
+
attr_reader :operand_a, :operand_b
|
|
255
|
+
def initialize(operand_a:, operand_b:)
|
|
256
|
+
@operand_a = operand_a
|
|
257
|
+
@operand_b = operand_b
|
|
258
|
+
freeze
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Operators
|
|
6
|
+
module Boolean
|
|
7
|
+
# PS boolean literals. They look like operator tokens but
|
|
8
|
+
# push constant Boolean values. Modeled as zero-arity
|
|
9
|
+
# operators so the parser/registry handle them naturally.
|
|
10
|
+
class True < Operator
|
|
11
|
+
register_as "true", consumes: 0, produces: 1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class False < Operator
|
|
15
|
+
register_as "false", consumes: 0, produces: 1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Eq < Operator
|
|
19
|
+
register_as "eq"
|
|
20
|
+
def self.from_operands(stack)
|
|
21
|
+
b = stack.pop
|
|
22
|
+
a = stack.pop
|
|
23
|
+
new(operand_a: a, operand_b: b)
|
|
24
|
+
end
|
|
25
|
+
attr_reader :operand_a, :operand_b
|
|
26
|
+
def initialize(operand_a:, operand_b:)
|
|
27
|
+
@operand_a = operand_a
|
|
28
|
+
@operand_b = operand_b
|
|
29
|
+
freeze
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Ne < Operator
|
|
34
|
+
register_as "ne"
|
|
35
|
+
def self.from_operands(stack)
|
|
36
|
+
b = stack.pop
|
|
37
|
+
a = stack.pop
|
|
38
|
+
new(operand_a: a, operand_b: b)
|
|
39
|
+
end
|
|
40
|
+
attr_reader :operand_a, :operand_b
|
|
41
|
+
def initialize(operand_a:, operand_b:)
|
|
42
|
+
@operand_a = operand_a
|
|
43
|
+
@operand_b = operand_b
|
|
44
|
+
freeze
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class Gt < Operator
|
|
49
|
+
register_as "gt"
|
|
50
|
+
def self.from_operands(stack)
|
|
51
|
+
b = stack.pop_number
|
|
52
|
+
a = stack.pop_number
|
|
53
|
+
new(operand_a: a, operand_b: b)
|
|
54
|
+
end
|
|
55
|
+
attr_reader :operand_a, :operand_b
|
|
56
|
+
def initialize(operand_a:, operand_b:)
|
|
57
|
+
@operand_a = operand_a
|
|
58
|
+
@operand_b = operand_b
|
|
59
|
+
freeze
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class Ge < Operator
|
|
64
|
+
register_as "ge"
|
|
65
|
+
def self.from_operands(stack)
|
|
66
|
+
b = stack.pop_number
|
|
67
|
+
a = stack.pop_number
|
|
68
|
+
new(operand_a: a, operand_b: b)
|
|
69
|
+
end
|
|
70
|
+
attr_reader :operand_a, :operand_b
|
|
71
|
+
def initialize(operand_a:, operand_b:)
|
|
72
|
+
@operand_a = operand_a
|
|
73
|
+
@operand_b = operand_b
|
|
74
|
+
freeze
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class Lt < Operator
|
|
79
|
+
register_as "lt"
|
|
80
|
+
def self.from_operands(stack)
|
|
81
|
+
b = stack.pop_number
|
|
82
|
+
a = stack.pop_number
|
|
83
|
+
new(operand_a: a, operand_b: b)
|
|
84
|
+
end
|
|
85
|
+
attr_reader :operand_a, :operand_b
|
|
86
|
+
def initialize(operand_a:, operand_b:)
|
|
87
|
+
@operand_a = operand_a
|
|
88
|
+
@operand_b = operand_b
|
|
89
|
+
freeze
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class Le < Operator
|
|
94
|
+
register_as "le"
|
|
95
|
+
def self.from_operands(stack)
|
|
96
|
+
b = stack.pop_number
|
|
97
|
+
a = stack.pop_number
|
|
98
|
+
new(operand_a: a, operand_b: b)
|
|
99
|
+
end
|
|
100
|
+
attr_reader :operand_a, :operand_b
|
|
101
|
+
def initialize(operand_a:, operand_b:)
|
|
102
|
+
@operand_a = operand_a
|
|
103
|
+
@operand_b = operand_b
|
|
104
|
+
freeze
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class And < Operator
|
|
109
|
+
register_as "and"
|
|
110
|
+
def self.from_operands(stack)
|
|
111
|
+
b = stack.pop
|
|
112
|
+
a = stack.pop
|
|
113
|
+
new(operand_a: a, operand_b: b)
|
|
114
|
+
end
|
|
115
|
+
attr_reader :operand_a, :operand_b
|
|
116
|
+
def initialize(operand_a:, operand_b:)
|
|
117
|
+
@operand_a = operand_a
|
|
118
|
+
@operand_b = operand_b
|
|
119
|
+
freeze
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class Or < Operator
|
|
124
|
+
register_as "or"
|
|
125
|
+
def self.from_operands(stack)
|
|
126
|
+
b = stack.pop
|
|
127
|
+
a = stack.pop
|
|
128
|
+
new(operand_a: a, operand_b: b)
|
|
129
|
+
end
|
|
130
|
+
attr_reader :operand_a, :operand_b
|
|
131
|
+
def initialize(operand_a:, operand_b:)
|
|
132
|
+
@operand_a = operand_a
|
|
133
|
+
@operand_b = operand_b
|
|
134
|
+
freeze
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class Xor < Operator
|
|
139
|
+
register_as "xor"
|
|
140
|
+
def self.from_operands(stack)
|
|
141
|
+
b = stack.pop
|
|
142
|
+
a = stack.pop
|
|
143
|
+
new(operand_a: a, operand_b: b)
|
|
144
|
+
end
|
|
145
|
+
attr_reader :operand_a, :operand_b
|
|
146
|
+
def initialize(operand_a:, operand_b:)
|
|
147
|
+
@operand_a = operand_a
|
|
148
|
+
@operand_b = operand_b
|
|
149
|
+
freeze
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class Not < Operator
|
|
154
|
+
register_as "not"
|
|
155
|
+
attr_reader :operand
|
|
156
|
+
def initialize(operand:)
|
|
157
|
+
@operand = operand
|
|
158
|
+
freeze
|
|
159
|
+
end
|
|
160
|
+
def self.from_operands(stack)
|
|
161
|
+
new(operand: stack.pop)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
class Bitshift < Operator
|
|
166
|
+
register_as "bitshift"
|
|
167
|
+
def self.from_operands(stack)
|
|
168
|
+
shift = stack.pop_number.to_i
|
|
169
|
+
value = stack.pop_number.to_i
|
|
170
|
+
new(operand: value, shift: shift)
|
|
171
|
+
end
|
|
172
|
+
attr_reader :operand, :shift
|
|
173
|
+
def initialize(operand:, shift:)
|
|
174
|
+
@operand = operand
|
|
175
|
+
@shift = shift
|
|
176
|
+
freeze
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|