antelope 0.2.0 → 0.2.2
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/.gitignore +25 -23
- data/.rspec +3 -3
- data/.travis.yml +10 -9
- data/.yardopts +7 -7
- data/CONTRIBUTING.md +38 -38
- data/GENERATORS.md +124 -124
- data/Gemfile +7 -7
- data/LICENSE.txt +22 -22
- data/README.md +104 -104
- data/Rakefile +2 -2
- data/TODO.md +58 -58
- data/antelope.gemspec +28 -28
- data/bin/antelope +7 -7
- data/examples/deterministic.ace +35 -35
- data/examples/example.ace +51 -50
- data/examples/example.err +192 -0
- data/examples/{example.output → example.inf} +384 -385
- data/examples/liquidscript.ace +233 -162
- data/examples/simple.ace +22 -22
- data/lib/antelope/ace/compiler.rb +334 -334
- data/lib/antelope/ace/errors.rb +48 -48
- data/lib/antelope/ace/grammar/generation.rb +80 -80
- data/lib/antelope/ace/grammar/loading.rb +53 -53
- data/lib/antelope/ace/grammar/precedences.rb +68 -65
- data/lib/antelope/ace/grammar/productions.rb +156 -150
- data/lib/antelope/ace/grammar/symbols.rb +66 -66
- data/lib/antelope/ace/grammar.rb +69 -69
- data/lib/antelope/ace/precedence.rb +61 -61
- data/lib/antelope/ace/production.rb +57 -57
- data/lib/antelope/ace/scanner/argument.rb +57 -57
- data/lib/antelope/ace/scanner/first.rb +89 -89
- data/lib/antelope/ace/scanner/second.rb +177 -177
- data/lib/antelope/ace/scanner/third.rb +27 -27
- data/lib/antelope/ace/scanner.rb +134 -134
- data/lib/antelope/ace/token/epsilon.rb +24 -24
- data/lib/antelope/ace/token/error.rb +26 -26
- data/lib/antelope/ace/token/nonterminal.rb +17 -17
- data/lib/antelope/ace/token/terminal.rb +17 -17
- data/lib/antelope/ace/token.rb +238 -238
- data/lib/antelope/ace.rb +53 -53
- data/lib/antelope/cli.rb +55 -55
- data/lib/antelope/errors.rb +8 -8
- data/lib/antelope/generation/constructor/first.rb +88 -88
- data/lib/antelope/generation/constructor/follow.rb +103 -103
- data/lib/antelope/generation/constructor/nullable.rb +64 -64
- data/lib/antelope/generation/constructor.rb +126 -126
- data/lib/antelope/generation/errors.rb +17 -17
- data/lib/antelope/generation/null.rb +13 -13
- data/lib/antelope/generation/recognizer/rule.rb +216 -216
- data/lib/antelope/generation/recognizer/state.rb +130 -130
- data/lib/antelope/generation/recognizer.rb +180 -180
- data/lib/antelope/generation/tableizer.rb +175 -154
- data/lib/antelope/generation.rb +15 -15
- data/lib/antelope/generator/base.rb +264 -264
- data/lib/antelope/generator/c.rb +11 -11
- data/lib/antelope/generator/c_header.rb +105 -105
- data/lib/antelope/generator/c_source.rb +39 -39
- data/lib/antelope/generator/error.rb +34 -0
- data/lib/antelope/generator/group.rb +57 -57
- data/lib/antelope/generator/html.rb +51 -0
- data/lib/antelope/generator/info.rb +47 -0
- data/lib/antelope/generator/null.rb +18 -18
- data/lib/antelope/generator/output.rb +17 -49
- data/lib/antelope/generator/ruby.rb +79 -79
- data/lib/antelope/generator/templates/c_header.ant +36 -36
- data/lib/antelope/generator/templates/c_source.ant +202 -202
- data/lib/antelope/generator/templates/error.ant +33 -0
- data/lib/antelope/generator/templates/html/antelope.css +1 -0
- data/lib/antelope/generator/templates/html/antelope.html +1 -0
- data/lib/antelope/generator/templates/html/antelope.js +1 -0
- data/lib/antelope/generator/templates/html/css.ant +53 -0
- data/lib/antelope/generator/templates/html/html.ant +82 -0
- data/lib/antelope/generator/templates/html/js.ant +9 -0
- data/lib/antelope/generator/templates/info.ant +53 -0
- data/lib/antelope/generator/templates/ruby.ant +178 -146
- data/lib/antelope/generator.rb +66 -63
- data/lib/antelope/template/compiler.rb +78 -78
- data/lib/antelope/template/errors.rb +9 -9
- data/lib/antelope/template/scanner.rb +109 -109
- data/lib/antelope/template.rb +65 -60
- data/lib/antelope/version.rb +6 -6
- data/lib/antelope.rb +13 -13
- data/optimizations.txt +42 -0
- data/spec/antelope/ace/compiler_spec.rb +60 -60
- data/spec/antelope/ace/scanner_spec.rb +27 -27
- data/spec/antelope/constructor_spec.rb +133 -136
- data/spec/antelope/template_spec.rb +50 -49
- data/spec/fixtures/simple.ace +22 -22
- data/spec/spec_helper.rb +39 -39
- data/spec/support/benchmark_helper.rb +5 -5
- data/spec/support/grammar_helper.rb +15 -15
- data/subl/Ace (Ruby).JSON-tmLanguage +94 -94
- data/subl/Ace (Ruby).tmLanguage +153 -153
- metadata +17 -6
- data/lib/antelope/generator/templates/output.ant +0 -68
@@ -1,61 +1,61 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Antelope
|
4
|
-
module Ace
|
5
|
-
|
6
|
-
# Defines a precedence. A precedence has a type, tokens, and a
|
7
|
-
# level.
|
8
|
-
class Precedence < Struct.new(:type, :tokens, :level)
|
9
|
-
|
10
|
-
# @!attribute [rw] type
|
11
|
-
# The type of precedence level. This should be one of
|
12
|
-
# `:left`, `:right`, or `:nonassoc`.
|
13
|
-
#
|
14
|
-
# @return [Symbol] the type.
|
15
|
-
# @!attribute [rw] tokens
|
16
|
-
# An set of tokens that are on this specific precedence
|
17
|
-
# level. The tokens are identified as symbols. The special
|
18
|
-
# symbol, `:_`, represents any token.
|
19
|
-
#
|
20
|
-
# @return [Set<Symbol>] the tokens on this level.
|
21
|
-
# @!attribute [rw] level
|
22
|
-
# The level we're on. The higher the level, the higher the
|
23
|
-
# precedence.
|
24
|
-
|
25
|
-
include Comparable
|
26
|
-
|
27
|
-
# Compares the other object to this object. If the other object
|
28
|
-
# isn't a {Precedence}, it returns nil. If the other
|
29
|
-
# precedence isn't on the same level as this one, then the
|
30
|
-
# levels are compared and the result of that is returned. If
|
31
|
-
# it is, however, the type is checked; if this precedence is
|
32
|
-
# left associative, then it returns 1 (it is greater than the
|
33
|
-
# other); if this precedence is right associative, then it
|
34
|
-
# returns -1 (it is less than the other); if this precedence is
|
35
|
-
# nonassociative, it returns 0 (it is equal to the other).
|
36
|
-
#
|
37
|
-
# @param other [Object] the object to compare to this one.
|
38
|
-
# @return [Numeric?]
|
39
|
-
def <=>(other)
|
40
|
-
return nil unless other.is_a? Precedence
|
41
|
-
if level != other.level
|
42
|
-
level <=> other.level
|
43
|
-
elsif type == :left
|
44
|
-
1
|
45
|
-
elsif type == :right
|
46
|
-
-1
|
47
|
-
else
|
48
|
-
0
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Converts the precedence into a representative string, denoting
|
53
|
-
# the type and the level.
|
54
|
-
#
|
55
|
-
# @return [String]
|
56
|
-
def to_s
|
57
|
-
"#{type.to_s[0]}#{level}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
module Ace
|
5
|
+
|
6
|
+
# Defines a precedence. A precedence has a type, tokens, and a
|
7
|
+
# level.
|
8
|
+
class Precedence < Struct.new(:type, :tokens, :level)
|
9
|
+
|
10
|
+
# @!attribute [rw] type
|
11
|
+
# The type of precedence level. This should be one of
|
12
|
+
# `:left`, `:right`, or `:nonassoc`.
|
13
|
+
#
|
14
|
+
# @return [Symbol] the type.
|
15
|
+
# @!attribute [rw] tokens
|
16
|
+
# An set of tokens that are on this specific precedence
|
17
|
+
# level. The tokens are identified as symbols. The special
|
18
|
+
# symbol, `:_`, represents any token.
|
19
|
+
#
|
20
|
+
# @return [Set<Symbol>] the tokens on this level.
|
21
|
+
# @!attribute [rw] level
|
22
|
+
# The level we're on. The higher the level, the higher the
|
23
|
+
# precedence.
|
24
|
+
|
25
|
+
include Comparable
|
26
|
+
|
27
|
+
# Compares the other object to this object. If the other object
|
28
|
+
# isn't a {Precedence}, it returns nil. If the other
|
29
|
+
# precedence isn't on the same level as this one, then the
|
30
|
+
# levels are compared and the result of that is returned. If
|
31
|
+
# it is, however, the type is checked; if this precedence is
|
32
|
+
# left associative, then it returns 1 (it is greater than the
|
33
|
+
# other); if this precedence is right associative, then it
|
34
|
+
# returns -1 (it is less than the other); if this precedence is
|
35
|
+
# nonassociative, it returns 0 (it is equal to the other).
|
36
|
+
#
|
37
|
+
# @param other [Object] the object to compare to this one.
|
38
|
+
# @return [Numeric?]
|
39
|
+
def <=>(other)
|
40
|
+
return nil unless other.is_a? Precedence
|
41
|
+
if level != other.level
|
42
|
+
level <=> other.level
|
43
|
+
elsif type == :left
|
44
|
+
1
|
45
|
+
elsif type == :right
|
46
|
+
-1
|
47
|
+
else
|
48
|
+
0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Converts the precedence into a representative string, denoting
|
53
|
+
# the type and the level.
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
def to_s
|
57
|
+
"#{type.to_s[0]}#{level}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,57 +1,57 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Antelope
|
4
|
-
module Ace
|
5
|
-
|
6
|
-
# Defines a production.
|
7
|
-
class Production < Struct.new(:label, :items, :block, :prec, :id)
|
8
|
-
# @!attribute [rw] label
|
9
|
-
# The label (or left-hand side) of the production. This
|
10
|
-
# should be a nonterminal.
|
11
|
-
#
|
12
|
-
# @return [Symbol]
|
13
|
-
# @!attribute [rw] items
|
14
|
-
# The body (or right-hand side) of the production. This can
|
15
|
-
# be array of terminals and nonterminals.
|
16
|
-
#
|
17
|
-
# @return [Array<Token>]
|
18
|
-
# @!attribute [rw] block
|
19
|
-
# The block of code to be executed when the production's right
|
20
|
-
# hand side is reduced.
|
21
|
-
#
|
22
|
-
# @return [String]
|
23
|
-
# @!attribute [rw] prec
|
24
|
-
# The precedence declaration for the production.
|
25
|
-
#
|
26
|
-
# @return [Ace::Precedence]
|
27
|
-
# @!attribute [rw] id
|
28
|
-
# The ID of the production. The starting production always
|
29
|
-
# has an ID of 0.
|
30
|
-
#
|
31
|
-
# @return [Numeric]
|
32
|
-
|
33
|
-
# Creates a new production from a hash. The hash's keys
|
34
|
-
# correspond to the attributes on this class.
|
35
|
-
#
|
36
|
-
# @param hash [Hash<(Symbol, Object)>]
|
37
|
-
def self.from_hash(hash)
|
38
|
-
new(hash[:label] || hash["label"],
|
39
|
-
hash[:items] || hash["items"],
|
40
|
-
hash[:block] || hash["block"],
|
41
|
-
hash[:prec] || hash["prec"],
|
42
|
-
hash[:id] || hash["id"])
|
43
|
-
end
|
44
|
-
|
45
|
-
# Create a new version of the production with duplicated values.
|
46
|
-
#
|
47
|
-
# @return [Production]
|
48
|
-
def clone
|
49
|
-
Production.new(label.dup,
|
50
|
-
items.map(&:dup),
|
51
|
-
block.dup,
|
52
|
-
prec.dup,
|
53
|
-
id)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
module Ace
|
5
|
+
|
6
|
+
# Defines a production.
|
7
|
+
class Production < Struct.new(:label, :items, :block, :prec, :id)
|
8
|
+
# @!attribute [rw] label
|
9
|
+
# The label (or left-hand side) of the production. This
|
10
|
+
# should be a nonterminal.
|
11
|
+
#
|
12
|
+
# @return [Symbol]
|
13
|
+
# @!attribute [rw] items
|
14
|
+
# The body (or right-hand side) of the production. This can
|
15
|
+
# be array of terminals and nonterminals.
|
16
|
+
#
|
17
|
+
# @return [Array<Token>]
|
18
|
+
# @!attribute [rw] block
|
19
|
+
# The block of code to be executed when the production's right
|
20
|
+
# hand side is reduced.
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
# @!attribute [rw] prec
|
24
|
+
# The precedence declaration for the production.
|
25
|
+
#
|
26
|
+
# @return [Ace::Precedence]
|
27
|
+
# @!attribute [rw] id
|
28
|
+
# The ID of the production. The starting production always
|
29
|
+
# has an ID of 0.
|
30
|
+
#
|
31
|
+
# @return [Numeric]
|
32
|
+
|
33
|
+
# Creates a new production from a hash. The hash's keys
|
34
|
+
# correspond to the attributes on this class.
|
35
|
+
#
|
36
|
+
# @param hash [Hash<(Symbol, Object)>]
|
37
|
+
def self.from_hash(hash)
|
38
|
+
new(hash[:label] || hash["label"],
|
39
|
+
hash[:items] || hash["items"],
|
40
|
+
hash[:block] || hash["block"],
|
41
|
+
hash[:prec] || hash["prec"],
|
42
|
+
hash[:id] || hash["id"])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create a new version of the production with duplicated values.
|
46
|
+
#
|
47
|
+
# @return [Production]
|
48
|
+
def clone
|
49
|
+
Production.new(label.dup,
|
50
|
+
items.map(&:dup),
|
51
|
+
block.dup,
|
52
|
+
prec.dup,
|
53
|
+
id)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,57 +1,57 @@
|
|
1
|
-
module Antelope
|
2
|
-
module Ace
|
3
|
-
class Scanner
|
4
|
-
|
5
|
-
# Represents an argument to a directive. It encapsulates a
|
6
|
-
# string object, which is the value of the argument.
|
7
|
-
class Argument < String
|
8
|
-
|
9
|
-
# Initialize the argument.
|
10
|
-
#
|
11
|
-
# @param type [Symbol] the type of argument it is; it can be
|
12
|
-
# a `:block`, `:text`, or `:caret`. The type is defined by
|
13
|
-
# the encapsulating characters. If the encapsulating
|
14
|
-
# characters are `{` and `}`, it's a `:block`; if they are
|
15
|
-
# `<` and `>`, it's a `:caret`; otherwise, it's a `:text`.
|
16
|
-
# @param value [String] the value of the argument.
|
17
|
-
def initialize(type, value)
|
18
|
-
@type = type
|
19
|
-
super(value)
|
20
|
-
end
|
21
|
-
|
22
|
-
# If this argument is type `:block`.
|
23
|
-
#
|
24
|
-
# @return [Boolean]
|
25
|
-
# @see type?
|
26
|
-
def block?
|
27
|
-
type? :block
|
28
|
-
end
|
29
|
-
|
30
|
-
# If this argument is type `:text`.
|
31
|
-
#
|
32
|
-
# @return [Boolean]
|
33
|
-
# @see type?
|
34
|
-
def text?
|
35
|
-
type? :text
|
36
|
-
end
|
37
|
-
|
38
|
-
# If this argument is type `:caret`.
|
39
|
-
#
|
40
|
-
# @return [Boolean]
|
41
|
-
# @see type?
|
42
|
-
def caret?
|
43
|
-
type? :caret
|
44
|
-
end
|
45
|
-
|
46
|
-
# Checks to see if any of the given arguments match the type
|
47
|
-
# of this argument.
|
48
|
-
#
|
49
|
-
# @param inc [Array<Symbol>]
|
50
|
-
# @return [Boolean]
|
51
|
-
def type?(*inc)
|
52
|
-
inc.include?(@type)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
1
|
+
module Antelope
|
2
|
+
module Ace
|
3
|
+
class Scanner
|
4
|
+
|
5
|
+
# Represents an argument to a directive. It encapsulates a
|
6
|
+
# string object, which is the value of the argument.
|
7
|
+
class Argument < String
|
8
|
+
|
9
|
+
# Initialize the argument.
|
10
|
+
#
|
11
|
+
# @param type [Symbol] the type of argument it is; it can be
|
12
|
+
# a `:block`, `:text`, or `:caret`. The type is defined by
|
13
|
+
# the encapsulating characters. If the encapsulating
|
14
|
+
# characters are `{` and `}`, it's a `:block`; if they are
|
15
|
+
# `<` and `>`, it's a `:caret`; otherwise, it's a `:text`.
|
16
|
+
# @param value [String] the value of the argument.
|
17
|
+
def initialize(type, value)
|
18
|
+
@type = type
|
19
|
+
super(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
# If this argument is type `:block`.
|
23
|
+
#
|
24
|
+
# @return [Boolean]
|
25
|
+
# @see type?
|
26
|
+
def block?
|
27
|
+
type? :block
|
28
|
+
end
|
29
|
+
|
30
|
+
# If this argument is type `:text`.
|
31
|
+
#
|
32
|
+
# @return [Boolean]
|
33
|
+
# @see type?
|
34
|
+
def text?
|
35
|
+
type? :text
|
36
|
+
end
|
37
|
+
|
38
|
+
# If this argument is type `:caret`.
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
# @see type?
|
42
|
+
def caret?
|
43
|
+
type? :caret
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks to see if any of the given arguments match the type
|
47
|
+
# of this argument.
|
48
|
+
#
|
49
|
+
# @param inc [Array<Symbol>]
|
50
|
+
# @return [Boolean]
|
51
|
+
def type?(*inc)
|
52
|
+
inc.include?(@type)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,89 +1,89 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Antelope
|
4
|
-
module Ace
|
5
|
-
class Scanner
|
6
|
-
|
7
|
-
# Scans the first section of the file. This contains directives and
|
8
|
-
# small blocks that can be copied directly into the body of the output.
|
9
|
-
# The blocks are formatted as `%{ ... %}`; however, the ending tag _must_
|
10
|
-
# be on its own line. The directive is formatted as `%<name> <value>`,
|
11
|
-
# with `<name>` being the key, and `<value>` being the value. The value
|
12
|
-
# can be a piece of straight-up text (no quotes), or it can be quoted.
|
13
|
-
# There can be any number of values to a directive.
|
14
|
-
module First
|
15
|
-
|
16
|
-
# Scans until the first content boundry. If it encounters anything but
|
17
|
-
# a block or a directive (or whitespace), it will raise an error.
|
18
|
-
#
|
19
|
-
# @raise [SyntaxError] if it encounters anything but whitespace, a
|
20
|
-
# block, or a directive.
|
21
|
-
# @return [void]
|
22
|
-
def scan_first_part
|
23
|
-
until @scanner.check(CONTENT_BOUNDRY)
|
24
|
-
scan_first_copy || scan_first_directive ||
|
25
|
-
scan_whitespace || error!
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Scans for a block. It is called `copy` instead of `block` because
|
30
|
-
# contents of the block is _copied_ directly into the body.
|
31
|
-
#
|
32
|
-
# @return [Boolean] if it matched.
|
33
|
-
def scan_first_copy
|
34
|
-
if @scanner.scan(/%{([\s\S]+?)\n\s*%}/)
|
35
|
-
tokens << [:copy, @scanner[1]]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# Scans a directive. A directive has one _name_, and any number of
|
40
|
-
# arguments. Every argument is a _value_. The name can be any
|
41
|
-
# combinations of alphabetical characters, underscores, and dashes;
|
42
|
-
# the value can be word characters, or a quote-delimited string.
|
43
|
-
# It emits a `:directive` token with the directive (Sring) as an
|
44
|
-
# argument, and the passed arguments (Array<String>).
|
45
|
-
#
|
46
|
-
# @return [Boolean] if it matched.
|
47
|
-
def scan_first_directive
|
48
|
-
if @scanner.scan(/%(#{IDENTIFIER}) ?/)
|
49
|
-
directive = @scanner[1]
|
50
|
-
arguments = scan_first_directive_arguments
|
51
|
-
|
52
|
-
tokens << [:directive, directive, arguments]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# Scan the arguments for a directive. It keeps attempting to
|
57
|
-
# scan arguments until the first newline that was not in a
|
58
|
-
# block. Arguments can be blocks, carets, or text; blocks are
|
59
|
-
# encapsulated with `{` and `}`, carets are encapsulated with
|
60
|
-
# `<` and `>`, and text is encapsulated with quotes or
|
61
|
-
# nothing.
|
62
|
-
#
|
63
|
-
# @return [Array<Argument>]
|
64
|
-
def scan_first_directive_arguments
|
65
|
-
arguments = []
|
66
|
-
until @scanner.check(/\n/)
|
67
|
-
if @scanner.scan(/\{/)
|
68
|
-
argument =
|
69
|
-
Argument.new(:block, _scan_block[1..-2])
|
70
|
-
elsif @scanner.scan(/</)
|
71
|
-
@scanner.scan(/((?:\\>|[^>])*)\>/)
|
72
|
-
argument =
|
73
|
-
Argument.new(:caret, @scanner[1])
|
74
|
-
else
|
75
|
-
@scanner.scan(/#{VALUE}/x) or error!
|
76
|
-
argument = Argument.new(:text,
|
77
|
-
@scanner[2] || @scanner[3])
|
78
|
-
end
|
79
|
-
|
80
|
-
arguments.push(argument)
|
81
|
-
@scanner.scan(/ */)
|
82
|
-
end
|
83
|
-
|
84
|
-
arguments
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
module Ace
|
5
|
+
class Scanner
|
6
|
+
|
7
|
+
# Scans the first section of the file. This contains directives and
|
8
|
+
# small blocks that can be copied directly into the body of the output.
|
9
|
+
# The blocks are formatted as `%{ ... %}`; however, the ending tag _must_
|
10
|
+
# be on its own line. The directive is formatted as `%<name> <value>`,
|
11
|
+
# with `<name>` being the key, and `<value>` being the value. The value
|
12
|
+
# can be a piece of straight-up text (no quotes), or it can be quoted.
|
13
|
+
# There can be any number of values to a directive.
|
14
|
+
module First
|
15
|
+
|
16
|
+
# Scans until the first content boundry. If it encounters anything but
|
17
|
+
# a block or a directive (or whitespace), it will raise an error.
|
18
|
+
#
|
19
|
+
# @raise [SyntaxError] if it encounters anything but whitespace, a
|
20
|
+
# block, or a directive.
|
21
|
+
# @return [void]
|
22
|
+
def scan_first_part
|
23
|
+
until @scanner.check(CONTENT_BOUNDRY)
|
24
|
+
scan_first_copy || scan_first_directive ||
|
25
|
+
scan_whitespace || error!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Scans for a block. It is called `copy` instead of `block` because
|
30
|
+
# contents of the block is _copied_ directly into the body.
|
31
|
+
#
|
32
|
+
# @return [Boolean] if it matched.
|
33
|
+
def scan_first_copy
|
34
|
+
if @scanner.scan(/%{([\s\S]+?)\n\s*%}/)
|
35
|
+
tokens << [:copy, @scanner[1]]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Scans a directive. A directive has one _name_, and any number of
|
40
|
+
# arguments. Every argument is a _value_. The name can be any
|
41
|
+
# combinations of alphabetical characters, underscores, and dashes;
|
42
|
+
# the value can be word characters, or a quote-delimited string.
|
43
|
+
# It emits a `:directive` token with the directive (Sring) as an
|
44
|
+
# argument, and the passed arguments (Array<String>).
|
45
|
+
#
|
46
|
+
# @return [Boolean] if it matched.
|
47
|
+
def scan_first_directive
|
48
|
+
if @scanner.scan(/%(#{IDENTIFIER}) ?/)
|
49
|
+
directive = @scanner[1]
|
50
|
+
arguments = scan_first_directive_arguments
|
51
|
+
|
52
|
+
tokens << [:directive, directive, arguments]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Scan the arguments for a directive. It keeps attempting to
|
57
|
+
# scan arguments until the first newline that was not in a
|
58
|
+
# block. Arguments can be blocks, carets, or text; blocks are
|
59
|
+
# encapsulated with `{` and `}`, carets are encapsulated with
|
60
|
+
# `<` and `>`, and text is encapsulated with quotes or
|
61
|
+
# nothing.
|
62
|
+
#
|
63
|
+
# @return [Array<Argument>]
|
64
|
+
def scan_first_directive_arguments
|
65
|
+
arguments = []
|
66
|
+
until @scanner.check(/\n/)
|
67
|
+
if @scanner.scan(/\{/)
|
68
|
+
argument =
|
69
|
+
Argument.new(:block, _scan_block[1..-2])
|
70
|
+
elsif @scanner.scan(/</)
|
71
|
+
@scanner.scan(/((?:\\>|[^>])*)\>/)
|
72
|
+
argument =
|
73
|
+
Argument.new(:caret, @scanner[1])
|
74
|
+
else
|
75
|
+
@scanner.scan(/#{VALUE}/x) or error!
|
76
|
+
argument = Argument.new(:text,
|
77
|
+
@scanner[2] || @scanner[3])
|
78
|
+
end
|
79
|
+
|
80
|
+
arguments.push(argument)
|
81
|
+
@scanner.scan(/ */)
|
82
|
+
end
|
83
|
+
|
84
|
+
arguments
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|