opulent 0.0.9 → 1.0.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 +9 -0
- data/.libold/opulent.rb +96 -0
- data/.libold/opulent/context.rb +80 -0
- data/.libold/opulent/engine.rb +88 -0
- data/.libold/opulent/filter.rb +101 -0
- data/.libold/opulent/logger.rb +67 -0
- data/.libold/opulent/nodes.rb +13 -0
- data/.libold/opulent/nodes/block.rb +29 -0
- data/.libold/opulent/nodes/comment.rb +35 -0
- data/.libold/opulent/nodes/control.rb +230 -0
- data/.libold/opulent/nodes/define.rb +42 -0
- data/.libold/opulent/nodes/eval.rb +41 -0
- data/.libold/opulent/nodes/expression.rb +28 -0
- data/.libold/opulent/nodes/filter.rb +70 -0
- data/.libold/opulent/nodes/helper.rb +69 -0
- data/.libold/opulent/nodes/node.rb +101 -0
- data/.libold/opulent/nodes/root.rb +62 -0
- data/.libold/opulent/nodes/text.rb +54 -0
- data/.libold/opulent/nodes/theme.rb +36 -0
- data/.libold/opulent/parser.rb +252 -0
- data/.libold/opulent/parser/block.rb +70 -0
- data/.libold/opulent/parser/comment.rb +32 -0
- data/.libold/opulent/parser/control.rb +83 -0
- data/.libold/opulent/parser/define.rb +39 -0
- data/.libold/opulent/parser/eval.rb +33 -0
- data/.libold/opulent/parser/expression.rb +350 -0
- data/.libold/opulent/parser/filter.rb +41 -0
- data/.libold/opulent/parser/node.rb +232 -0
- data/.libold/opulent/parser/root.rb +96 -0
- data/.libold/opulent/parser/text.rb +114 -0
- data/.libold/opulent/parser/theme.rb +36 -0
- data/.libold/opulent/preprocessor.rb +102 -0
- data/.libold/opulent/runtime.rb +144 -0
- data/.libold/opulent/template.rb +43 -0
- data/.libold/opulent/tokens.rb +276 -0
- data/.libold/opulent/version.rb +5 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +102 -0
- data/Rakefile +6 -0
- data/benchmark/benchmark.rb +46 -0
- data/benchmark/cases/node/node.haml +17 -0
- data/benchmark/cases/node/node.op +14 -0
- data/benchmark/cases/node/node.slim +19 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/docs/syntax.md +3 -0
- data/docs/usage.md +3 -0
- data/lib/opulent.rb +11 -64
- data/lib/opulent/compiler.rb +132 -0
- data/lib/opulent/compiler/block.rb +31 -0
- data/lib/opulent/compiler/comment.rb +22 -0
- data/lib/opulent/compiler/control.rb +152 -0
- data/lib/opulent/compiler/define.rb +88 -0
- data/lib/opulent/compiler/eval.rb +15 -0
- data/lib/opulent/compiler/filter.rb +54 -0
- data/lib/opulent/compiler/node.rb +232 -0
- data/lib/opulent/compiler/root.rb +19 -0
- data/lib/opulent/compiler/text.rb +60 -0
- data/lib/opulent/context.rb +88 -0
- data/lib/opulent/engine.rb +60 -0
- data/lib/opulent/filters.rb +222 -0
- data/lib/opulent/logger.rb +47 -0
- data/lib/opulent/parser.rb +196 -0
- data/lib/opulent/parser/block.rb +56 -0
- data/lib/opulent/parser/comment.rb +27 -0
- data/lib/opulent/parser/control.rb +112 -0
- data/lib/opulent/parser/define.rb +30 -0
- data/lib/opulent/parser/eval.rb +21 -0
- data/lib/opulent/parser/expression.rb +344 -0
- data/lib/opulent/parser/filter.rb +25 -0
- data/lib/opulent/parser/node.rb +246 -0
- data/lib/opulent/parser/root.rb +48 -0
- data/lib/opulent/parser/text.rb +127 -0
- data/lib/opulent/settings.rb +79 -0
- data/lib/opulent/template.rb +67 -0
- data/lib/opulent/tokens.rb +166 -0
- data/lib/opulent/version.rb +4 -0
- data/opulent.gemspec +36 -0
- metadata +160 -7
@@ -0,0 +1,36 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Parser
|
4
|
+
module Parser
|
5
|
+
# @Define
|
6
|
+
module Theme
|
7
|
+
# Match a theme namespace node with its parameters and body
|
8
|
+
#
|
9
|
+
# theme node_name[ parameters ]
|
10
|
+
# body nodes
|
11
|
+
#
|
12
|
+
# @param parent [Node] Parent node to which we append the definition
|
13
|
+
#
|
14
|
+
def theme(parent)
|
15
|
+
if lookahead :theme_lookahead
|
16
|
+
# Get current line's indentation
|
17
|
+
indent = accept_unstripped(:indent) || ""
|
18
|
+
|
19
|
+
if accept :theme
|
20
|
+
# Get definition name
|
21
|
+
theme_name = accept :theme_identifier, :*
|
22
|
+
|
23
|
+
# Create a new node
|
24
|
+
node = @create.theme theme_name.to_sym, parent, indent.size
|
25
|
+
|
26
|
+
# Consume the newline from the end of the element
|
27
|
+
error :theme unless accept_unstripped(:line_feed).strip.empty?
|
28
|
+
accept_unstripped :newline
|
29
|
+
|
30
|
+
return node
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @PreProcessor
|
4
|
+
module PreProcessor
|
5
|
+
# @Singleton
|
6
|
+
class << self
|
7
|
+
# Preprocessor directives which will be checked before lexing starts.
|
8
|
+
# Each directive takes leading whitespace into account, therefore all the
|
9
|
+
# included lines will have the same indentation size as the directive
|
10
|
+
#
|
11
|
+
REQUIRE_DIRECTIVE = /([\t ]*)\/\/= *require +("(.*)"|'(.*)')/
|
12
|
+
|
13
|
+
# Include required files using the \\=directive_name pattern and keep
|
14
|
+
# the expanded directory path to make the require relative to the input
|
15
|
+
#
|
16
|
+
# @param file [String] name of the file to be processed
|
17
|
+
#
|
18
|
+
def process(input, mode = :file, &block)
|
19
|
+
# We can preprocess the code in file mode without requiring a block, but
|
20
|
+
# if we're preprocessing code directly, we need a block to get the
|
21
|
+
# caller file path, otherwise we return an error
|
22
|
+
if mode == :file
|
23
|
+
# Get the complete path of the input file
|
24
|
+
dir = File.dirname File.expand_path input
|
25
|
+
# Process the file contents
|
26
|
+
code = File.read input
|
27
|
+
elsif block
|
28
|
+
# Get the complete path of the input file
|
29
|
+
dir = File.dirname File.expand_path eval('__FILE__', block.binding)
|
30
|
+
# Process the file contents
|
31
|
+
code = input
|
32
|
+
elsif input =~ REQUIRE_DIRECTIVE
|
33
|
+
error :block, 'require', $~
|
34
|
+
else
|
35
|
+
return input
|
36
|
+
end
|
37
|
+
|
38
|
+
return require_files code, dir
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Replace the found \\=require directive with the file's contents
|
44
|
+
#
|
45
|
+
# @param contents [String] contents of the input file
|
46
|
+
# @param dir [String] complete path of the input file
|
47
|
+
#
|
48
|
+
def require_files(contents, dir)
|
49
|
+
contents.gsub REQUIRE_DIRECTIVE do |path|
|
50
|
+
require_contents = ''
|
51
|
+
match = $2[1..-2]
|
52
|
+
|
53
|
+
# Check if file already has extension
|
54
|
+
path = "#{dir}/#{match}"
|
55
|
+
|
56
|
+
# Check if the required file exists
|
57
|
+
if File.file? path
|
58
|
+
# Get contents
|
59
|
+
require_contents = process path
|
60
|
+
else
|
61
|
+
error :file, $2
|
62
|
+
end
|
63
|
+
|
64
|
+
# Indent lines
|
65
|
+
indent_lines $1, require_contents
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add indentation to each line if the require directive is indented
|
70
|
+
#
|
71
|
+
# @param indent [String] a string containing whitespace
|
72
|
+
# @param contents [String] required file's contents
|
73
|
+
#
|
74
|
+
def indent_lines(indent, contents)
|
75
|
+
unless indent.empty?
|
76
|
+
contents.lines.map!{ |line| indent + line }.join
|
77
|
+
end
|
78
|
+
|
79
|
+
return contents
|
80
|
+
end
|
81
|
+
|
82
|
+
# Give an explicit error report where an unexpected sequence of tokens
|
83
|
+
# appears and give indications on how to solve it
|
84
|
+
#
|
85
|
+
def error(context, *data)
|
86
|
+
message = case context
|
87
|
+
when :file
|
88
|
+
"The file #{data[0]} does not exist at the specified path."
|
89
|
+
when :block
|
90
|
+
"Found a #{data[0]} directive at \"#{data[1]}\" but no block was given as rendering environment.\n" +
|
91
|
+
"Please include a block when rendering to fix this issue.\n\n" +
|
92
|
+
"E.g. opulent.render(code, locals){}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Reconstruct lines to display where errors occur
|
96
|
+
fail "\n\nOpulent " + Logger.red('[Preprocessor Error]') + "\n---\n" +
|
97
|
+
"#{message}\n\n"
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Runtime
|
4
|
+
module Runtime
|
5
|
+
# @Singleton
|
6
|
+
class << self
|
7
|
+
# HTML Entities Object for escaping and unescaping strings
|
8
|
+
@@entities = HTMLEntities.new
|
9
|
+
|
10
|
+
# Definitions knowledgebase
|
11
|
+
@@themes = {}
|
12
|
+
|
13
|
+
# Remodel a structured abstract syntax tree using a given context
|
14
|
+
#
|
15
|
+
# @param syntax [Root] Root node with contents and definitions
|
16
|
+
# @param context [Context] Context holding environment variables
|
17
|
+
#
|
18
|
+
def remodel(syntax, context)
|
19
|
+
@@themes = syntax.themes
|
20
|
+
syntax.evaluate(context)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Apply given context to node attributes through evaluation
|
24
|
+
#
|
25
|
+
# @param attributes [Hash] Node attributes
|
26
|
+
# @param extension [String] Inline attributes extension code
|
27
|
+
# @param context [Context] Context holding environment variables
|
28
|
+
#
|
29
|
+
def attributes(attributes, extension, context)
|
30
|
+
atts = Hash[attributes.map{ |key, value|
|
31
|
+
case value
|
32
|
+
when Nodes::Expression
|
33
|
+
evaluated = value.evaluate(context)
|
34
|
+
when Array
|
35
|
+
evaluated = value.map do |v| v.evaluate(context) end
|
36
|
+
when Hash
|
37
|
+
evaluated = value.each do |k, v| value[k] = v.evaluate(context) end
|
38
|
+
end
|
39
|
+
|
40
|
+
[key, evaluated]
|
41
|
+
}]
|
42
|
+
|
43
|
+
# If we have an extension, we evaluate it and we merge it with the
|
44
|
+
# old attributes hash
|
45
|
+
if extension
|
46
|
+
atts_extension = context.evaluate extension.value
|
47
|
+
if atts_extension.is_a? Hash
|
48
|
+
atts.merge! atts_extension
|
49
|
+
else
|
50
|
+
error :extension, extension
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
return atts
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a node from the chosen theme and definition
|
58
|
+
#
|
59
|
+
def define(node, attributes, context)
|
60
|
+
# Get the definition model
|
61
|
+
model = @@themes[node.theme][node.name].dup
|
62
|
+
|
63
|
+
# Create a new definition context
|
64
|
+
definition_context = Context.new
|
65
|
+
definition_context.extend_nonlocals context.binding
|
66
|
+
definition_context.name = node.name
|
67
|
+
|
68
|
+
# Definition call arguments
|
69
|
+
arguments = {}
|
70
|
+
|
71
|
+
# Extract values which appear as definition parameters. If we have the
|
72
|
+
# key passed as argument, get its value. Otherwise, set the default
|
73
|
+
# parameter value
|
74
|
+
model.attributes.each do |key, value|
|
75
|
+
if attributes[key]
|
76
|
+
arguments[key] = attributes.delete key
|
77
|
+
else
|
78
|
+
arguments[key] = value.evaluate definition_context
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set the remaining attributes as a value in the arguments
|
83
|
+
arguments[:attributes] = attributes
|
84
|
+
|
85
|
+
# Set variable to determine available blocks
|
86
|
+
arguments[:blocks] = Hash[node.blocks.keys.map do |blk| [blk, true] end]
|
87
|
+
|
88
|
+
# Create local variables from argument variables
|
89
|
+
definition_context.extend_locals arguments
|
90
|
+
|
91
|
+
# Evaluate the model using the new context
|
92
|
+
model.evaluate definition_context, node.blocks
|
93
|
+
end
|
94
|
+
|
95
|
+
# Escape a given input value using htmlentities
|
96
|
+
#
|
97
|
+
# @param value [String] String to be escaped
|
98
|
+
#
|
99
|
+
def escape(value)
|
100
|
+
@@entities.encode value
|
101
|
+
end
|
102
|
+
|
103
|
+
# Quick access wrapper to defined runtime themes
|
104
|
+
#
|
105
|
+
def [](theme)
|
106
|
+
@@themes[theme]
|
107
|
+
end
|
108
|
+
|
109
|
+
# Give an explicit error report where an unexpected sequence of tokens
|
110
|
+
# appears and give indications on how to solve it
|
111
|
+
#
|
112
|
+
# @param context [Symbol] Context name in which the error happens
|
113
|
+
# @param data [Array] Additional error information
|
114
|
+
#
|
115
|
+
def error(context, *data)
|
116
|
+
message = case context
|
117
|
+
when :theme
|
118
|
+
"The theme \"#{data[0]}\" cannot be found in the theme knowledgebase."
|
119
|
+
when :enumerable
|
120
|
+
"The provided each structure iteration input \"#{data[0]}\" is not Enumerable."
|
121
|
+
when :binding
|
122
|
+
data[0] = data[0].to_s.match(/\`(.*)\'/)
|
123
|
+
data[0] = data[0][1] if data[0]
|
124
|
+
"Found an undefined local variable or method \"#{data[0]}\" at \"#{data[1]}\"."
|
125
|
+
when :variable_name
|
126
|
+
data[0] = data[0].to_s.match(/\`(.*)\'/)[1]
|
127
|
+
"Found an undefined local variable or method \"#{data[0]}\" in locals."
|
128
|
+
when :extension
|
129
|
+
"The extension sequence \"#{data[0]}\" is not a valid attributes extension. " +
|
130
|
+
"Please use a Hash to extend attributes."
|
131
|
+
when :filter_registered
|
132
|
+
"The \"#{data[0]}\" filter could not be recognized by Opulent."
|
133
|
+
when :filter_load
|
134
|
+
"The gem required for the \"#{data[0]}\" filter is not installed. You can install it by running:\n\n#{data[1]}"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Reconstruct lines to display where errors occur
|
138
|
+
fail "\n\nOpulent " + Logger.red("[Runtime Error]") + "\n---\n" +
|
139
|
+
"A runtime error has been encountered when building the compiled node tree.\n" +
|
140
|
+
"#{message}\n\n\n"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @OpulentTemplate
|
4
|
+
class OpulentTemplate < ::Tilt::Template
|
5
|
+
self.default_mime_type = 'text/html'
|
6
|
+
|
7
|
+
# Do whatever preparation is necessary to setup the underlying template
|
8
|
+
# engine. Called immediately after template data is loaded. Instance
|
9
|
+
# variables set in this method are available when #evaluate is called.
|
10
|
+
#
|
11
|
+
# Subclasses must provide an implementation of this method.
|
12
|
+
#
|
13
|
+
def prepare
|
14
|
+
@engine = ::Opulent::Opulent.new
|
15
|
+
@engine.update_options @options
|
16
|
+
end
|
17
|
+
|
18
|
+
# Execute the compiled template and return the result string. Template
|
19
|
+
# evaluation is guaranteed to be performed in the scope object with the
|
20
|
+
# locals specified and with support for yielding to the block.
|
21
|
+
#
|
22
|
+
# This method is only used by source generating templates. Subclasses that
|
23
|
+
# override render() may not support all features.
|
24
|
+
#
|
25
|
+
def evaluate(scope, locals, &block)
|
26
|
+
locals[:scope] = scope
|
27
|
+
@output = @engine.render_file @file, locals, &block
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# A string containing the (Ruby) source code for the template. The
|
32
|
+
# default Template#evaluate implementation requires either this
|
33
|
+
# method or the #precompiled method be overridden. When defined,
|
34
|
+
# the base Template guarantees correct file/line handling, locals
|
35
|
+
# support, custom scopes, proper encoding, and support for template
|
36
|
+
# compilation.
|
37
|
+
def precompiled_template(locals)
|
38
|
+
@output
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
::Tilt.register OpulentTemplate, 'op', 'opl', 'opulent'
|
43
|
+
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Tokens
|
4
|
+
module Tokens
|
5
|
+
# @Token
|
6
|
+
#
|
7
|
+
# Structure holding matching data for each Opulent token
|
8
|
+
#
|
9
|
+
# @param regex [RegEx] Regular expression for matching the token
|
10
|
+
# @param extra [FixNum] Extra characters around the first capture group
|
11
|
+
#
|
12
|
+
Token = Struct.new :regex, :extra
|
13
|
+
|
14
|
+
# @Singleton
|
15
|
+
class << self
|
16
|
+
# Token knowledgebase
|
17
|
+
@@tokens = {}
|
18
|
+
|
19
|
+
# Opulent Keywords
|
20
|
+
KEYWORDS = %w(def theme block yield if else elsif unless case when each while until)
|
21
|
+
|
22
|
+
def keyword(word)
|
23
|
+
KEYWORDS.include? word
|
24
|
+
end
|
25
|
+
|
26
|
+
# Shorthand Attributes
|
27
|
+
@@shorthand_lookahead = []
|
28
|
+
Opulent::Engine[:shorthand].each do |key, value|
|
29
|
+
@@tokens["shorthand@#{key}".to_sym] = Token.new /\A(#{value})/
|
30
|
+
@@shorthand_lookahead << "#{value}[a-zA-Z\"(]"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Node identifier
|
34
|
+
@@tokens[:identifier] = Token.new /\A([a-zA-Z]([\-\_]?[a-zA-Z0-9]+)*)/
|
35
|
+
@@tokens[:identifier_lookahead] = Token.new /\A([a-zA-Z]\w*\:\:)?(?<capture>([a-zA-Z]([\-\_]?[a-zA-Z0-9]+)*|#{@@shorthand_lookahead.join '|'}))/
|
36
|
+
|
37
|
+
# Attribute lookahead
|
38
|
+
@@tokens[:attribute_lookahead] = Token.new /\A([a-zA-Z]([\-\_]?[a-zA-Z0-9]+)*)/
|
39
|
+
|
40
|
+
# Self enclosing nodes
|
41
|
+
@@tokens[:self_enclosing] = Token.new /\A\/(.*)/, 1
|
42
|
+
|
43
|
+
# Node assignments
|
44
|
+
@@tokens[:assignment] = Token.new /\A(\:|\=)/
|
45
|
+
@@tokens[:assignment_unescaped] = Token.new /\A(\~)/
|
46
|
+
@@tokens[:assignment_terminator] = Token.new /\A((\,|\;)\s*)/
|
47
|
+
@@tokens[:assignment_lookahead] = Token.new /\A *(?<capture>[a-zA-Z]([\-\_]?[a-zA-Z0-9]+)* *[\:\=])/
|
48
|
+
|
49
|
+
# Node extension
|
50
|
+
@@tokens[:extend_attributes] = Token.new /\A(\+)/
|
51
|
+
|
52
|
+
# Node inline child
|
53
|
+
@@tokens[:inline_child] = Token.new /\A(\>)/
|
54
|
+
|
55
|
+
# Node output whitespace
|
56
|
+
@@tokens[:leading_whitespace] = Token.new /\A(\<\-)/
|
57
|
+
@@tokens[:leading_trailing_whitespace] = Token.new /\A(\>)/
|
58
|
+
@@tokens[:trailing_whitespace] = Token.new /\A(\-\>)/
|
59
|
+
|
60
|
+
# Comments
|
61
|
+
@@tokens[:comment] = Token.new /\A\/(.*)/, 1
|
62
|
+
@@tokens[:comment_lookahead] = Token.new /\A *(?<capture>\/)/
|
63
|
+
|
64
|
+
# Intepreted filters
|
65
|
+
@@tokens[:filter] = Token.new /\A\:([a-zA-Z]([\-\_]?[a-zA-Z0-9]+)*)/, 1
|
66
|
+
@@tokens[:filter_lookahead] = Token.new /\A *\:(?<capture>[a-zA-Z]([\-\_]?[a-zA-Z0-9]+)*)/
|
67
|
+
|
68
|
+
# Text
|
69
|
+
@@tokens[:escaped_text] = Token.new /\A(.*)/
|
70
|
+
@@tokens[:unescaped_text] = Token.new /\A\~(.*)/, 1
|
71
|
+
|
72
|
+
# Print
|
73
|
+
@@tokens[:escaped_print] = Token.new /\A\=(.*)/, 1
|
74
|
+
@@tokens[:unescaped_print] = Token.new /\A\=\~(.*)/, 2
|
75
|
+
@@tokens[:print_lookahead] = Token.new /\A *(?<capture>\=)/
|
76
|
+
|
77
|
+
# Multiline Text
|
78
|
+
@@tokens[:multiline] = Token.new /\A(\|)/
|
79
|
+
|
80
|
+
# HTML Text
|
81
|
+
@@tokens[:html_text] = Token.new /\A(\<.+\>.*)/
|
82
|
+
|
83
|
+
# Definitions
|
84
|
+
@@tokens[:def] = Token.new /\A(def +)/
|
85
|
+
@@tokens[:def_lookahead] = Token.new /\A *(?<capture>def )/
|
86
|
+
|
87
|
+
# Yield
|
88
|
+
@@tokens[:yield] = Token.new /\A(yield)/
|
89
|
+
@@tokens[:yield_lookahead] = Token.new /\A *(?<capture>yield)/
|
90
|
+
@@tokens[:yield_identifier] = Token.new /\A( +[a-zA-Z]([\_]?[a-zA-Z0-9]+)*)/
|
91
|
+
|
92
|
+
# Yield
|
93
|
+
@@tokens[:block] = Token.new /\A(block)/
|
94
|
+
@@tokens[:block_lookahead] = Token.new /\A *(?<capture>block)/
|
95
|
+
|
96
|
+
# Theme
|
97
|
+
@@tokens[:theme] = Token.new /\A(theme +)/
|
98
|
+
@@tokens[:theme_lookahead] = Token.new /\A *(?<capture>theme )/
|
99
|
+
@@tokens[:theme_identifier] = Token.new /\A([a-zA-Z]\w*)/
|
100
|
+
@@tokens[:theme_node] = Token.new /\A([a-zA-Z]\w*)\:\:/, 2
|
101
|
+
|
102
|
+
# Conditional Structures
|
103
|
+
@@tokens[:control] = Token.new /\A(if|elsif|else|unless|case|when|each|while|until)/
|
104
|
+
@@tokens[:control_lookahead] = Token.new /\A *(?<capture>if|elsif|else|unless|case|when|each|while|until)/
|
105
|
+
@@tokens[:each_pattern] = Token.new /\A(\w+( *, *\w+)? +)?in +.+/
|
106
|
+
|
107
|
+
# Brackets
|
108
|
+
@@tokens[:round_bracket_open] = Token.new /\A(\()/
|
109
|
+
@@tokens[:round_bracket_close] = Token.new /\A(\))/
|
110
|
+
@@tokens[:square_bracket_open] = Token.new /\A(\[)/
|
111
|
+
@@tokens[:square_bracket_close] = Token.new /\A(\])/
|
112
|
+
@@tokens[:curly_bracket_open] = Token.new /\A(\{)/
|
113
|
+
@@tokens[:curly_bracket_close] = Token.new /\A(\})/
|
114
|
+
@@tokens[:angular_bracket_open] = Token.new /\A(\<)/
|
115
|
+
@@tokens[:angular_bracket_close] = Token.new /\A(\>)/
|
116
|
+
|
117
|
+
# Receive matching brackets for allowing multiple bracket types for
|
118
|
+
# element attributes
|
119
|
+
@@tokens[:brackets] = Token.new /\A([\(\[\{])/
|
120
|
+
@@tokens[:'('] = @@tokens[:round_bracket_close]
|
121
|
+
@@tokens[:'['] = @@tokens[:square_bracket_close]
|
122
|
+
@@tokens[:'{'] = @@tokens[:curly_bracket_close]
|
123
|
+
@@tokens[:'<'] = @@tokens[:angular_bracket_close]
|
124
|
+
|
125
|
+
# Terminators
|
126
|
+
@@tokens[:comma] = Token.new /\A(\s*\,\s*)/
|
127
|
+
@@tokens[:colon] = Token.new /\A(\s*\:\s*)/
|
128
|
+
@@tokens[:semicolon] = Token.new /\A(\s*\;\s*)/
|
129
|
+
|
130
|
+
# Array
|
131
|
+
@@tokens[:array_open] = @@tokens[:square_bracket_open]
|
132
|
+
@@tokens[:array_terminator] = @@tokens[:comma]
|
133
|
+
@@tokens[:array_close] = @@tokens[:square_bracket_close]
|
134
|
+
|
135
|
+
# Hash
|
136
|
+
@@tokens[:hash_open] = @@tokens[:curly_bracket_open]
|
137
|
+
@@tokens[:hash_terminator] = Token.new /\A(\s*(\,)\s*)/
|
138
|
+
@@tokens[:hash_assignment] = Token.new /\A(\s*(\=\>)\s*)/
|
139
|
+
@@tokens[:hash_symbol] = Token.new /\A([a-zA-Z\_][a-zA-Z0-9\_]*\:(?!\:))/
|
140
|
+
@@tokens[:hash_close] = @@tokens[:curly_bracket_close]
|
141
|
+
|
142
|
+
# Expressions
|
143
|
+
@@tokens[:exp_context] = Token.new /\A(\$(.|\-.)?|\@|\@\@)/
|
144
|
+
@@tokens[:exp_method_call] = Token.new /\A(\.[a-zA-Z\_][a-zA-Z0-9\_]*[\!\?]?)/
|
145
|
+
@@tokens[:exp_module] = Token.new /\A(\:\:)/
|
146
|
+
@@tokens[:exp_identifier] = Token.new /\A([a-zA-Z\_][a-zA-Z0-9\_]*[\!\?]?)/
|
147
|
+
@@tokens[:exp_assignment] = Token.new /\A(\=)/
|
148
|
+
@@tokens[:exp_operation] = Token.new /\A( *(\+|\-|\*\*|\*|\/|\<\<|\>\>|\.\.|\%|\<\=\>|\<\=|\^|\<|\>\=|\>|\=\~|\!\~|\=\=\=|\=\=|\=~|\!|not|\&\&|\&|and|\|\||\||or) *)/
|
149
|
+
@@tokens[:exp_regex] = Token.new /\A(\/((?:[^\/\\]|\\.)*?)\/)/
|
150
|
+
@@tokens[:exp_string] = Token.new /\A(("((?:[^"\\]|\\.)*?)")|('(?:[^'\\]|\\.)*?'))/
|
151
|
+
@@tokens[:exp_percent] = Token.new /\A(\%[wWqQrxsiI]?.)/
|
152
|
+
@@tokens[:exp_double] = Token.new /\A([0-9]+\.[0-9]+([eE][-+]?[0-9]+)?)/
|
153
|
+
@@tokens[:exp_fixnum] = Token.new /\A([0-9]+)/
|
154
|
+
@@tokens[:exp_nil] = Token.new /\A(nil)/
|
155
|
+
@@tokens[:exp_boolean] = Token.new /\A(true|false)/
|
156
|
+
@@tokens[:exp_ternary] = Token.new /\A( *\? *)/
|
157
|
+
@@tokens[:exp_ternary_else] = Token.new /\A( *\: *)/
|
158
|
+
|
159
|
+
# Expression identifier lookahead
|
160
|
+
@@tokens[:exp_identifier_lookahead] = Token.new /\A(?<capture>[a-zA-Z\_][a-zA-Z0-9\_]*[\!\?]?)/
|
161
|
+
|
162
|
+
# Evaluation
|
163
|
+
@@tokens[:eval] = Token.new /\A\-(.*)/, 1
|
164
|
+
@@tokens[:eval_multiline] = Token.new /\A\+(.*)/, 1
|
165
|
+
|
166
|
+
# Whitespace
|
167
|
+
@@tokens[:newline] = Token.new /\A(\n+)/
|
168
|
+
@@tokens[:whitespace] = Token.new /\A( +)/
|
169
|
+
@@tokens[:whitespace_lookahead] = Token.new /\A(?<capture> +)/
|
170
|
+
|
171
|
+
# Indentation
|
172
|
+
@@tokens[:indent] = Token.new /\A( *)/
|
173
|
+
@@tokens[:indent_lookahead] = Token.new /\A\n?(?<capture> *)/
|
174
|
+
|
175
|
+
# Feed
|
176
|
+
@@tokens[:line_feed] = Token.new /\A(.*)/
|
177
|
+
|
178
|
+
# Return the matching closing bracket
|
179
|
+
#
|
180
|
+
# @param bracket [String] Opening bracket for the capture group
|
181
|
+
#
|
182
|
+
def bracket(bracket)
|
183
|
+
case bracket
|
184
|
+
when '(' then return ')'
|
185
|
+
when '[' then return ']'
|
186
|
+
when '{' then return '}'
|
187
|
+
when '<' then return '>'
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Return the requested token to the parser
|
192
|
+
#
|
193
|
+
# @param name [Symbol] Token requested by the parser accept method
|
194
|
+
#
|
195
|
+
def [](name)
|
196
|
+
@@tokens[name]
|
197
|
+
end
|
198
|
+
|
199
|
+
# Set a new token at runtime
|
200
|
+
#
|
201
|
+
# @param name [Symboidentifierl] Identifier for the token
|
202
|
+
# @param token [Token] Token data to be set
|
203
|
+
#
|
204
|
+
def []=(name, token)
|
205
|
+
@@tokens[name] = token
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# # Control structures
|
210
|
+
# T_IF = /\A(if)/
|
211
|
+
# T_ELSIF = /\A(elsif)/
|
212
|
+
# T_ELSE = /\A(else)/
|
213
|
+
# T_EACH = /\A(each)/
|
214
|
+
# T_IN = /\A(in)/
|
215
|
+
#
|
216
|
+
# # Punctuation
|
217
|
+
# T_COLON = /\A(\:)/
|
218
|
+
# T_SEMICOLON = /\A(\;)/
|
219
|
+
#
|
220
|
+
# # Assignments terminator
|
221
|
+
# T_TERMINATOR = /\A(\,|\;)/
|
222
|
+
#
|
223
|
+
# # Assignments identifier
|
224
|
+
# T_ASSIGNMENT = /\A(\:|=)/
|
225
|
+
#
|
226
|
+
# # Paranthesis
|
227
|
+
# T_SQUARE_BRACKET_OPEN = /\A(\[)/
|
228
|
+
# T_SQUARE_BRACKET_CLOSE = /\A(\])/
|
229
|
+
# T_CURLY_BRACKET_OPEN = /\A(\{)/
|
230
|
+
# T_CURLY_BRACKET_CLOSE = /\A(\})/
|
231
|
+
# T_PARANTHESIS_OPEN = /\A(\()/
|
232
|
+
# T_PARANTHESIS_CLOSE = /\A(\))/
|
233
|
+
#
|
234
|
+
# # Embedded Ruby
|
235
|
+
# T_RUBY_CONTEXT = /\A(\$[0-9]*?|\@|\@\@)/
|
236
|
+
# T_RUBY_CONSTANT = /\A([a-zA-Z\_][a-zA-Z0-9\_]*)/
|
237
|
+
# T_RUBY_INDEX = /\A(\[\s*\:?(([0-9]+)|([a-zA-Z\_][a-zA-Z0-9\-\_]*)|("((?:[^"\\]|\\.)*?)")|('(?:[^'\\]|\\.)*?')|(.+\.\..+))\s*\])/
|
238
|
+
# T_RUBY_MODULES = /\A(\:\:[a-zA-Z\_][a-zA-Z0-9\_]*)/
|
239
|
+
# T_RUBY_METHOD = /\A(\.[a-zA-Z\_][a-zA-Z0-9\_]*[\!\?]?)/
|
240
|
+
#
|
241
|
+
# # Literals
|
242
|
+
# T_STRING = /\A(("((?:[^"\\]|\\.)*?)")|('(?:[^'\\]|\\.)*?'))/
|
243
|
+
# T_DOUBLE = /\A([0-9]+\.[0-9]+([eE][-+]?[0-9]+)?)/
|
244
|
+
# T_INTEGER = /\A([0-9]+)/
|
245
|
+
# T_UNDEFINED = /\A(nil)/
|
246
|
+
# T_BOOLEAN_TRUE = /\A(true)/
|
247
|
+
# T_BOOLEAN_FALSE = /\A(false)/
|
248
|
+
#
|
249
|
+
# # Operation Signs
|
250
|
+
# T_MULTIPLICATIVE = /\A(\*\*|\*|\/)/
|
251
|
+
# T_ADDITIVE = /\A(\<\<|\+|\-)/
|
252
|
+
# T_EQUALITY = /\A(\<\=|\<|\>\=|\>|\=\=)/
|
253
|
+
# T_BOOLEAN_NOT = /\A(\!|not)/
|
254
|
+
# T_BOOLEAN_AND = /\A(\&\&|and)/
|
255
|
+
# T_BOOLEAN_OR = /\A(\|\||or)/
|
256
|
+
#
|
257
|
+
# # Operation Signs Lookahead
|
258
|
+
# T_SIGN_LOOKAHEAD = /\A\*\*|\*|\/|\+|\-|\<\<|\<\=|\<|\>\=|\>|\=\=|\=|\!|not|\&\&|and|\|\||or/
|
259
|
+
#
|
260
|
+
# # Whitespace
|
261
|
+
# T_WHITESPACE = /\A( +)/
|
262
|
+
# T_NEWLINE = /\A(\n*)/
|
263
|
+
# T_INDENT = /\A( *)/
|
264
|
+
#
|
265
|
+
# # Whitespace Lookahead
|
266
|
+
# T_INDENT_LOOKAHEAD = /\A\n?( *)/
|
267
|
+
#
|
268
|
+
# # Line and char feeds
|
269
|
+
# T_LINE_FEED = /\A(.*)/
|
270
|
+
# T_CHAR_FEED = /\A([\s\S])/
|
271
|
+
# T_UNESCAPED_TEXT = /\A\!(.*)/
|
272
|
+
# T_UNESCAPED_MULTILINE_TEXT = /\A(\!\=)/
|
273
|
+
# T_ESCAPED_MULTILINE_TEXT = /\A(\=)/
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|