brandish 0.1.1
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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +41 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +9 -0
- data/bin/brandish +16 -0
- data/brandish.gemspec +39 -0
- data/defaults/templates/html.liquid +39 -0
- data/lib/brandish.rb +51 -0
- data/lib/brandish/application.rb +163 -0
- data/lib/brandish/application/bench_command.rb +96 -0
- data/lib/brandish/application/build_command.rb +73 -0
- data/lib/brandish/application/initialize_command.rb +83 -0
- data/lib/brandish/application/serve_command.rb +150 -0
- data/lib/brandish/configure.rb +196 -0
- data/lib/brandish/configure/dsl.rb +135 -0
- data/lib/brandish/configure/dsl/form.rb +136 -0
- data/lib/brandish/configure/form.rb +32 -0
- data/lib/brandish/errors.rb +65 -0
- data/lib/brandish/execute.rb +26 -0
- data/lib/brandish/markup.rb +10 -0
- data/lib/brandish/markup/redcarpet.rb +14 -0
- data/lib/brandish/markup/redcarpet/format.rb +127 -0
- data/lib/brandish/markup/redcarpet/html.rb +95 -0
- data/lib/brandish/parser.rb +26 -0
- data/lib/brandish/parser/main.rb +237 -0
- data/lib/brandish/parser/node.rb +89 -0
- data/lib/brandish/parser/node/block.rb +98 -0
- data/lib/brandish/parser/node/command.rb +102 -0
- data/lib/brandish/parser/node/pair.rb +42 -0
- data/lib/brandish/parser/node/root.rb +83 -0
- data/lib/brandish/parser/node/string.rb +18 -0
- data/lib/brandish/parser/node/text.rb +114 -0
- data/lib/brandish/path_set.rb +163 -0
- data/lib/brandish/processor.rb +47 -0
- data/lib/brandish/processor/base.rb +144 -0
- data/lib/brandish/processor/block.rb +47 -0
- data/lib/brandish/processor/command.rb +47 -0
- data/lib/brandish/processor/context.rb +169 -0
- data/lib/brandish/processor/descend.rb +32 -0
- data/lib/brandish/processor/inline.rb +49 -0
- data/lib/brandish/processor/name_filter.rb +67 -0
- data/lib/brandish/processor/pair_filter.rb +96 -0
- data/lib/brandish/processors.rb +26 -0
- data/lib/brandish/processors/all.rb +19 -0
- data/lib/brandish/processors/all/comment.rb +29 -0
- data/lib/brandish/processors/all/embed.rb +56 -0
- data/lib/brandish/processors/all/if.rb +109 -0
- data/lib/brandish/processors/all/import.rb +95 -0
- data/lib/brandish/processors/all/literal.rb +42 -0
- data/lib/brandish/processors/all/verify.rb +47 -0
- data/lib/brandish/processors/common.rb +20 -0
- data/lib/brandish/processors/common/asset.rb +118 -0
- data/lib/brandish/processors/common/asset/paths.rb +93 -0
- data/lib/brandish/processors/common/group.rb +67 -0
- data/lib/brandish/processors/common/header.rb +86 -0
- data/lib/brandish/processors/common/markup.rb +127 -0
- data/lib/brandish/processors/common/output.rb +73 -0
- data/lib/brandish/processors/html.rb +18 -0
- data/lib/brandish/processors/html/group.rb +33 -0
- data/lib/brandish/processors/html/header.rb +46 -0
- data/lib/brandish/processors/html/markup.rb +131 -0
- data/lib/brandish/processors/html/output.rb +62 -0
- data/lib/brandish/processors/html/output/document.rb +127 -0
- data/lib/brandish/processors/html/script.rb +64 -0
- data/lib/brandish/processors/html/script/babel.rb +48 -0
- data/lib/brandish/processors/html/script/coffee.rb +47 -0
- data/lib/brandish/processors/html/script/vanilla.rb +45 -0
- data/lib/brandish/processors/html/style.rb +82 -0
- data/lib/brandish/processors/html/style/highlight.rb +89 -0
- data/lib/brandish/processors/html/style/sass.rb +64 -0
- data/lib/brandish/processors/html/style/vanilla.rb +71 -0
- data/lib/brandish/processors/latex.rb +15 -0
- data/lib/brandish/processors/latex/markup.rb +47 -0
- data/lib/brandish/scanner.rb +64 -0
- data/lib/brandish/version.rb +9 -0
- data/templates/initialize/Gemfile.tt +14 -0
- data/templates/initialize/brandish.config.rb.tt +49 -0
- metadata +296 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Brandish
|
|
5
|
+
class Parser
|
|
6
|
+
class Node
|
|
7
|
+
# A "block" node. Basically, this is a node that encapsulates a block of
|
|
8
|
+
# text. This typically has a 1-to-1 relationship with the body of text,
|
|
9
|
+
# while a command may not.
|
|
10
|
+
class Block < Node
|
|
11
|
+
# The name of the block.
|
|
12
|
+
#
|
|
13
|
+
# @return [::String]
|
|
14
|
+
attr_reader :name
|
|
15
|
+
|
|
16
|
+
# The body of the block.
|
|
17
|
+
#
|
|
18
|
+
# @return [Node::Root]
|
|
19
|
+
attr_reader :body
|
|
20
|
+
|
|
21
|
+
# The "pairs" of arguments to be passed to the command.
|
|
22
|
+
#
|
|
23
|
+
# @return [{::String => ::String}]
|
|
24
|
+
attr_reader :pairs
|
|
25
|
+
|
|
26
|
+
# Creates a block with the given name, body, and location. If no
|
|
27
|
+
# location is given, it is assumed from the name and body.
|
|
28
|
+
#
|
|
29
|
+
# @param name [Scanner::Token] The name of the block.
|
|
30
|
+
# @param body [Node::Root] The body of the block.
|
|
31
|
+
|
|
32
|
+
# @param location [Location] The location of the block.
|
|
33
|
+
def initialize(name:, body:, arguments: nil, pairs: nil, location: nil)
|
|
34
|
+
if !location && !arguments
|
|
35
|
+
fail ArgumentError, "Expected either a location or " \
|
|
36
|
+
"arguments, got neither"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
@name = name.is_a?(Yoga::Token) ? name.value : name
|
|
40
|
+
@body = body
|
|
41
|
+
@location = location || arguments.map(&:location)
|
|
42
|
+
.inject(name.location.union(body.location),
|
|
43
|
+
:union)
|
|
44
|
+
@pairs = pairs.freeze || derive_pairs(arguments).freeze
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Pretty inspect.
|
|
48
|
+
#
|
|
49
|
+
# @return [::String]
|
|
50
|
+
def inspect
|
|
51
|
+
"#<#{self.class} name=#{@name.inspect} pairs=#{@pairs.inspect} " \
|
|
52
|
+
"body=#{@body.inspect} location=#{@location.inspect}>"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Equates this object with another object. If the other object is
|
|
56
|
+
# this object, it returns true; otherwise, if it is a block, whose
|
|
57
|
+
# properties all equal this one, it returns true; otherwise, it
|
|
58
|
+
# returns false.
|
|
59
|
+
#
|
|
60
|
+
# @param other [::Object]
|
|
61
|
+
# @return [Boolean]
|
|
62
|
+
def ==(other)
|
|
63
|
+
equal?(other) || other.is_a?(Block) && @name == other.name &&
|
|
64
|
+
@body == other.body && @location == other.location
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def update_name(name)
|
|
70
|
+
Block.new(name: name, body: @body, pairs: @pairs, location: @location)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def update_body(body)
|
|
74
|
+
Block.new(name: @name, body: body, pairs: @pairs, location: @location)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_pairs(pairs)
|
|
78
|
+
Block.new(name: @name, body: @body, pairs: pairs, location: @location)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def update_arguments(arguments)
|
|
82
|
+
update_pairs(derive_pairs(arguments))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def update_location(location)
|
|
86
|
+
Block.new(name: @name, body: @body, pairs: @pairs, location: location)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def derive_pairs(arguments)
|
|
90
|
+
arguments.inject({}) do |a, pair|
|
|
91
|
+
k, v = pair.key, pair.value
|
|
92
|
+
a.merge!(k.value => v.value)
|
|
93
|
+
end.freeze
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Brandish
|
|
5
|
+
class Parser
|
|
6
|
+
class Node
|
|
7
|
+
# A command node. This takes the form of `{.a b=c}`, and executes a
|
|
8
|
+
# given command with the given options.
|
|
9
|
+
class Command < Node
|
|
10
|
+
# The name of the command node. This is the name of the command that
|
|
11
|
+
# is executed.
|
|
12
|
+
#
|
|
13
|
+
# @return [::String]
|
|
14
|
+
attr_reader :name
|
|
15
|
+
|
|
16
|
+
# The "pairs" of arguments to be passed to the command.
|
|
17
|
+
#
|
|
18
|
+
# @return [{::String => ::String}]
|
|
19
|
+
attr_reader :pairs
|
|
20
|
+
|
|
21
|
+
# Initialize the command node.
|
|
22
|
+
#
|
|
23
|
+
# @overload initialize(name:, arguments:, location: nil)
|
|
24
|
+
# Creates a command node with the given name. The arguments are
|
|
25
|
+
# processed to turn into a hash that is a key-value store of the
|
|
26
|
+
# arguments; if there are duplicate key-values pairs, the earlier
|
|
27
|
+
# ones are overwritten. If no location is provided, it is assumed
|
|
28
|
+
# from the name and arguments.
|
|
29
|
+
#
|
|
30
|
+
# @param name [::String, Scanner::Token] The name of the command. If
|
|
31
|
+
# no location is provided, this *must* respond to `#location`.
|
|
32
|
+
# @param arguments [<Node::Pair>] The pairs of arguments as an array
|
|
33
|
+
# of nodes.
|
|
34
|
+
# @overload initialize(name:, pairs:, location:)
|
|
35
|
+
# Creates a command node with the given name and argument pairs.
|
|
36
|
+
# Since no location can be derived from either, the location is
|
|
37
|
+
# required.
|
|
38
|
+
#
|
|
39
|
+
# @param name [::String, Scanner::Token] The name of the command.
|
|
40
|
+
# @param pairs [{::String => ::String, ::Numeric}] The argument pairs
|
|
41
|
+
# for the command.
|
|
42
|
+
# @param location [Location] The location of the node.
|
|
43
|
+
def initialize(name:, arguments: nil, pairs: nil, location: nil)
|
|
44
|
+
if !location && !arguments
|
|
45
|
+
fail ArgumentError, "Expected either a location or " \
|
|
46
|
+
"arguments, got neither"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@name = name.is_a?(Yoga::Token) ? name.value : name.freeze
|
|
50
|
+
@location = location || arguments.map(&:location)
|
|
51
|
+
.inject(name.location, :union)
|
|
52
|
+
@pairs = pairs.freeze || derive_pairs(arguments).freeze
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Determines if this object and the given object are equal. If the
|
|
56
|
+
# other object is this object, it returns true; otherwise, if the other
|
|
57
|
+
# object is a {Command}, and all of the properties of that object equal
|
|
58
|
+
# this one, then it returns true; otherwise, it returns false.
|
|
59
|
+
#
|
|
60
|
+
# @param other [::Object] The object to equal.
|
|
61
|
+
# @return [Boolean]
|
|
62
|
+
def ==(other)
|
|
63
|
+
equal?(other) || (other.is_a?(Command) && @name == other.name &&
|
|
64
|
+
@location == other.location && @pairs == other.pairs)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Pretty inspect.
|
|
68
|
+
#
|
|
69
|
+
# @return [::String]
|
|
70
|
+
def inspect
|
|
71
|
+
"#<#{self.class} name=#{@name.inspect} pairs=#{@pairs.inspect} " \
|
|
72
|
+
"location=#{@location.inspect}>"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def update_name(name)
|
|
78
|
+
Command.new(name: name, pairs: @pairs, location: @location)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def update_pairs(pairs)
|
|
82
|
+
Command.new(name: @name, pairs: pairs, location: @location)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def update_arguments(arguments)
|
|
86
|
+
update_pairs(derive_pairs(arguments))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def update_location(location)
|
|
90
|
+
Command.new(name: @name, pairs: @pairs, location: location)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def derive_pairs(arguments)
|
|
94
|
+
arguments.inject({}) do |a, pair|
|
|
95
|
+
k, v = pair.key, pair.value
|
|
96
|
+
a.merge!(k.value => v.value)
|
|
97
|
+
end.freeze
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Brandish
|
|
5
|
+
class Parser
|
|
6
|
+
class Node
|
|
7
|
+
# A key-value pair used for a command. This is used to represent an
|
|
8
|
+
# argument for the command node.
|
|
9
|
+
class Pair < Node
|
|
10
|
+
# The key of the pair. This will *always* be a `:TEXT`.
|
|
11
|
+
#
|
|
12
|
+
# @return [Scanner::Token] The key.
|
|
13
|
+
attr_reader :key
|
|
14
|
+
|
|
15
|
+
# The value of the pair.
|
|
16
|
+
#
|
|
17
|
+
# @return [Parser::Node::Text, Parser::Node::String] The value.
|
|
18
|
+
attr_reader :value
|
|
19
|
+
|
|
20
|
+
# Initialize the pair node with the given key, value and location.
|
|
21
|
+
#
|
|
22
|
+
# @param key [Yoga::Token] The key.
|
|
23
|
+
# @param value [Parser::Node::Text, Parser::Node::String] The value.
|
|
24
|
+
# @param location [Location] The location of the key-value pair.
|
|
25
|
+
def initialize(key:, value:, location: nil)
|
|
26
|
+
@key = key
|
|
27
|
+
@value = value
|
|
28
|
+
@location = location || key.location.union(value.location)
|
|
29
|
+
freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Pretty inspect.
|
|
33
|
+
#
|
|
34
|
+
# @return [::String]
|
|
35
|
+
def inspect
|
|
36
|
+
"#<#{self.class} key=#{@key.inspect} value=#{@value.inspect} " \
|
|
37
|
+
"location=#{@location.inspect}>"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Brandish
|
|
5
|
+
class Parser
|
|
6
|
+
class Node
|
|
7
|
+
# The root node. This is the parent of the full document.
|
|
8
|
+
class Root < Node
|
|
9
|
+
# The children of the root node. This should be a series of nodes
|
|
10
|
+
# that are in the body.
|
|
11
|
+
#
|
|
12
|
+
# @return [<Node>]
|
|
13
|
+
attr_reader :children
|
|
14
|
+
|
|
15
|
+
# Initialize the root node with the given children and location.
|
|
16
|
+
#
|
|
17
|
+
# @param children [<Node>] The children of the root node. See
|
|
18
|
+
# {#children}.
|
|
19
|
+
# @param location [Location] The location of the node. If no location
|
|
20
|
+
# is provided, it assumed from the children.
|
|
21
|
+
def initialize(children:, location: nil)
|
|
22
|
+
@children = children.freeze
|
|
23
|
+
@location = location || derive_location(children)
|
|
24
|
+
fail unless children.any?
|
|
25
|
+
freeze
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Pretty inspect.
|
|
29
|
+
#
|
|
30
|
+
# @return [::String]
|
|
31
|
+
def inspect
|
|
32
|
+
"#<#{self.class} children=#{@children.inspect} " \
|
|
33
|
+
"location=#{@location.inspect}>"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Checks for equivalence between this and another object. If the other
|
|
37
|
+
# object is this object, it returns true; otherwise, if the other
|
|
38
|
+
# object is a root, and its properties are equal to this one, it returns
|
|
39
|
+
# true; otherwise, it returns false.
|
|
40
|
+
#
|
|
41
|
+
# @param other [::Object]
|
|
42
|
+
# @return [Boolean]
|
|
43
|
+
def ==(other)
|
|
44
|
+
equal?(other) || other.is_a?(Root) && @children == other.children &&
|
|
45
|
+
@location == other.location
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# This flattens out the root node into a single string value. This is
|
|
49
|
+
# used for outputing the contents.
|
|
50
|
+
#
|
|
51
|
+
# @raise [NodeError] if the node contains a non-root or non-text node.
|
|
52
|
+
# @return [::String]
|
|
53
|
+
def flatten
|
|
54
|
+
children.map do |child|
|
|
55
|
+
case child
|
|
56
|
+
when Node::Root then child.flatten
|
|
57
|
+
when Node::Text then child.value
|
|
58
|
+
else
|
|
59
|
+
fail NodeError.new("Unexpected node `#{child.class}",
|
|
60
|
+
node.location)
|
|
61
|
+
end
|
|
62
|
+
end.join
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def derive_location(children)
|
|
68
|
+
children.map(&:location).inject(:union) || Yoga::Location.default
|
|
69
|
+
rescue
|
|
70
|
+
Yoga::Location.default
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def update_children(children)
|
|
74
|
+
Root.new(children: children, location: @location)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update_location(location)
|
|
78
|
+
Root.new(children: @children, location: location)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Brandish
|
|
5
|
+
class Parser
|
|
6
|
+
class Node
|
|
7
|
+
# A "string." Because of how loose the language is, this is similar to
|
|
8
|
+
# a {Text} node, but has no restriction on the allowed values for the
|
|
9
|
+
# node.
|
|
10
|
+
class String < Text
|
|
11
|
+
# A set of tokens kinds that are allowed to be in a string node.
|
|
12
|
+
#
|
|
13
|
+
# @return [::Set<::Symbol>]
|
|
14
|
+
TOKENS = (Node::Text::TOKENS - ::Set[:'"']) + ::Set[:<, :>]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "set"
|
|
5
|
+
|
|
6
|
+
module Brandish
|
|
7
|
+
class Parser
|
|
8
|
+
class Node
|
|
9
|
+
# A text node. This contains only text, and has no other node
|
|
10
|
+
# decendants. This node automatically merges the contents of the text
|
|
11
|
+
# into a single value, and places it in the `value` attribute.
|
|
12
|
+
class Text < Node
|
|
13
|
+
# A set of tokens kinds that are allowed to be in a text node.
|
|
14
|
+
#
|
|
15
|
+
# @return [::Set<::Symbol>]
|
|
16
|
+
TOKENS =
|
|
17
|
+
::Set[:SPACE, :TEXT, :LINE, :NUMERIC, :ESCAPE, :/, :'"', :'='].freeze
|
|
18
|
+
|
|
19
|
+
# The value of the text node. This is the string value of the source
|
|
20
|
+
# text that this node is based off of.
|
|
21
|
+
#
|
|
22
|
+
# @return [::String]
|
|
23
|
+
attr_reader :value
|
|
24
|
+
|
|
25
|
+
# Initialize the node.
|
|
26
|
+
#
|
|
27
|
+
# @overload initialize(tokens:, location: nil)
|
|
28
|
+
# Initialize the text node with the given series of tokens. The
|
|
29
|
+
# location can be either provided, or assumed from the locations of
|
|
30
|
+
# the tokens. The tokens are concatenated into a single string,
|
|
31
|
+
# and used to provide the text.
|
|
32
|
+
#
|
|
33
|
+
# @param tokens [<Scanner::Token>] The tokens that make up
|
|
34
|
+
# this text node.
|
|
35
|
+
# @param location [Location] The location of the text node. If none
|
|
36
|
+
# is provided, it is assumed from the tokens.
|
|
37
|
+
# @overload initialize(value:, location:)
|
|
38
|
+
# Initialize the text node with the given value. The location must
|
|
39
|
+
# be provided.
|
|
40
|
+
#
|
|
41
|
+
# @param value [::String] The value of the text node.
|
|
42
|
+
# @param location [Location] The location of the text node.
|
|
43
|
+
def initialize(tokens: nil, value: nil, location: nil)
|
|
44
|
+
unless tokens || (value && location)
|
|
45
|
+
fail ::ArgumentError, "Expected either a set of tokens or a " \
|
|
46
|
+
"value and a location, got neither"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@value = value || derive_value(tokens)
|
|
50
|
+
@location = location || derive_location(tokens)
|
|
51
|
+
freeze
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Pretty inspect.
|
|
55
|
+
#
|
|
56
|
+
# @return [::String]
|
|
57
|
+
def inspect
|
|
58
|
+
"#<#{self.class} value=#{@value.inspect} " \
|
|
59
|
+
"location=#{@location.inspect}>"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Determines if this object equals another object. If the other object
|
|
63
|
+
# is this object, it returns true; otherwise, if the other object is a
|
|
64
|
+
# {Text}, and all of the properties are equal, it returns true;
|
|
65
|
+
# otherwise, it returns false.
|
|
66
|
+
#
|
|
67
|
+
# @param other [::Object]
|
|
68
|
+
# @return [Boolean]
|
|
69
|
+
def ==(other)
|
|
70
|
+
equal?(other) || other.is_a?(self.class) && @value == other.value &&
|
|
71
|
+
@location == other.location
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def update_value(value)
|
|
77
|
+
Text.new(value: value, location: @location)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def update_tokens(tokens)
|
|
81
|
+
update_value(derive_value(tokens))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def update_location(location)
|
|
85
|
+
Text.new(value: @value, location: location)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def derive_location(tokens)
|
|
89
|
+
unless tokens
|
|
90
|
+
fail ::ArgumentError, "Expected either location or tokens, got" \
|
|
91
|
+
" neither"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
tokens.map(&:location).inject(:union)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def derive_value(tokens)
|
|
98
|
+
assert_valid_tokens(tokens)
|
|
99
|
+
tokens
|
|
100
|
+
.map { |t| t.kind == :ESCAPE ? t.value[-1] : t.value }
|
|
101
|
+
.join
|
|
102
|
+
.freeze
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def assert_valid_tokens(tokens)
|
|
106
|
+
valid = tokens.all? { |t| TOKENS.include?(t.kind) }
|
|
107
|
+
return valid if valid
|
|
108
|
+
fail ::ArgumentError, "Expected tokens to all be one of " \
|
|
109
|
+
"#{TOKENS.map(&:inspect).join(', ')}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|