postsvg 0.2.2 → 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/.rubocop_todo.yml +49 -533
- data/Gemfile +1 -1
- data/lib/postsvg/cli.rb +5 -10
- data/lib/postsvg/color.rb +132 -0
- data/lib/postsvg/format_number.rb +22 -0
- data/lib/postsvg/graphics_context.rb +1 -2
- data/lib/postsvg/matrix.rb +106 -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 +1 -1
- data/lib/postsvg/parser/postscript_parser.rb +7 -7
- data/lib/postsvg/renderer.rb +3 -7
- 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/clip_path_registry.rb +1 -3
- data/lib/postsvg/svg/element.rb +1 -3
- data/lib/postsvg/svg/elements/ellipse.rb +1 -2
- data/lib/postsvg/svg/elements/svg.rb +1 -2
- data/lib/postsvg/svg/paint.rb +1 -1
- data/lib/postsvg/svg/path_data/parser.rb +6 -10
- data/lib/postsvg/svg/stroke.rb +2 -4
- data/lib/postsvg/svg/transform_list.rb +1 -1
- data/lib/postsvg/svg_builder.rb +11 -15
- data/lib/postsvg/translation/arc_converter.rb +12 -22
- data/lib/postsvg/translation/bounding_box.rb +4 -7
- data/lib/postsvg/translation/handlers/circle_handler.rb +2 -2
- data/lib/postsvg/translation/handlers/ellipse_handler.rb +3 -8
- data/lib/postsvg/translation/handlers/group_handler.rb +1 -2
- data/lib/postsvg/translation/handlers/image_handler.rb +2 -3
- data/lib/postsvg/translation/handlers/line_handler.rb +2 -6
- data/lib/postsvg/translation/handlers/open_handler.rb +2 -3
- data/lib/postsvg/translation/handlers/path_handler.rb +41 -57
- data/lib/postsvg/translation/handlers/polygon_handler.rb +2 -4
- data/lib/postsvg/translation/handlers/polyline_handler.rb +2 -4
- data/lib/postsvg/translation/handlers/rect_handler.rb +5 -10
- data/lib/postsvg/translation/handlers/shared.rb +11 -11
- data/lib/postsvg/translation/handlers/svg_handler.rb +2 -3
- data/lib/postsvg/translation/handlers/text_handler.rb +7 -8
- data/lib/postsvg/translation/handlers.rb +1 -2
- data/lib/postsvg/translation/ps_renderer.rb +1 -2
- data/lib/postsvg/version.rb +1 -1
- data/lib/postsvg/visitors/ps_visitor/arithmetic.rb +6 -6
- data/lib/postsvg/visitors/ps_visitor/color.rb +5 -8
- data/lib/postsvg/visitors/ps_visitor/common.rb +2 -1
- data/lib/postsvg/visitors/ps_visitor/container.rb +13 -13
- data/lib/postsvg/visitors/ps_visitor/control_flow.rb +3 -3
- data/lib/postsvg/visitors/ps_visitor/dictionary.rb +4 -8
- data/lib/postsvg/visitors/ps_visitor/font.rb +4 -14
- data/lib/postsvg/visitors/ps_visitor/graphics_state.rb +4 -4
- data/lib/postsvg/visitors/ps_visitor/painting.rb +1 -1
- data/lib/postsvg/visitors/ps_visitor/path.rb +4 -9
- data/lib/postsvg.rb +37 -42
- metadata +36 -16
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Operators
|
|
6
|
+
module Stack
|
|
7
|
+
class Pop < Operator
|
|
8
|
+
register_as "pop", consumes: 1, produces: 0
|
|
9
|
+
|
|
10
|
+
def self.from_operands(stack)
|
|
11
|
+
stack.pop
|
|
12
|
+
new
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Exch < Operator
|
|
17
|
+
register_as "exch", consumes: 2, produces: 2
|
|
18
|
+
|
|
19
|
+
def self.from_operands(stack)
|
|
20
|
+
stack.pop
|
|
21
|
+
stack.pop
|
|
22
|
+
new
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Dup < Operator
|
|
27
|
+
register_as "dup", consumes: 1, produces: 2
|
|
28
|
+
|
|
29
|
+
def self.from_operands(stack)
|
|
30
|
+
stack.pop
|
|
31
|
+
new
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Index < Operator
|
|
36
|
+
register_as "index", consumes: 1, produces: 1
|
|
37
|
+
attr_reader :index
|
|
38
|
+
def initialize(index:)
|
|
39
|
+
@index = index
|
|
40
|
+
freeze
|
|
41
|
+
end
|
|
42
|
+
def self.from_operands(stack)
|
|
43
|
+
new(index: stack.pop_number.to_i)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class Roll < Operator
|
|
48
|
+
register_as "roll", consumes: 2, produces: 0
|
|
49
|
+
attr_reader :count, :positions
|
|
50
|
+
def initialize(count:, positions:)
|
|
51
|
+
@count = count
|
|
52
|
+
@positions = positions
|
|
53
|
+
freeze
|
|
54
|
+
end
|
|
55
|
+
def self.from_operands(stack)
|
|
56
|
+
positions = stack.pop_number.to_i
|
|
57
|
+
count = stack.pop_number.to_i
|
|
58
|
+
new(count: count, positions: positions)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class Clear < Operator
|
|
63
|
+
register_as "clear", consumes: 0, produces: 0
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class Count < Operator
|
|
67
|
+
register_as "count", consumes: 0, produces: 1
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
module Operators
|
|
6
|
+
module Transformations
|
|
7
|
+
class Translate < Operator
|
|
8
|
+
register_as "translate"
|
|
9
|
+
attr_reader :tx, :ty
|
|
10
|
+
def initialize(tx:, ty:)
|
|
11
|
+
@tx = tx
|
|
12
|
+
@ty = ty
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
def self.from_operands(stack)
|
|
16
|
+
ty = stack.pop_number
|
|
17
|
+
tx = stack.pop_number
|
|
18
|
+
new(tx: tx, ty: ty)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Scale < Operator
|
|
23
|
+
register_as "scale"
|
|
24
|
+
attr_reader :sx, :sy
|
|
25
|
+
def initialize(sx:, sy:)
|
|
26
|
+
@sx = sx
|
|
27
|
+
@sy = sy
|
|
28
|
+
freeze
|
|
29
|
+
end
|
|
30
|
+
def self.from_operands(stack)
|
|
31
|
+
sy = stack.pop_number
|
|
32
|
+
sx = stack.pop_number
|
|
33
|
+
new(sx: sx, sy: sy)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class Rotate < Operator
|
|
38
|
+
register_as "rotate"
|
|
39
|
+
attr_reader :angle
|
|
40
|
+
def initialize(angle:)
|
|
41
|
+
@angle = angle
|
|
42
|
+
freeze
|
|
43
|
+
end
|
|
44
|
+
def self.from_operands(stack)
|
|
45
|
+
new(angle: stack.pop_number)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Concat < Operator
|
|
50
|
+
register_as "concat"
|
|
51
|
+
attr_reader :matrix
|
|
52
|
+
def initialize(matrix:)
|
|
53
|
+
@matrix = matrix
|
|
54
|
+
freeze
|
|
55
|
+
end
|
|
56
|
+
def self.from_operands(stack)
|
|
57
|
+
new(matrix: stack.pop)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class Matrix < Operator
|
|
62
|
+
register_as "matrix"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Currentmatrix < Operator
|
|
66
|
+
register_as "currentmatrix"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class Setmatrix < Operator
|
|
70
|
+
register_as "setmatrix"
|
|
71
|
+
attr_reader :matrix
|
|
72
|
+
def initialize(matrix:)
|
|
73
|
+
@matrix = matrix
|
|
74
|
+
freeze
|
|
75
|
+
end
|
|
76
|
+
def self.from_operands(stack)
|
|
77
|
+
new(matrix: stack.pop)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class Transform < Operator
|
|
82
|
+
register_as "transform"
|
|
83
|
+
def self.from_operands(stack); end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class Dtransform < Operator
|
|
87
|
+
register_as "dtransform"
|
|
88
|
+
def self.from_operands(stack); end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class Itransform < Operator
|
|
92
|
+
register_as "itransform"
|
|
93
|
+
def self.from_operands(stack); end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class Idtransform < Operator
|
|
97
|
+
register_as "idtransform"
|
|
98
|
+
def self.from_operands(stack); end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
# PS operator catalogue. Each subclass of Model::Operator registers
|
|
6
|
+
# itself under its keyword. The parser looks keywords up here to
|
|
7
|
+
# decide which class to instantiate.
|
|
8
|
+
#
|
|
9
|
+
# Categories (mirrors PLRM chapter structure):
|
|
10
|
+
# - Stack, Arithmetic, Boolean, Path, Painting, Color,
|
|
11
|
+
# GraphicsState, Transformations, Dictionary, ControlFlow, Device
|
|
12
|
+
module Operators
|
|
13
|
+
autoload :Stack, "postsvg/model/operators/stack"
|
|
14
|
+
autoload :Arithmetic, "postsvg/model/operators/arithmetic"
|
|
15
|
+
autoload :Boolean, "postsvg/model/operators/boolean"
|
|
16
|
+
autoload :Path, "postsvg/model/operators/path"
|
|
17
|
+
autoload :Painting, "postsvg/model/operators/painting"
|
|
18
|
+
autoload :Color, "postsvg/model/operators/color"
|
|
19
|
+
autoload :GraphicsState, "postsvg/model/operators/graphics_state"
|
|
20
|
+
autoload :Transformations, "postsvg/model/operators/transformations"
|
|
21
|
+
autoload :Dictionary, "postsvg/model/operators/dictionary"
|
|
22
|
+
autoload :ControlFlow, "postsvg/model/operators/control_flow"
|
|
23
|
+
autoload :Device, "postsvg/model/operators/device"
|
|
24
|
+
autoload :Font, "postsvg/model/operators/font"
|
|
25
|
+
autoload :Container, "postsvg/model/operators/container"
|
|
26
|
+
|
|
27
|
+
@registry = {}
|
|
28
|
+
@visit_names = {}
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# @api private Used by Operator subclasses at load time.
|
|
32
|
+
def register(keyword, klass)
|
|
33
|
+
@registry[keyword] = klass
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Lookup by PS keyword. Returns nil for unknown keywords.
|
|
37
|
+
def [](keyword)
|
|
38
|
+
@registry[keyword]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def registered?(keyword)
|
|
42
|
+
@registry.key?(keyword)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def keywords
|
|
46
|
+
@registry.keys
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Force-load every operator category so the registry is fully
|
|
50
|
+
# populated. Call this once before relying on [+].
|
|
51
|
+
def load_all!
|
|
52
|
+
constants.each { |c| const_get(c) }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Wrapper for operator tokens with no registered class. Carries
|
|
58
|
+
# the original keyword so the visitor can emit a comment / warn.
|
|
59
|
+
class UnknownOperator < Operator
|
|
60
|
+
attr_reader :keyword
|
|
61
|
+
|
|
62
|
+
def initialize(keyword:)
|
|
63
|
+
@keyword = keyword
|
|
64
|
+
freeze
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def visit_name = "unknown"
|
|
68
|
+
|
|
69
|
+
def self.from_operands(_stack); end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# A bare name reference resolved to a user-defined procedure via
|
|
73
|
+
# +def+. The renderer descends into +procedure+ with the current
|
|
74
|
+
# context, mirroring how PostScript executes the procedure body.
|
|
75
|
+
class InvokeProcedure < Operator
|
|
76
|
+
attr_reader :name, :procedure
|
|
77
|
+
|
|
78
|
+
def initialize(name:, procedure:)
|
|
79
|
+
@name = name
|
|
80
|
+
@procedure = procedure
|
|
81
|
+
freeze
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def visit_name = "invoke_procedure"
|
|
85
|
+
|
|
86
|
+
def keyword = name.to_s
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
# Top-level program node. Carries DSC header (BoundingBox, Title,
|
|
6
|
+
# etc.) and the body — an ordered list of statements, each either a
|
|
7
|
+
# literal (Number/Name/String/Array/Procedure/Dictionary) or an
|
|
8
|
+
# Operator instance.
|
|
9
|
+
class Program
|
|
10
|
+
attr_reader :header, :body
|
|
11
|
+
|
|
12
|
+
def initialize(header: Header.new, body: [])
|
|
13
|
+
@header = header
|
|
14
|
+
@body = body
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def with(header: nil, body: nil)
|
|
19
|
+
Program.new(header: header || @header, body: body || @body)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def append(statement)
|
|
23
|
+
Program.new(header: @header, body: @body + [statement])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def concat(statements)
|
|
27
|
+
Program.new(header: @header, body: @body + statements)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# DSC header: structured document structuring comments.
|
|
31
|
+
class Header
|
|
32
|
+
attr_reader :bounding_box, :hires_bounding_box, :title, :creator,
|
|
33
|
+
:creation_date, :pages, :page_count, :epsf,
|
|
34
|
+
:language_level, :custom
|
|
35
|
+
|
|
36
|
+
def initialize(bounding_box: nil, hires_bounding_box: nil, title: nil,
|
|
37
|
+
creator: nil, creation_date: nil, pages: nil,
|
|
38
|
+
page_count: nil, epsf: false, language_level: nil,
|
|
39
|
+
custom: {})
|
|
40
|
+
@bounding_box = bounding_box
|
|
41
|
+
@hires_bounding_box = hires_bounding_box
|
|
42
|
+
@title = title
|
|
43
|
+
@creator = creator
|
|
44
|
+
@creation_date = creation_date
|
|
45
|
+
@pages = pages
|
|
46
|
+
@page_count = page_count
|
|
47
|
+
@epsf = epsf
|
|
48
|
+
@language_level = language_level
|
|
49
|
+
@custom = custom.dup.freeze
|
|
50
|
+
freeze
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def with(**overrides)
|
|
54
|
+
Header.new(**to_h.merge(overrides))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_h
|
|
58
|
+
{
|
|
59
|
+
bounding_box: bounding_box, hires_bounding_box: hires_bounding_box,
|
|
60
|
+
title: title, creator: creator, creation_date: creation_date,
|
|
61
|
+
pages: pages, page_count: page_count, epsf: epsf,
|
|
62
|
+
language_level: language_level, custom: custom,
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
module Model
|
|
5
|
+
# Lexical token. Type is one of:
|
|
6
|
+
# +:number+, +:operator+, +:name+, +:string+, +:hexstring+,
|
|
7
|
+
# +:proc_open+, +:proc_close+, +:array_open+, +:array_close+,
|
|
8
|
+
# +:dict_open+, +:dict_close+, +:dsc+.
|
|
9
|
+
#
|
|
10
|
+
# +literal+ is true when a +:name+ was written with a leading slash
|
|
11
|
+
# (i.e. it is a literal name, not an operator-style name reference).
|
|
12
|
+
class Token
|
|
13
|
+
attr_reader :type, :value, :line, :column, :literal
|
|
14
|
+
|
|
15
|
+
def initialize(type, value, line: nil, column: nil, literal: false)
|
|
16
|
+
@type = type
|
|
17
|
+
@value = value
|
|
18
|
+
@line = line
|
|
19
|
+
@column = column
|
|
20
|
+
@literal = literal
|
|
21
|
+
freeze
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def position
|
|
25
|
+
[line, column]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ==(other)
|
|
29
|
+
other.is_a?(Token) &&
|
|
30
|
+
other.type == @type &&
|
|
31
|
+
other.value == @value &&
|
|
32
|
+
other.line == @line &&
|
|
33
|
+
other.column == @column &&
|
|
34
|
+
other.literal == @literal
|
|
35
|
+
end
|
|
36
|
+
alias eql? ==
|
|
37
|
+
|
|
38
|
+
def hash
|
|
39
|
+
[@type, @value, @line, @column, @literal].hash
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Postsvg
|
|
4
|
+
# PS domain model. Every value object that participates in the AST
|
|
5
|
+
# or in round-trip is a Model::* class. The lexer emits Model::Token;
|
|
6
|
+
# the parser emits Model::Program; the renderer / serializer consume
|
|
7
|
+
# Model::Program.
|
|
8
|
+
module Model
|
|
9
|
+
autoload :Token, "postsvg/model/token"
|
|
10
|
+
autoload :Program, "postsvg/model/program"
|
|
11
|
+
autoload :Literals, "postsvg/model/literals"
|
|
12
|
+
autoload :Operator, "postsvg/model/operator"
|
|
13
|
+
autoload :UnknownOperator, "postsvg/model/operator"
|
|
14
|
+
autoload :InvokeProcedure, "postsvg/model/operator"
|
|
15
|
+
autoload :Operators, "postsvg/model/operators"
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/postsvg/options.rb
CHANGED
|
@@ -38,7 +38,7 @@ module Postsvg
|
|
|
38
38
|
(str("\\") >> any) | (str(")").absent? >> any)
|
|
39
39
|
end
|
|
40
40
|
rule(:string) do
|
|
41
|
-
str("(") >> string_char.repeat.as(:string) >> str(")")
|
|
41
|
+
(str("(") >> string_char.repeat.as(:string) >> str(")"))
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# Operators and keywords
|
|
@@ -49,16 +49,16 @@ module Postsvg
|
|
|
49
49
|
|
|
50
50
|
# Arrays
|
|
51
51
|
rule(:array) do
|
|
52
|
-
str("[") >> whitespace? >>
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
(str("[") >> whitespace? >>
|
|
53
|
+
value.repeat.as(:array) >>
|
|
54
|
+
whitespace? >> str("]"))
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# Procedures (code blocks in curly braces)
|
|
58
58
|
rule(:procedure) do
|
|
59
|
-
str("{") >> whitespace? >>
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
(str("{") >> whitespace? >>
|
|
60
|
+
statement.repeat.as(:procedure) >>
|
|
61
|
+
whitespace? >> str("}"))
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# Values
|
data/lib/postsvg/renderer.rb
CHANGED
|
@@ -13,10 +13,7 @@ module Postsvg
|
|
|
13
13
|
attr_reader :program, :options, :builder, :visitor
|
|
14
14
|
|
|
15
15
|
def initialize(program, options)
|
|
16
|
-
unless program.is_a?(Model::Program)
|
|
17
|
-
raise ArgumentError,
|
|
18
|
-
"program must be a Model::Program"
|
|
19
|
-
end
|
|
16
|
+
raise ArgumentError, "program must be a Model::Program" unless program.is_a?(Model::Program)
|
|
20
17
|
|
|
21
18
|
@program = program
|
|
22
19
|
@options = options
|
|
@@ -28,7 +25,7 @@ module Postsvg
|
|
|
28
25
|
open_svg
|
|
29
26
|
wrap_y_flip do
|
|
30
27
|
visitor.visit_program(program)
|
|
31
|
-
rescue
|
|
28
|
+
rescue Postsvg::QuitSignal
|
|
32
29
|
# PS +quit+ ends program execution; suppress and finalize SVG.
|
|
33
30
|
end
|
|
34
31
|
builder.close_svg
|
|
@@ -65,8 +62,7 @@ module Postsvg
|
|
|
65
62
|
[viewbox, width, height]
|
|
66
63
|
else
|
|
67
64
|
width, height = options.page_size
|
|
68
|
-
["0 0 #{FormatNumber.call(width)} #{FormatNumber.call(height)}", width,
|
|
69
|
-
height]
|
|
65
|
+
["0 0 #{FormatNumber.call(width)} #{FormatNumber.call(height)}", width, height]
|
|
70
66
|
end
|
|
71
67
|
end
|
|
72
68
|
|