opulent 0.0.9 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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