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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +41 -0
  5. data/.travis.yml +5 -0
  6. data/.yardopts +1 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +10 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +41 -0
  11. data/Rakefile +9 -0
  12. data/bin/brandish +16 -0
  13. data/brandish.gemspec +39 -0
  14. data/defaults/templates/html.liquid +39 -0
  15. data/lib/brandish.rb +51 -0
  16. data/lib/brandish/application.rb +163 -0
  17. data/lib/brandish/application/bench_command.rb +96 -0
  18. data/lib/brandish/application/build_command.rb +73 -0
  19. data/lib/brandish/application/initialize_command.rb +83 -0
  20. data/lib/brandish/application/serve_command.rb +150 -0
  21. data/lib/brandish/configure.rb +196 -0
  22. data/lib/brandish/configure/dsl.rb +135 -0
  23. data/lib/brandish/configure/dsl/form.rb +136 -0
  24. data/lib/brandish/configure/form.rb +32 -0
  25. data/lib/brandish/errors.rb +65 -0
  26. data/lib/brandish/execute.rb +26 -0
  27. data/lib/brandish/markup.rb +10 -0
  28. data/lib/brandish/markup/redcarpet.rb +14 -0
  29. data/lib/brandish/markup/redcarpet/format.rb +127 -0
  30. data/lib/brandish/markup/redcarpet/html.rb +95 -0
  31. data/lib/brandish/parser.rb +26 -0
  32. data/lib/brandish/parser/main.rb +237 -0
  33. data/lib/brandish/parser/node.rb +89 -0
  34. data/lib/brandish/parser/node/block.rb +98 -0
  35. data/lib/brandish/parser/node/command.rb +102 -0
  36. data/lib/brandish/parser/node/pair.rb +42 -0
  37. data/lib/brandish/parser/node/root.rb +83 -0
  38. data/lib/brandish/parser/node/string.rb +18 -0
  39. data/lib/brandish/parser/node/text.rb +114 -0
  40. data/lib/brandish/path_set.rb +163 -0
  41. data/lib/brandish/processor.rb +47 -0
  42. data/lib/brandish/processor/base.rb +144 -0
  43. data/lib/brandish/processor/block.rb +47 -0
  44. data/lib/brandish/processor/command.rb +47 -0
  45. data/lib/brandish/processor/context.rb +169 -0
  46. data/lib/brandish/processor/descend.rb +32 -0
  47. data/lib/brandish/processor/inline.rb +49 -0
  48. data/lib/brandish/processor/name_filter.rb +67 -0
  49. data/lib/brandish/processor/pair_filter.rb +96 -0
  50. data/lib/brandish/processors.rb +26 -0
  51. data/lib/brandish/processors/all.rb +19 -0
  52. data/lib/brandish/processors/all/comment.rb +29 -0
  53. data/lib/brandish/processors/all/embed.rb +56 -0
  54. data/lib/brandish/processors/all/if.rb +109 -0
  55. data/lib/brandish/processors/all/import.rb +95 -0
  56. data/lib/brandish/processors/all/literal.rb +42 -0
  57. data/lib/brandish/processors/all/verify.rb +47 -0
  58. data/lib/brandish/processors/common.rb +20 -0
  59. data/lib/brandish/processors/common/asset.rb +118 -0
  60. data/lib/brandish/processors/common/asset/paths.rb +93 -0
  61. data/lib/brandish/processors/common/group.rb +67 -0
  62. data/lib/brandish/processors/common/header.rb +86 -0
  63. data/lib/brandish/processors/common/markup.rb +127 -0
  64. data/lib/brandish/processors/common/output.rb +73 -0
  65. data/lib/brandish/processors/html.rb +18 -0
  66. data/lib/brandish/processors/html/group.rb +33 -0
  67. data/lib/brandish/processors/html/header.rb +46 -0
  68. data/lib/brandish/processors/html/markup.rb +131 -0
  69. data/lib/brandish/processors/html/output.rb +62 -0
  70. data/lib/brandish/processors/html/output/document.rb +127 -0
  71. data/lib/brandish/processors/html/script.rb +64 -0
  72. data/lib/brandish/processors/html/script/babel.rb +48 -0
  73. data/lib/brandish/processors/html/script/coffee.rb +47 -0
  74. data/lib/brandish/processors/html/script/vanilla.rb +45 -0
  75. data/lib/brandish/processors/html/style.rb +82 -0
  76. data/lib/brandish/processors/html/style/highlight.rb +89 -0
  77. data/lib/brandish/processors/html/style/sass.rb +64 -0
  78. data/lib/brandish/processors/html/style/vanilla.rb +71 -0
  79. data/lib/brandish/processors/latex.rb +15 -0
  80. data/lib/brandish/processors/latex/markup.rb +47 -0
  81. data/lib/brandish/scanner.rb +64 -0
  82. data/lib/brandish/version.rb +9 -0
  83. data/templates/initialize/Gemfile.tt +14 -0
  84. data/templates/initialize/brandish.config.rb.tt +49 -0
  85. 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