farleyknight-ionize 0.1.0

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 (98) hide show
  1. data/README.rdoc +59 -0
  2. data/Rakefile +5 -0
  3. data/bin/ionize +47 -0
  4. data/lib/ionize.rb +75 -0
  5. data/lib/ionize/environment.rb +56 -0
  6. data/lib/ionize/environment/application.rb +58 -0
  7. data/lib/ionize/environment/php_array.rb +95 -0
  8. data/lib/ionize/parser.rb +272 -0
  9. data/lib/ionize/tokenizer.rb +544 -0
  10. data/lib/ionize/translate.rb +34 -0
  11. data/lib/ionize/translate/composite_string_statements.rb +79 -0
  12. data/lib/ionize/translate/debug.rb +16 -0
  13. data/lib/ionize/translate/ext.rb +47 -0
  14. data/lib/ionize/translate/function_args.rb +132 -0
  15. data/lib/ionize/translate/if_statements.rb +42 -0
  16. data/lib/ionize/translate/multiple_statements.rb +22 -0
  17. data/lib/ionize/translate/php_to_ruby.rb +40 -0
  18. data/lib/ionize/translate/rails_for_php.rb +191 -0
  19. data/lib/ionize/translate/rewritable.rb +133 -0
  20. data/lib/ionize/translate/rewrites.rb +51 -0
  21. data/lib/ionize/translate/statements.rb +622 -0
  22. data/lib/ionize/translate/switch_case_statements.rb +52 -0
  23. data/lib/ionize/translate/term_statements.rb +76 -0
  24. data/lib/ionize/translate/translator.rb +52 -0
  25. data/lib/ionize/version.rb +9 -0
  26. data/spec/fixtures/array_lookup.php +10 -0
  27. data/spec/fixtures/boolean_operators.php +5 -0
  28. data/spec/fixtures/boolean_operators.rb +6 -0
  29. data/spec/fixtures/class_def.php +34 -0
  30. data/spec/fixtures/class_def.rb +34 -0
  31. data/spec/fixtures/dangling_else.php +8 -0
  32. data/spec/fixtures/dangling_else.rb +12 -0
  33. data/spec/fixtures/drupal_1.php +663 -0
  34. data/spec/fixtures/drupal_2.php +1152 -0
  35. data/spec/fixtures/empty_string.php +12 -0
  36. data/spec/fixtures/for_loop.php +17 -0
  37. data/spec/fixtures/for_loop2.php +13 -0
  38. data/spec/fixtures/for_loop3.php +16 -0
  39. data/spec/fixtures/for_loop3.rb +17 -0
  40. data/spec/fixtures/for_loop4.php +5 -0
  41. data/spec/fixtures/for_loop4.rb +6 -0
  42. data/spec/fixtures/foreach.php +9 -0
  43. data/spec/fixtures/foreach2.php +8 -0
  44. data/spec/fixtures/foreach3.php +7 -0
  45. data/spec/fixtures/foreach3.rb +7 -0
  46. data/spec/fixtures/fun_def.php +9 -0
  47. data/spec/fixtures/fun_def2.php +30 -0
  48. data/spec/fixtures/fun_def2.rb +30 -0
  49. data/spec/fixtures/fun_def3.php +33 -0
  50. data/spec/fixtures/fun_def4.php +43 -0
  51. data/spec/fixtures/fun_def4.rb +37 -0
  52. data/spec/fixtures/fun_def5.php +36 -0
  53. data/spec/fixtures/fun_with_if.php +6 -0
  54. data/spec/fixtures/fun_with_if.rb +6 -0
  55. data/spec/fixtures/fun_with_ifs.php +12 -0
  56. data/spec/fixtures/fun_with_ifs.rb +14 -0
  57. data/spec/fixtures/hello_world.php +6 -0
  58. data/spec/fixtures/heredoc.php +6 -0
  59. data/spec/fixtures/heredoc.rb +5 -0
  60. data/spec/fixtures/if.php +6 -0
  61. data/spec/fixtures/if.rb +7 -0
  62. data/spec/fixtures/if_boolean.php +5 -0
  63. data/spec/fixtures/if_boolean.rb +5 -0
  64. data/spec/fixtures/if_else.php +11 -0
  65. data/spec/fixtures/if_else1.php +17 -0
  66. data/spec/fixtures/if_else2.php +8 -0
  67. data/spec/fixtures/if_else3.php +15 -0
  68. data/spec/fixtures/if_else_nested.php +14 -0
  69. data/spec/fixtures/if_else_nested.rb +15 -0
  70. data/spec/fixtures/if_else_series.php +12 -0
  71. data/spec/fixtures/if_else_series.rb +12 -0
  72. data/spec/fixtures/if_not.php +5 -0
  73. data/spec/fixtures/if_not.rb +5 -0
  74. data/spec/fixtures/if_with_brackets.php +7 -0
  75. data/spec/fixtures/if_with_brackets.rb +7 -0
  76. data/spec/fixtures/long_if_else.php +10 -0
  77. data/spec/fixtures/long_if_else.rb +9 -0
  78. data/spec/fixtures/oo.php +16 -0
  79. data/spec/fixtures/php_nuke/sql_layer.php +527 -0
  80. data/spec/fixtures/postop.php +3 -0
  81. data/spec/fixtures/preop.php +7 -0
  82. data/spec/fixtures/simple_fun_def.php +4 -0
  83. data/spec/fixtures/switch_case.php +13 -0
  84. data/spec/fixtures/switch_case.rb +14 -0
  85. data/spec/fixtures/switch_case2.php +25 -0
  86. data/spec/fixtures/switch_case3.php +40 -0
  87. data/spec/fixtures/switch_case3.rb +42 -0
  88. data/spec/fixtures/switch_case4.php +56 -0
  89. data/spec/fixtures/switch_case5.php +71 -0
  90. data/spec/fixtures/switch_case_with_rescue_nil.php +43 -0
  91. data/spec/fixtures/switch_case_with_rescue_nil.rb +35 -0
  92. data/spec/fixtures/tertiary.php +3 -0
  93. data/spec/helper.rb +17 -0
  94. data/spec/php_environment_spec.rb +83 -0
  95. data/spec/php_parser_spec.rb +121 -0
  96. data/spec/php_translator_spec.rb +358 -0
  97. data/spec/rails_for_php_spec.rb +303 -0
  98. metadata +191 -0
