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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.libold/opulent.rb +96 -0
  4. data/.libold/opulent/context.rb +80 -0
  5. data/.libold/opulent/engine.rb +88 -0
  6. data/.libold/opulent/filter.rb +101 -0
  7. data/.libold/opulent/logger.rb +67 -0
  8. data/.libold/opulent/nodes.rb +13 -0
  9. data/.libold/opulent/nodes/block.rb +29 -0
  10. data/.libold/opulent/nodes/comment.rb +35 -0
  11. data/.libold/opulent/nodes/control.rb +230 -0
  12. data/.libold/opulent/nodes/define.rb +42 -0
  13. data/.libold/opulent/nodes/eval.rb +41 -0
  14. data/.libold/opulent/nodes/expression.rb +28 -0
  15. data/.libold/opulent/nodes/filter.rb +70 -0
  16. data/.libold/opulent/nodes/helper.rb +69 -0
  17. data/.libold/opulent/nodes/node.rb +101 -0
  18. data/.libold/opulent/nodes/root.rb +62 -0
  19. data/.libold/opulent/nodes/text.rb +54 -0
  20. data/.libold/opulent/nodes/theme.rb +36 -0
  21. data/.libold/opulent/parser.rb +252 -0
  22. data/.libold/opulent/parser/block.rb +70 -0
  23. data/.libold/opulent/parser/comment.rb +32 -0
  24. data/.libold/opulent/parser/control.rb +83 -0
  25. data/.libold/opulent/parser/define.rb +39 -0
  26. data/.libold/opulent/parser/eval.rb +33 -0
  27. data/.libold/opulent/parser/expression.rb +350 -0
  28. data/.libold/opulent/parser/filter.rb +41 -0
  29. data/.libold/opulent/parser/node.rb +232 -0
  30. data/.libold/opulent/parser/root.rb +96 -0
  31. data/.libold/opulent/parser/text.rb +114 -0
  32. data/.libold/opulent/parser/theme.rb +36 -0
  33. data/.libold/opulent/preprocessor.rb +102 -0
  34. data/.libold/opulent/runtime.rb +144 -0
  35. data/.libold/opulent/template.rb +43 -0
  36. data/.libold/opulent/tokens.rb +276 -0
  37. data/.libold/opulent/version.rb +5 -0
  38. data/.rspec +2 -0
  39. data/.travis.yml +4 -0
  40. data/Gemfile +2 -0
  41. data/LICENSE +21 -0
  42. data/README.md +102 -0
  43. data/Rakefile +6 -0
  44. data/benchmark/benchmark.rb +46 -0
  45. data/benchmark/cases/node/node.haml +17 -0
  46. data/benchmark/cases/node/node.op +14 -0
  47. data/benchmark/cases/node/node.slim +19 -0
  48. data/bin/console +14 -0
  49. data/bin/setup +7 -0
  50. data/docs/syntax.md +3 -0
  51. data/docs/usage.md +3 -0
  52. data/lib/opulent.rb +11 -64
  53. data/lib/opulent/compiler.rb +132 -0
  54. data/lib/opulent/compiler/block.rb +31 -0
  55. data/lib/opulent/compiler/comment.rb +22 -0
  56. data/lib/opulent/compiler/control.rb +152 -0
  57. data/lib/opulent/compiler/define.rb +88 -0
  58. data/lib/opulent/compiler/eval.rb +15 -0
  59. data/lib/opulent/compiler/filter.rb +54 -0
  60. data/lib/opulent/compiler/node.rb +232 -0
  61. data/lib/opulent/compiler/root.rb +19 -0
  62. data/lib/opulent/compiler/text.rb +60 -0
  63. data/lib/opulent/context.rb +88 -0
  64. data/lib/opulent/engine.rb +60 -0
  65. data/lib/opulent/filters.rb +222 -0
  66. data/lib/opulent/logger.rb +47 -0
  67. data/lib/opulent/parser.rb +196 -0
  68. data/lib/opulent/parser/block.rb +56 -0
  69. data/lib/opulent/parser/comment.rb +27 -0
  70. data/lib/opulent/parser/control.rb +112 -0
  71. data/lib/opulent/parser/define.rb +30 -0
  72. data/lib/opulent/parser/eval.rb +21 -0
  73. data/lib/opulent/parser/expression.rb +344 -0
  74. data/lib/opulent/parser/filter.rb +25 -0
  75. data/lib/opulent/parser/node.rb +246 -0
  76. data/lib/opulent/parser/root.rb +48 -0
  77. data/lib/opulent/parser/text.rb +127 -0
  78. data/lib/opulent/settings.rb +79 -0
  79. data/lib/opulent/template.rb +67 -0
  80. data/lib/opulent/tokens.rb +166 -0
  81. data/lib/opulent/version.rb +4 -0
  82. data/opulent.gemspec +36 -0
  83. 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