@@ -0,0 +1,59 @@
1
+ = Ionize
2
+
3
+ == DESCRIPTION:
4
+
5
+ Tranforms Php to Ruby.
6
+
7
+ Ionize parses Php, turns the parse result into an s-expression, then translates
8
+ the s-expression into one that can be consumed by Ruby2Ruby. When everything is
9
+ said and done, it will be able to translate an entire Php installation into Ruby.
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ Check out http://ionize.farleyknight.com for some examples of what
14
+ works and what doesn't.
15
+
16
+ == SYNOPSIS:
17
+
18
+ TODO: Add an example of using a command line tool to convert
19
+ an existing Php installation into a Ruby one
20
+
21
+ == REQUIREMENTS:
22
+
23
+ Dhaka, ParseTree, Ruby2Ruby
24
+
25
+ The Rspec gem is required to run the specs..
26
+
27
+ , .. anything else?
28
+
29
+ == INSTALL:
30
+
31
+ When I register this project with Rubygems, the command will be:
32
+ sudo gem install ionize
33
+
34
+ But, at the moment, we're still in development..
35
+
36
+ == LICENSE:
37
+
38
+ (The MIT License)
39
+
40
+ Copyright (c) 2008 Farley Knight
41
+
42
+ Permission is hereby granted, free of charge, to any person obtaining
43
+ a copy of this software and associated documentation files (the
44
+ 'Software'), to deal in the Software without restriction, including
45
+ without limitation the rights to use, copy, modify, merge, publish,
46
+ distribute, sublicense, and/or sell copies of the Software, and to
47
+ permit persons to whom the Software is furnished to do so, subject to
48
+ the following conditions:
49
+
50
+ The above copyright notice and this permission notice shall be
51
+ included in all copies or substantial portions of the Software.
52
+
53
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
54
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
55
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
56
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
57
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
58
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
59
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ # require 'config/requirements'
2
+ # require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
5
+
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This is stupid..
4
+ require '/home/robinhoode/dev/ruby/ionize/lib/ionize'
5
+
6
+ module Ionize
7
+ module Php
8
+ class Convert
9
+ def run
10
+ if ARGV.first =~ /\.php$/
11
+ # convert_file(ARGV.first)
12
+ elsif File.stat(ARGV.first).directory?
13
+ puts "#{ARGV.first} was a directory. Converting.."
14
+ convert_directory(ARGV.first)
15
+ else
16
+ # Should actually print the usage here..
17
+ raise "First argument was neither php file nor directory: #{ARGV.first.inspect}"
18
+ end
19
+ end
20
+
21
+ def convert_file(php_file, ruby_file = nil)
22
+ ruby_file ||= php_file.gsub(".php", ".rb")
23
+
24
+ ruby_dir = File.dirname(ruby_file)
25
+ FileUtils.mkdir_p(ruby_dir) unless File.exists?(ruby_dir)
26
+
27
+ File.open(ruby_file, "w+") do |f|
28
+ f.write(Ionize::Php.to_ruby(File.read(php_file)))
29
+ end
30
+ puts "Converted file #{php_file} and stored it as #{ruby_file}"
31
+ end
32
+
33
+ def convert_directory(php_directory, ruby_directory = nil)
34
+ ruby_directory ||= "#{php_directory}-ruby"
35
+ FileUtils.mkdir ruby_directory unless File.exists?(ruby_directory) and File.stat(ruby_directory).directory?
36
+ Dir["#{php_directory}/**/*"].select {|f| f =~ /\.php$/ }.each do |php_file|
37
+ ruby_file = php_file.gsub(php_directory, ruby_directory).gsub(".php", ".rb")
38
+ puts "Found file #{php_file}"
39
+ convert_file(php_file, ruby_file)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+
47
+ Ionize::Php::Convert.new.run
@@ -0,0 +1,75 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'dhaka'
6
+ require 'parse_tree'
7
+ require 'ruby2ruby'
8
+
9
+ require 'ionize/translate'
10
+ require 'ionize/environment'
11
+
12
+ require 'ionize/parser'
13
+ require 'ionize/compiled_parser'
14
+
15
+ module Ionize
16
+ module Php
17
+ CharacterLimit = 50000
18
+
19
+ # Returns a Dhaka AST of a string
20
+ def self.to_ast(string)
21
+ # TODO: Error handling could be better..
22
+ # Maybe another time
23
+
24
+ if string.length > CharacterLimit
25
+ raise "Your input is #{string.length}. Ionize::Php::Parser currently cannot handle inputs larger than #{CharacterLimit}, due to Dhaka's memory usage"
26
+ end
27
+
28
+ tokens = Php.to_tokens(string)
29
+ result = Php::CompiledParser.parse(tokens)
30
+
31
+ if result.is_a? Dhaka::ParseErrorResult
32
+ error = %Q{
33
+ Could not parse token.
34
+ Token => #{result.unexpected_token.inspect}
35
+
36
+ }
37
+ error << string.slice(result.unexpected_token.input_position, 40) rescue nil
38
+ raise error
39
+ elsif result.is_a? Dhaka::TokenizerErrorResult
40
+ raise result
41
+ end
42
+
43
+ result
44
+ end
45
+
46
+ # Tokens from Dhaka's tokenizer
47
+ def self.to_tokens(string)
48
+ require 'ionize/tokenizer'
49
+ tokenizer = Php::Tokenizer.new(string)
50
+ tokenizer.switch_to :php unless string =~ /<\?php/i
51
+ tokens = tokenizer.run
52
+
53
+ if tokens.is_a? Dhaka::TokenizerErrorResult
54
+ raise "Could not tokenize at index #{tokens.unexpected_char_index}\n" + string.slice(tokens.unexpected_char_index, 40).inspect
55
+ end
56
+
57
+ tokens
58
+ end
59
+
60
+ # An S-expression of the string
61
+ def self.to_sexp(string)
62
+ Php::Translate.translate(Php.to_ast(string))
63
+ end
64
+
65
+ # A stripped down version of what Dhaka hands us
66
+ def self.to_raw_sexp(string)
67
+ Php::Translate.flatten(Php.to_ast(string))
68
+ end
69
+
70
+ # Abracadbra!
71
+ def self.to_ruby(string)
72
+ Php::Translate::Php2Ruby.new.process(Php.to_sexp(string))
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,56 @@
1
+
2
+ load File.dirname(__FILE__) + '/environment/php_array.rb'
3
+ load File.dirname(__FILE__) + '/environment/application.rb'
4
+
5
+ module Ionize
6
+ module Php
7
+ Config = {
8
+ "magic_quotes_gpc" => true,
9
+ "magic_quotes_runtime" => true
10
+ }
11
+ module Environment
12
+ def self.included(target)
13
+ Kernel.module_eval do
14
+ def array(*args)
15
+ PhpArray.from_mixed(*args)
16
+ end
17
+
18
+ def get_magic_quotes_gpc
19
+ Php::Config["magic_quotes_gpc"] ? 1 : 0
20
+ end
21
+
22
+ def get_magic_quotes_runtime
23
+ Php::Config["magic_quotes_runtime"] ? 1 : 0
24
+ end
25
+
26
+ def gethostbyaddr(string)
27
+ address = string.split(".").map {|n| n.to_i }.pack("CCCC")
28
+ Socket.gethostbyaddr(address).first
29
+ end
30
+ end
31
+
32
+ String.class_eval do
33
+ def preg_match(regex)
34
+ matches = match(regex)
35
+ return [!matches.nil?, matches.to_a]
36
+ end
37
+
38
+ def preg_match_all(regex)
39
+ copy = self.dup
40
+ results = []
41
+
42
+ match = copy.match(regex)
43
+ while match
44
+ results << match.to_a
45
+ copy.gsub!(match.to_a.first, "")
46
+ match = copy.match(regex)
47
+ end
48
+
49
+ [results.length - 1, results.transpose]
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,58 @@
1
+
2
+ require 'rack'
3
+
4
+ module Ionize
5
+ module Php
6
+ module Environment
7
+ class ScriptExit < Exception; end
8
+
9
+ class Application
10
+ def self.run(code)
11
+ Rack::Handler::Mongrel.run(Application.new(code), :Port => 3000)
12
+ end
13
+
14
+ def initialize(code)
15
+ @output, @header, @code = "", {}, 200
16
+ @php_script = Php.to_ruby(code)
17
+ end
18
+
19
+ def GET
20
+ @get ||= @request.GET
21
+ end
22
+
23
+ def POST
24
+ @post ||= @request.POST
25
+ end
26
+
27
+ def SERVER
28
+ @server ||= @requeset.SERVER
29
+ end
30
+
31
+ def exit
32
+ raise ScriptExit
33
+ end
34
+
35
+ def print(text)
36
+ @output << text
37
+ end
38
+
39
+ def header(string)
40
+ if m = string.match(/Location: (.*)/i)
41
+ @header["Location"] = m[1]
42
+ @code = 302
43
+ end
44
+ end
45
+
46
+ def call(env)
47
+ @request = Rack::Request.new(env)
48
+ begin
49
+ instance_eval(@php_script)
50
+ rescue ScriptExit
51
+ # Catch an exit() or die() and show that to the user
52
+ end
53
+ [@code, @header, @output]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,95 @@
1
+
2
+ module Ionize
3
+ module Php
4
+ module Environment
5
+ class PhpArray < Hash
6
+ def initialize(*args, &block)
7
+ @index = 0
8
+ super(*args, &block)
9
+ end
10
+
11
+ def join(*args)
12
+ values.join(*args)
13
+ end
14
+
15
+ def hash?
16
+ keys.any? {|k| k.class != Fixnum }
17
+ end
18
+
19
+ def array?
20
+ keys.all? {|k| k.class == Fixnum }
21
+ end
22
+
23
+ def combine(second)
24
+ array = self.class.new
25
+ second.values.each_with_index do |val, key|
26
+ array.put(self[key], val)
27
+ end
28
+ array
29
+ end
30
+
31
+ def intersect(second)
32
+ array = self.class.new
33
+ second.values.each do |value|
34
+ if self.values.include? value
35
+ key = self.to_a.select {|k,v| v == value }.first.first
36
+ array.put(key, value)
37
+ end
38
+ end
39
+ array
40
+ end
41
+
42
+ def add(value)
43
+ put(@index, value)
44
+ end
45
+
46
+ def put(key, value)
47
+ if key.is_a? Fixnum and key >= @index
48
+ @index = key+1
49
+ end
50
+ self[key] = value
51
+ end
52
+
53
+ def to_hash
54
+ Hash[*(self.to_a).flatten]
55
+ end
56
+
57
+ def merge(other)
58
+ if self.array? and other.array?
59
+ self.class.from_array(*(self.values + other.values))
60
+ else
61
+ self.class.from_hash(super(other))
62
+ end
63
+ end
64
+
65
+ def self.from_mixed(*args)
66
+ array = self.new
67
+ args.each do |arg|
68
+ if arg.is_a? Hash
69
+ arg.each {|key, value| array.put(key, value)}
70
+ else
71
+ array.add(arg)
72
+ end
73
+ end
74
+ array
75
+ end
76
+
77
+ def self.from_hash(hash)
78
+ array = self.new
79
+ hash.each {|key, value| array.put(key, value)}
80
+ array
81
+ end
82
+
83
+ def self.from_array(*args)
84
+ array = self.new
85
+ args.each {|arg| array.add(arg)}
86
+ array
87
+ end
88
+
89
+ def inspect
90
+ "array(" + super.gsub("{", "").gsub("}", "") + ")"
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,272 @@
1
+
2
+ require 'rubygems'
3
+ require 'dhaka'
4
+
5
+ class Dhaka::Grammar
6
+ def for_rule(hash)
7
+ i = 0
8
+ name = hash.keys.first
9
+ for_symbol(name) do
10
+ hash.values.first.split("|").each do |s|
11
+ send "#{name}_#{i += 1}", *(s.split)
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ module Ionize
18
+ module Php
19
+ class Grammar < Dhaka::Grammar
20
+ precedences do
21
+ left %w{ expr }
22
+ left %w{ tertiary }
23
+ left %w{ + - }
24
+ left %w{ term_plus_expr term_minus_expr }
25
+ left %w{ preop op }
26
+ left %w{ fun_call fun_call_wo_parens }
27
+ end
28
+
29
+ for_symbol(Dhaka::START_SYMBOL_NAME) do
30
+ html_file_with_php_with_end_and_html_string %w{ html_string php_start statements php_end html_string }
31
+ html_file_with_php_with_end %w{ html_string php_start statements php_end }
32
+ html_file_with_php_without_end %w{ html_string php_start statements }
33
+ php_file %w{ statements }
34
+ end
35
+
36
+ for_symbol 'statements' do
37
+ multiple_statements %w{ statement statements }
38
+ single_statement %w{ statement }
39
+ end
40
+
41
+ for_symbol 'statement' do
42
+ matched %w{ matched }
43
+ unmatched %w{ unmatched }
44
+ if_statement_b " if ( expr ) { statements } ".split
45
+ if_else_statement_b " if ( expr ) { statements } else { statements } ".split
46
+ if_else_statement_b1 " if ( expr ) { statements } else statement ".split
47
+ end
48
+
49
+ for_symbol 'matched' do
50
+ if_else_statement0 " if ( expr ) ; else matched ".split
51
+ if_else_statement1 " if ( expr ) matched else matched ".split
52
+ if_else_statement3 " if ( expr ) matched else { statements } ".split
53
+ simple %w{ simple_statement }
54
+ end
55
+
56
+ for_symbol 'unmatched' do
57
+ if_statement0 " if ( expr ) matched ".split
58
+ if_statement1 " if ( expr ) unmatched ".split
59
+ if_else_statement2 " if ( expr ) matched else unmatched ".split
60
+ end
61
+
62
+ for_symbol 'block' do
63
+ empty_braces " { } ".split
64
+ braced_statements " { statements } ".split
65
+ end
66
+
67
+ for_symbol 'simple_statement' do
68
+ line_statement %w{ expr ; }
69
+ class_statement " class word { statements } ".split
70
+ class_extends_statement " class word extends word { statements } ".split
71
+
72
+ while_statement " while ( expr ) block ".split
73
+ do_while_statement " do block while ( expr ) ; ".split
74
+ for_statement " for ( expr ; expr ; expr ) block ".split
75
+ foreach_statement " foreach ( expr as expr ) block ".split
76
+ switch_case_statement " switch ( expr ) { case_statements } ".split
77
+ fun_define " function word ( fun_args ) { statements } ".split
78
+ protected_fun_define " protected function word ( fun_args ) { statements } ".split
79
+ private_fun_define " private function word ( fun_args ) { statements } ".split
80
+
81
+ static_declaration %w{ static variables ; }
82
+ global_declaration %w{ global variables ; }
83
+ class_protected_var_statement %w{ protected variable ; }
84
+ class_protected_w_default_stmt %w{ protected variable = expr ; }
85
+ class_var_statement %w{ var variable ; }
86
+ class_var_with_default_statement %w{ var variable = expr ; }
87
+ return_one %w{ return expr ; }
88
+ print_statement %w{ print expr ; }
89
+ echo_statement %w{ echo expr ; }
90
+ return_zero %w{ return ; }
91
+ break_statement %w{ break ; }
92
+ exit_call_statement %w{ exit ( ) ; }
93
+ exit_statement %w{ exit ; }
94
+ end
95
+
96
+ for_symbol 'elseif_statements' do
97
+ one_elseif %w{ elseif_statement }
98
+ multi_else_ifs %w{ elseif_statements elseif_statement }
99
+ end
100
+
101
+ for_symbol 'elseif_statement' do
102
+ else_if " else if ( expr ) block ".split
103
+ end
104
+
105
+ for_symbol 'case_statements' do
106
+ default_only_case %w{ default : statements }
107
+ one_case %w{ case expr : opt_statements }
108
+ default_case %w{ case_statements default : statements }
109
+ multi_case %w{ case_statements case expr : opt_statements }
110
+ end
111
+
112
+ for_symbol 'opt_statements' do
113
+ opt_multiple_statements %w{ statement statements }
114
+ opt_single_statement %w{ statement }
115
+ opt_no_statements %w{ }
116
+ end
117
+
118
+ for_symbol 'expr' do
119
+ # TODO: This one could get cleaned up
120
+ tertiary %w{ term ? expr : expr }
121
+ term_postop %w{ term postop }
122
+ post_increment_var %w{ term ++ }
123
+ post_decrement_var %w{ term -- }
124
+ reference_term %w{ & term }
125
+
126
+ term_or_expr %w{ term || expr }
127
+ term_or_expr_with_or %w{ term or expr }
128
+ term_and_expr %w{ term && expr }
129
+ term_boolean_and_expr %w{ term & expr }
130
+ term_and_expr_with_and %w{ term and expr }
131
+ term_not_equal_expr %w{ term <> expr }
132
+ term_less_than_expr %w{ term < expr }
133
+ term_greater_than_expr %w{ term > expr }
134
+
135
+ require_statement %w{ require expr }
136
+ require_once_statement %w{ require_once expr }
137
+ include_statement %w{ include expr }
138
+ include_once_statement %w{ include_once expr }
139
+
140
+ list_assign %w{ list ( fun_args ) = expr }
141
+
142
+ # oo_variable_variable_assign %w{ term -> variable = expr }
143
+ # oo_array_assign %w{ term -> word [ term ] = expr }
144
+
145
+ # term_array_append %w{ term [ ] = expr }
146
+ # term_array_assign %w{ term [ expr ] = expr }
147
+
148
+ # Unfortunately, I'm going to have to stuff everything
149
+ # inside this one statement.. so that others that I
150
+ # cannot predict can be parsed
151
+ term_assign %w{ term = expr }
152
+
153
+ term_concat_expr %w{ expr . term }
154
+ term_plus_expr %w{ expr + expr }
155
+ term_minus_expr %w{ expr - expr }
156
+ term_not_equals_expr %w{ expr != expr }
157
+ term_hashs_to_expr %w{ expr => expr }
158
+
159
+ term_op_expr %w{ term op expr }
160
+ term %w{ term }
161
+ end
162
+
163
+ for_symbol 'term' do
164
+ # TODO: This one could get cleaned up
165
+ number %w{ number }
166
+ false_value %w{ false }
167
+ true_value %w{ true }
168
+ a_string %w{ a_string }
169
+ a_variable %w{ variable }
170
+ class_method_call %w{ word :: word ( fun_call_args ) }
171
+
172
+ array_append %w{ term [ ] }
173
+ array_lookup %w{ term [ expr ] }
174
+ oo_variable %w{ term -> word }
175
+ oo_call %w{ term -> word ( fun_call_args ) }
176
+ oo_variable_variable %w{ term -> variable }
177
+
178
+ fun_call_trailing_comma %w{ word ( fun_call_args , ) }
179
+ regular_fun_call %w{ word ( fun_call_args ) }
180
+ var_call %w{ variable ( fun_call_args ) }
181
+ user_constant %w{ word }
182
+ new_object %w{ new word }
183
+ new_object_with_args %w{ new word ( fun_call_args ) }
184
+ new_variable_class_object %w{ new variable }
185
+ braced_lookup %w{ variable { expr } }
186
+
187
+ pre_increment_var %w{ ++ term }
188
+ pre_decrement_var %w{ -- term }
189
+ negative_var %w{ - term }
190
+ not_var %w{ ! term }
191
+
192
+ term_error_guard %w{ @ expr }
193
+
194
+ preop_term %w{ preop term }
195
+ paren_cast %w{ ( cast ) term }
196
+ paren_expr %w{ ( expr ) }
197
+ end
198
+
199
+ for_symbol 'a_string' do
200
+ composite_double_string %w{ open_double_quoted_string composite_string_nodes }
201
+ double_string %w{ double_quoted_string }
202
+ single_string %w{ single_quoted_string }
203
+ end
204
+
205
+ for_symbol 'composite_string_node' do
206
+ variable_within_string %w{ variable }
207
+ array_lookup_within_string %w{ array_lookup_node }
208
+ double_string_within_string %w{ double_quoted_string_node }
209
+ single_string_within_string %w{ single_quoted_string_node }
210
+ end
211
+
212
+ for_symbol 'composite_string_nodes' do
213
+ multiple_nodes %w{ composite_string_nodes composite_string_node }
214
+ single_node %w{ composite_string_node }
215
+ end
216
+
217
+ for_symbol 'variables' do
218
+ one_variable %w{ variable }
219
+ multiple_variables %w{ variables , variable }
220
+ end
221
+
222
+ for_symbol 'number' do
223
+ positive_number %w{ num }
224
+ signed_number %w{ op num }
225
+ end
226
+
227
+ for_symbol 'bracketed_expr' do
228
+ one_bracket %w{ single_bracket }
229
+ multi_brackets %w{ bracketed_expr single_bracket }
230
+ end
231
+
232
+ for_symbol 'oo_fun_call' do
233
+ oo_fun_call_trailing_comma %w{ word ( fun_call_args , ) }
234
+ oo_regular_fun_call %w{ word ( fun_call_args ) }
235
+ end
236
+
237
+ for_symbol 'fun_args' do
238
+ no_args %w{ }
239
+ single_arg %w{ expr }
240
+ multiple_args %w{ fun_args , expr }
241
+ end
242
+
243
+ for_symbol 'fun_args_within_call' do
244
+ no_args_within_call %w{ }
245
+ single_arg_within_call %w{ expr }
246
+ multiple_args_within_call %w{ fun_args_within_call , expr }
247
+ end
248
+
249
+ for_symbol 'fun_call_args' do
250
+ fun_args_within_call %w{ fun_args_within_call }
251
+ fun_args_with_trailing_comma %w{ fun_args_within_call , }
252
+ end
253
+ end
254
+ end
255
+ end
256
+
257
+
258
+
259
+ my_filename = "lib/ionize/parser.rb"
260
+ compiled_filename = "lib/ionize/compiled_parser.rb"
261
+
262
+ if File.stat(my_filename).ctime > File.stat(compiled_filename).ctime
263
+ puts "Grammar newer than CompiledParser. Recompiling.."
264
+ null = Object.new.instance_eval do
265
+ def method_missing(*args, &blk); end
266
+ self
267
+ end
268
+ File.open("lib/ionize/compiled_parser.rb", "w+") do |f|
269
+ f.write(Dhaka::Parser.new(Ionize::Php::Grammar, null).compile_to_ruby_source_as("Ionize::Php::CompiledParser"))
270
+ end
271
+ end
272
+