phaad 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Phaad
2
+
3
+ Phaad is a little language, written in Ruby, implementing a subset of Ruby's syntax, and compiling it down to PHP.
4
+
5
+ ## Status
6
+
7
+ Phaad is a work in progress. I made it to save myself from typing dollar signs, and brackets everywhere, when developing in PHP, while teaching myself a little about Lexers and Parsers.
8
+
9
+ The following features are currently implemented
10
+
11
+ - Unary operators ~, +, -, !
12
+ - Binary operators
13
+ - Arithmetic: +, -, \*, /, %, \*\* (converted to `pow`)
14
+ - Logical: &&, ||, and, or
15
+ - Bitwise: |, &, ^
16
+ - Comparison: ==, !=, >, <, >=, <=, ===
17
+ - Regex match: =~, !~ (converted to `preg_match`)
18
+ - Assigning variables, both single and multiple at once (`a, b = 1, 2` => `$a = 1;\n$b=2;`)
19
+ - if, unless, while, and until statements, both in the long and one line form of Ruby
20
+ - Function definitions
21
+ - Arrays, both linear and associative
22
+
23
+ ## Getting Started
24
+
25
+ ### Installing
26
+
27
+ It's best to install the latest revision from the repository.
28
+
29
+ git clone https://github.com/utkarshkukreti/phaad.git
30
+ cd phaad
31
+ bundle install
32
+ rake spec # optional
33
+ rake install
34
+
35
+ Installing the gem will provide you with a `phaad` command. Invoking it without any parameters brings up an interactive REPL, similar to IRB. You can type in code, and get the generated PHP code back instantly.
36
+
37
+ ## Examples
38
+
39
+ It's best to checkout the [spec](https://github.com/utkarshkukreti/phaad/tree/master/spec) directory for a list of features with examples for now.
40
+
41
+ ## License
42
+
43
+ MIT License. (c) 2011 Utkarsh Kukreti.
44
+
data/lib/phaad/cli.rb CHANGED
@@ -1,24 +1,87 @@
1
1
  module Phaad
2
2
  class CLI
3
3
  def initialize(argv)
4
- repl
4
+ @options = Slop.parse! argv, :help => true do
5
+ banner "Usage: phaad [options] [file]"
6
+ on :c, :compile, "Compile to PHP, and save as .php files"
7
+ on :i, :interactive, "Run an interactive Phaad REPL"
8
+ on :s, :stdio, "Fetch, compile, and print a Phaad script over stdio"
9
+ on :e, :eval, "Compile a string from command line", true
10
+ on :w, :watch, "Watch a Phaad file for changes, and autocompile"
11
+ on :v, :version, "Print Phaad version" do
12
+ puts Phaad::VERSION
13
+ exit
14
+ end
15
+ end
16
+
17
+ if @options.interactive?
18
+ repl
19
+ elsif @options.eval?
20
+ puts compile(@options[:eval])
21
+ elsif @options.stdio?
22
+ puts "<?php\n"
23
+ puts compile(STDIN.readlines.join("\n"))
24
+ elsif @options.compile?
25
+ input_file = argv.shift
26
+ output_file = input_file.sub(/\..*?$/, '.php')
27
+ File.open(output_file, 'w') do |f|
28
+ f << "<?php\n"
29
+ f << compile(File.read(input_file))
30
+ end
31
+ elsif @options.watch?
32
+ require 'fssm'
33
+
34
+ input_file = argv.shift
35
+ output_file = input_file.sub(/\..*?$/, '.php')
36
+ FSSM.monitor(File.dirname(input_file), File.basename(input_file)) do
37
+ update do
38
+ puts ">>> Detected changes in #{input_file}"
39
+ File.open(output_file, 'w') do |f|
40
+ f << "<?php\n"
41
+ f << Phaad::Generator.new(File.read(input_file)).emitted
42
+ end
43
+ puts ">>> Compiled!"
44
+ end
45
+ end
46
+ else
47
+ repl
48
+ end
49
+ end
50
+
51
+ def compile(input)
52
+ Phaad::Generator.new(input).emitted
5
53
  end
6
54
 
7
- ##
8
- # A very primitive repl. Only handles one lines.
9
55
  def repl
10
- puts "Type exit to exit."
11
- print "> "
12
- while (input = gets.chomp) != "exit"
13
- begin
14
- puts Phaad::Generator.new(input).emitted
15
- rescue Exception => e
16
- puts e
17
- p e.message
18
- puts e.backtrace
56
+ require 'readline'
57
+
58
+ puts "Type exit or press Ctrl-D to exit."
59
+ lines = ""
60
+ loop do
61
+ prompt = lines.size == 0 ? '> ' : '>> '
62
+ line = Readline::readline(prompt)
63
+
64
+ exit if line.nil? || line == "exit" && lines.size == 0
65
+
66
+ lines << line + "\n"
67
+ Readline::HISTORY.push line
68
+
69
+ if valid_expression?(lines)
70
+ begin
71
+ puts Phaad::Generator.new(lines).emitted
72
+ rescue Exception => e
73
+ puts e
74
+ p e.message
75
+ puts e.backtrace
76
+ ensure
77
+ lines = ""
78
+ end
19
79
  end
20
- print "> "
21
80
  end
22
81
  end
82
+
83
+ def valid_expression?(lines)
84
+ !!Ripper.sexp(lines)
85
+ end
23
86
  end
24
87
  end
@@ -14,44 +14,404 @@ module Phaad
14
14
  end
15
15
 
16
16
  @emitted = ""
17
- @sexp.last.each(&method(:process))
17
+ @indent_level = 0
18
+ @indent_with = " "
19
+ process_statements @sexp.last, :indent => false
20
+ @emitted.chomp!
18
21
  end
19
22
 
20
- def emit(str)
21
- @emitted << str
23
+ def process_statements(array, options = {})
24
+ indent unless options[:indent] == false
25
+ array.each do |sexp|
26
+ process(sexp)
27
+ should_not_be = [ [:bodystmt, [[:void_stmt]], nil, nil, nil] ]
28
+ first_should_not_be = [:void_stmt, :def, :bodystmt, :if, :else, :elsif,
29
+ :unless, :while, :until, :while_mod, :until_mod, :if_mod, :unless_mod,
30
+ :massign, :class, :for]
31
+ emit ";\n" if !should_not_be.include?(sexp) && !first_should_not_be.include?(sexp.first)
32
+ end
33
+ outdent unless options[:indent] == false
34
+ end
35
+
36
+ def indent
37
+ @indent_level += 1
38
+ end
39
+
40
+ def outdent
41
+ @indent_level -= 1
42
+ end
43
+
44
+ def emit(*strings)
45
+ strings.each do |string|
46
+ @emitted << @indent_with * @indent_level if @emitted[-1] == "\n"
47
+ @emitted << string
48
+ end
22
49
  end
23
50
 
24
51
  def process(sexp)
25
52
  case sexp.first
53
+ when :void_stmt
26
54
  when :@int, :@float
27
55
  emit sexp[1]
28
56
  when :@tstring_content
29
- "\"#{sexp[1]}\""
57
+ emit "\"#{sexp[1].gsub('"', '\"')}\""
30
58
  when :string_content
31
- process sexp[1]
59
+ if sexp.size > 1
60
+ sexp[1..-1].each_with_index do |exp, i|
61
+ unless exp[0] == :string_embexpr && exp[1][0][0] == :void_stmt
62
+ process exp
63
+ emit " . " if i < sexp.size - 2
64
+ end
65
+ end
66
+ else
67
+ emit '""'
68
+ end
32
69
  when :string_literal
33
- emit process(sexp[1])
70
+ process(sexp[1])
71
+ when :string_embexpr
72
+ process sexp[1][0]
34
73
  when :regexp_literal
35
- emit "\"/#{sexp[1][0][1]}#{process(sexp[2])}\""
74
+ emit '"/'
75
+ emit sexp[1][0][1]
76
+ process sexp[2]
77
+ emit '"'
36
78
  when :@regexp_end
37
- sexp[1]
79
+ emit sexp[1]
38
80
  when :symbol_literal
39
81
  emit sexp[1][1][1].inspect
40
82
  when :dyna_symbol
41
- emit process(sexp[1][0])
83
+ process(sexp[1][0])
84
+ when :assign
85
+ process(sexp[1])
86
+ emit " = "
87
+ process(sexp[2])
88
+ when :massign
89
+ lhs = sexp[1]
90
+ rhs = sexp[2]
91
+
92
+ # hack, no idea why is the sexp like this.
93
+ rhs.shift if rhs.first == :mrhs_new_from_args
94
+ rhs = rhs[0] + [rhs[1]]
95
+ unless lhs.size == rhs.size
96
+ raise NotImplementedError, sexp.inspect
97
+ end
98
+
99
+ lhs.zip(rhs).each do |l, r|
100
+ process l
101
+ emit " = "
102
+ process r
103
+ emit ";\n"
104
+ end
105
+ when :return
106
+ emit "return "
107
+ process sexp[1]
108
+ when :var_field
109
+ process(sexp[1])
110
+ when :@ident
111
+ no_dollar = ["__NAMESPACE__", "__DIR__", "__METHOD__", "__CLASS__", "__FUNCTION__"]
112
+ emit "$" unless no_dollar.include?(sexp[1])
113
+ emit sexp[1]
114
+ when :@ivar
115
+ emit "$this->"
116
+ emit sexp[1][1..-1]
117
+ when :@const
118
+ emit sexp[1]
119
+ when :method_add_arg
120
+ if sexp[1][0] == :fcall && sexp[1][1][0] == :@ident
121
+ emit sexp[1][1][1]
122
+ emit "("
123
+ process sexp[2][1] if sexp[2][1]
124
+ emit ")"
125
+ elsif sexp[1][0] == :call
126
+ process sexp[1]
127
+ emit "("
128
+ process sexp[2][1] if sexp[2][1]
129
+ emit ")"
130
+ else
131
+ raise NotImplementedError, sexp.inspect
132
+ end
133
+ when :ifop
134
+ process sexp[1]
135
+ emit " ? "
136
+ process sexp[2]
137
+ emit " : "
138
+ process sexp[3]
139
+ when :command
140
+ if sexp[1][0] == :@ident
141
+ no_brackets = %w{global echo}
142
+ emit sexp[1][1]
143
+ emit no_brackets.include?(sexp[1][1]) ? " " : "("
144
+ if sexp[2][0] == :args_add_block
145
+ process sexp[2]
146
+ else
147
+ sexp[2].each(&method(:process))
148
+ end
149
+ emit ")" unless no_brackets.include?(sexp[1][1])
150
+ else
151
+ raise NotImplementedError, sexp.inspect
152
+ end
153
+ when :args_add_block
154
+ sexp[1].each do |s|
155
+ process s
156
+ emit ", " unless s == sexp[1].last
157
+ end
158
+ when :call
159
+ process sexp[1]
160
+ emit "->"
161
+ emit sexp[3][1]
162
+ when :command_call
163
+ if sexp[1][0] == :var_ref && sexp[1][1][0] == :@ident && sexp[2] == :"." &&
164
+ sexp[3][0] == :@ident && sexp[4][0] == :args_add_block
165
+ process sexp[1]
166
+ emit "->"
167
+ emit sexp[3][1]
168
+ emit "("
169
+ process sexp[4]
170
+ emit ")"
171
+ else
172
+ raise NotImplementedError, sexp.inspect
173
+ end
174
+ when :if, :elsif, :unless
175
+ if sexp.first == :if
176
+ emit "if("
177
+ elsif sexp.first == :unless
178
+ emit "if(!("
179
+ else
180
+ emit "elseif("
181
+ end
182
+ process sexp[1]
183
+ emit ")" if sexp.first == :unless
184
+ emit ") {\n"
185
+ process_statements(sexp[2])
186
+ emit "}"
187
+ if sexp[3]
188
+ emit " "
189
+ process sexp[3]
190
+ else
191
+ emit "\n"
192
+ end
193
+ when :else
194
+ emit "else {\n"
195
+ process_statements(sexp[1]) if sexp[1]
196
+ emit "}\n"
197
+ process sexp[3] if sexp[3]
198
+ when :if_mod, :unless_mod
199
+ emit "if("
200
+ emit "!(" if sexp.first == :unless_mod
201
+ process sexp[1]
202
+ emit ")" if sexp.first == :unless_mod
203
+ emit ") {\n"
204
+ process_statements [sexp[2]]
205
+ emit "}\n"
206
+ when :while, :until
207
+ emit "while("
208
+ emit "!(" if sexp.first == :until
209
+ process sexp[1]
210
+ emit ")" if sexp.first == :until
211
+ emit ") {\n"
212
+ process_statements sexp[2]
213
+ emit "}\n"
214
+ when :while_mod, :until_mod
215
+ emit "while("
216
+ emit "!(" if sexp.first == :until_mod
217
+ process sexp[1]
218
+ emit ")" if sexp.first == :until_mod
219
+ emit ") {\n"
220
+ process_statements [sexp[2]]
221
+ emit "}\n"
222
+ when :for
223
+ if !sexp[1][0].is_a?(Array) && sexp[2][0] == :dot2 || sexp[2][0] == :dot3
224
+ emit "for("
225
+ process sexp[1]
226
+ emit " = "
227
+ process sexp[2][1]
228
+ emit "; "
229
+ process sexp[1]
230
+ emit sexp[2][0] == :dot2 ? " <= " : " < "
231
+ process sexp[2][2]
232
+ emit "; "
233
+ process sexp[1]
234
+ emit "++"
235
+ else
236
+ emit "foreach("
237
+ process sexp[2]
238
+ emit " as "
239
+ if !sexp[1][0].is_a?(Array)
240
+ process sexp[1]
241
+ elsif sexp[1][0].is_a?(Array) && sexp[1].size == 2
242
+ process sexp[1][0]
243
+ emit " => "
244
+ process sexp[1][1]
245
+ else
246
+ raise NotImplementedError, sexp.inspect
247
+ end
248
+ end
249
+ emit ") {\n"
250
+ process_statements sexp[3]
251
+ emit "}\n"
252
+ when :def
253
+ raise NotImplementedError, sexp.inspect unless sexp[1][0] == :@ident
254
+ emit "function "
255
+ emit sexp[1][1]
256
+ emit "("
257
+ if sexp[2][0] == :params
258
+ process sexp[2] # params
259
+ elsif sexp[2][0] == :paren && sexp[2][1][0] == :params
260
+ process sexp[2][1]
261
+ else
262
+ raise NotImplementedError, sexp.inspect
263
+ end
264
+ emit ") {\n"
265
+ process_statements [sexp[3]]
266
+ emit "}\n"
267
+ when :class
268
+ if sexp[1][1][0] != :@const || (sexp[2] && sexp[2][1][0] != :@const)
269
+ raise NotImplementedError, sexp.inspect
270
+ end
271
+ emit "class "
272
+ emit sexp[1][1][1]
273
+ emit " extends #{sexp[2][1][1]}" if sexp[2]
274
+ emit " {\n"
275
+ process_statements [sexp[3]]
276
+ emit "}\n"
277
+ when :params
278
+ first = true
279
+ if sexp[1]
280
+ sexp[1].each do |param|
281
+ emit ", " unless first
282
+ first = false
283
+
284
+ process param
285
+ end
286
+ end
287
+
288
+ if sexp[2]
289
+ sexp[2].each do |param|
290
+ emit ", " unless first
291
+ first = false
292
+
293
+ process param[0]
294
+ emit " = "
295
+ process param[1]
296
+ end
297
+ end
298
+ when :array, :hash
299
+ emit "array("
300
+ if sexp[1]
301
+ if sexp[1][0] == :assoclist_from_args
302
+ process sexp[1]
303
+ else
304
+ sexp[1].each_with_index do |param, i|
305
+ process param
306
+ emit ", " if i < sexp[1].size - 1
307
+ end
308
+ end
309
+ end
310
+ emit ")"
311
+ when :assoclist_from_args
312
+ sexp[1].each_with_index do |param, i|
313
+ process param
314
+ emit ", " if i < sexp[1].size - 1
315
+ end
316
+ when :assoc_new
317
+ process sexp[1]
318
+ emit " => "
319
+ process sexp[2]
320
+ when :bodystmt
321
+ process_statements(sexp[1], :indent => false)
322
+ # skip rescue and ensure
323
+ when :paren
324
+ emit "("
325
+ if sexp[1].size == 1
326
+ process sexp[1][0]
327
+ else
328
+ raise NotImplementedError, sexp.inspect
329
+ end
330
+ emit ")"
331
+ when :aref_field
332
+ process sexp[1]
333
+ emit "["
334
+ process sexp[2] if sexp[2]
335
+ emit "]"
336
+ when :aref
337
+ process sexp[1]
338
+ emit "["
339
+ if sexp[2][1].size == 1
340
+ process sexp[2] if sexp[2][1][0]
341
+ else
342
+ raise NotImplementedError, sexp.inspect
343
+ end
344
+ emit "]"
345
+ when :unary
346
+ case sexp[1]
347
+ when :+@, :-@, :~, :'!'
348
+ emit sexp[1].to_s[0]
349
+ process sexp[2]
350
+ else
351
+ raise NotImplementedError, sexp.inspect
352
+ end
353
+ when :binary
354
+ case sexp[2]
355
+ when :+, :-, :*, :/, :%, :|, :&, :^, :'&&', :'||', :==, :'!=', :>, :<,
356
+ :>=, :<=, :===
357
+ process(sexp[1])
358
+ emit " #{sexp[2]} "
359
+ process(sexp[3])
360
+ when :and
361
+ process(sexp[1])
362
+ emit " && "
363
+ process(sexp[3])
364
+ when :or
365
+ process(sexp[1])
366
+ emit " || "
367
+ process(sexp[3])
368
+ when :<<
369
+ process(sexp[1])
370
+ emit " . "
371
+ process(sexp[3])
372
+ when :**
373
+ emit "pow("
374
+ process(sexp[1])
375
+ emit ", "
376
+ process(sexp[3])
377
+ emit ")"
378
+ when :=~
379
+ emit "preg_match("
380
+ process(sexp[3])
381
+ emit ", "
382
+ process(sexp[1])
383
+ emit ")"
384
+ when :'!~'
385
+ emit "!preg_match("
386
+ process(sexp[3])
387
+ emit ", "
388
+ process(sexp[1])
389
+ emit ")"
390
+ else
391
+ raise NotImplementedError, sexp.inspect
392
+ end
42
393
  when :var_ref
394
+ # probably need to remove this
43
395
  if sexp[1][0] == :@kw
44
- case sexp[1][1]
45
- when 'false', 'true'
46
- emit sexp[1][1].upcase
47
- when 'nil'
48
- emit 'NULL'
49
- else
50
- raise NotImplementedError, sexp
51
- end
396
+ case sexp[1][1]
397
+ when 'false', 'true'
398
+ emit sexp[1][1].upcase
399
+ when 'nil'
400
+ emit 'NULL'
401
+ when '__FILE__', '__LINE__'
402
+ emit sexp[1][1]
403
+ else
404
+ raise NotImplementedError, sexp.inspect
405
+ end
406
+ elsif sexp[1][0] == :@ident
407
+ process sexp[1]
408
+ elsif sexp[1][0] == :@ivar
409
+ process sexp[1]
410
+ elsif sexp[1][0] == :@const
411
+ process sexp[1]
52
412
  else
53
413
  # later
54
- raise NotImplementedError, sexp
414
+ raise NotImplementedError, sexp.inspect
55
415
  end
56
416
  else
57
417
  raise NotImplementedError, sexp.inspect
data/lib/phaad/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Phaad
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/phaad.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require 'slop'
2
+
1
3
  require 'ripper'
2
4
  require 'phaad/cli'
3
5
  require 'phaad/generator'
6
+ require 'phaad/version'
data/phaad.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Utkarsh Kukreti"]
10
10
  s.email = ["utkarshkukreti@gmail.com"]
11
- s.homepage = ""
11
+ s.homepage = "https://github.com/utkarshkukreti/phaad/"
12
12
  s.summary = %q{A beautiful way to write PHP}
13
13
  s.description = %q{A beautiful way to write PHP}
14
14
 
@@ -19,5 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
+ s.add_dependency 'slop', '~>1.6.0'
23
+ s.add_dependency 'fssm', '~>0.2.7'
22
24
  s.add_development_dependency 'rspec', '~>2.6.0'
23
25
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'array creation' do
4
+ context "linear arrays" do
5
+ it "should create blank array" do
6
+ compile("[]").should == "array();"
7
+ end
8
+
9
+ it "should create simple arrays" do
10
+ compile("[1, 'a', /b/, true]").should == 'array(1, "a", "/b/", TRUE);'
11
+ end
12
+
13
+ it "should create nested arrays" do
14
+ compile("[1, [1, [2, [3, [5, [8, 13]]]]]]").should ==
15
+ "array(1, array(1, array(2, array(3, array(5, array(8, 13))))));"
16
+ end
17
+ end
18
+
19
+ context "associative arrays" do
20
+ it "should create blank array" do
21
+ compile("{}").should == "array();"
22
+ end
23
+
24
+ it "should create simple arrays" do
25
+ compile("{a => :b, 1 => 2, /foo/ => /bar/}").should ==
26
+ 'array($a => "b", 1 => 2, "/foo/" => "/bar/");'
27
+ end
28
+
29
+ it "should create nested arrays" do
30
+ compile("{1 => {2 => {3 => {4 => {5 => 6}}}}}").should ==
31
+ "array(1 => array(2 => array(3 => array(4 => array(5 => 6)))));"
32
+ end
33
+ end
34
+
35
+ it "should parse a mix of linear and associative arrays" do
36
+ compile("[{}, [], {foo => [1, 2, 3, {}]}]").should ==
37
+ "array(array(), array(), array($foo => array(1, 2, 3, array())));"
38
+ end
39
+ end
40
+
41
+ describe Phaad::Generator, 'array access' do
42
+ context "linear arrays" do
43
+ it "should allow access" do
44
+ compile("a[0]") == "$a[0];"
45
+ end
46
+ end
47
+
48
+ context "associative arrays" do
49
+ it "should allow access" do
50
+ compile("a['foo']") == '$a["foo"];'
51
+ compile("a[:foo]") == '$a["foo"];'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'assign' do
4
+ it "should assign simple variables" do
5
+ compile("a = 1").should == "$a = 1;"
6
+ compile('a = "foo"').should == '$a = "foo";'
7
+ compile("a = /foo/").should == '$a = "/foo/";'
8
+ end
9
+
10
+ it "should assign array fields" do
11
+ compile("a[] = 1").should == "$a[] = 1;"
12
+ compile("a[0] = 1").should == "$a[0] = 1;"
13
+ compile("a['foo'] = 1").should == '$a["foo"] = 1;'
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, "binary" do
4
+ it "should parse +, -, *, /, %" do
5
+ compile("1 + 2").should == "1 + 2;"
6
+ compile("1 - 2").should == "1 - 2;"
7
+ compile("1 * 2").should == "1 * 2;"
8
+ compile("1 / 2").should == "1 / 2;"
9
+ compile("1 % 2").should == "1 % 2;"
10
+ end
11
+
12
+ it "should parse | & ^" do
13
+ compile("1 | 2").should == "1 | 2;"
14
+ compile("1 & 2").should == "1 & 2;"
15
+ compile("1 ^ 2").should == "1 ^ 2;"
16
+ end
17
+
18
+ it "should parse ** to pow" do
19
+ compile("1 ** 2").should == "pow(1, 2);"
20
+ end
21
+
22
+ it "should parse && ||" do
23
+ compile("true && false").should == "TRUE && FALSE;"
24
+ compile("true || false").should == "TRUE || FALSE;"
25
+ end
26
+
27
+ it "should parse 'and' and 'or'" do
28
+ compile("true and false").should == "TRUE && FALSE;"
29
+ compile("true or false").should == "TRUE || FALSE;"
30
+ end
31
+
32
+ it "should parse chain of binary statements" do
33
+ compile("1 + 2 - 3 + 4").should == "1 + 2 - 3 + 4;"
34
+ compile("1 + 2 ** 3 ** 4").should == "1 + pow(2, pow(3, 4));"
35
+ end
36
+
37
+ it "should parse == != > < >= <= ===" do
38
+ compile("1 == 2").should == "1 == 2;"
39
+ compile("1 != 2").should == "1 != 2;"
40
+ compile("1 > 2").should == "1 > 2;"
41
+ compile("1 < 2").should == "1 < 2;"
42
+ compile("1 >= 2").should == "1 >= 2;"
43
+ compile("1 <= 2").should == "1 <= 2;"
44
+ compile("1 === 2").should == "1 === 2;"
45
+ end
46
+
47
+ it "should parse =~ and !~ to preg_match statements" do
48
+ compile("a =~ b").should == "preg_match($b, $a);"
49
+ compile("a !~ b").should == "!preg_match($b, $a);"
50
+ end
51
+
52
+ it "should parse << as ." do
53
+ compile("'foo' << 'bar'").should == '"foo" . "bar";'
54
+ end
55
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'class' do
4
+ context 'define a class' do
5
+ it "should define a bare class" do
6
+ compile("class A; end").should == "class A {\n}"
7
+ end
8
+
9
+ it "should define a class which inherits" do
10
+ compile("class A < B; end").should == "class A extends B {\n}"
11
+ end
12
+ end
13
+
14
+ context 'access object variables' do
15
+ it "should allow accessing a.b" do
16
+ compile("a.b").should == "$a->b;"
17
+ end
18
+
19
+ it "should allow accessing a.b.c.d.e" do
20
+ compile("a.b.c.d.e").should == "$a->b->c->d->e;"
21
+ end
22
+ end
23
+
24
+ context "access object functions" do
25
+ it "should allow calling a.b()" do
26
+ compile("a.b()").should == "$a->b();"
27
+ end
28
+
29
+ it "should allow calling a.b c" do
30
+ compile("a.b c").should == "$a->b($c);"
31
+ compile("a.b(c)").should == "$a->b($c);"
32
+ end
33
+
34
+ it "should allow calling a.b c, d" do
35
+ compile("a.b c, d").should == "$a->b($c, $d);"
36
+ compile("a.b(c, d)").should == "$a->b($c, $d);"
37
+ end
38
+ end
39
+
40
+ context "chained access" do
41
+ it "mix of chained variable and function calls" do
42
+ compile("a.b(c, d).e(f, g).h().i").should == "$a->b($c, $d)->e($f, $g)->h()->i;"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'constants' do
4
+ it "should allow referring to constants" do
5
+ compile("FOO").should == "FOO;"
6
+ end
7
+
8
+ # http://php.net/manual/en/language.constants.predefined.php
9
+ context "magic constants" do
10
+ it "should parse __LINE__" do
11
+ compile("__LINE__").should == "__LINE__;"
12
+ end
13
+
14
+ it "should parse __FILE__" do
15
+ compile("__FILE__").should == "__FILE__;"
16
+ end
17
+
18
+ it "should parse __DIR__" do
19
+ compile("__DIR__").should == "__DIR__;"
20
+ end
21
+
22
+ it "should parse __FUNCTION__" do
23
+ compile("__FUNCTION__").should == "__FUNCTION__;"
24
+ end
25
+
26
+ it "should parse __CLASS__" do
27
+ compile("__CLASS__").should == "__CLASS__;"
28
+ end
29
+
30
+ it "should parse __METHOD__" do
31
+ compile("__METHOD__").should == "__METHOD__;"
32
+ end
33
+
34
+ it "should parse __NAMESPACE__" do
35
+ compile("__NAMESPACE__").should == "__NAMESPACE__;"
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, "for" do
4
+ context "over linear or associative arrays" do
5
+ it "should parse one variable for statements" do
6
+ compile("for a in b\nend").should == "foreach($b as $a) {\n}"
7
+ end
8
+
9
+ it "should parse two variable for statements" do
10
+ compile("for a, b in c\nend").should == "foreach($c as $a => $b) {\n}"
11
+ end
12
+
13
+ it "should parse the body" do
14
+ compile("for a, b in c\na\nb\nend").should == "foreach($c as $a => $b) {\n $a;\n $b;\n}"
15
+ end
16
+ end
17
+
18
+ context "over ranges" do
19
+ it "should loop over a simple range" do
20
+ compile("for a in 1..10\nend").should == "for($a = 1; $a <= 10; $a++) {\n}"
21
+ compile("for a in 1...10\nend").should == "for($a = 1; $a < 10; $a++) {\n}"
22
+ end
23
+
24
+ it "should loop over a dynamic range" do
25
+ compile("for a in 1..b\nend").should == "for($a = 1; $a <= $b; $a++) {\n}"
26
+ compile("for a in 1...b\nend").should == "for($a = 1; $a < $b; $a++) {\n}"
27
+ compile("for a in b..10\nend").should == "for($a = $b; $a <= 10; $a++) {\n}"
28
+ compile("for a in b...10\nend").should == "for($a = $b; $a < 10; $a++) {\n}"
29
+ compile("for a in b..c\nend").should == "for($a = $b; $a <= $c; $a++) {\n}"
30
+ compile("for a in b...c\nend").should == "for($a = $b; $a < $c; $a++) {\n}"
31
+ end
32
+
33
+ it "should parse the body" do
34
+ compile("for a in 1...10\na\nend").should == "for($a = 1; $a < 10; $a++) {\n $a;\n}"
35
+ compile("for a in b...c\na\nb\nc\nend").should ==
36
+ "for($a = $b; $a < $c; $a++) {\n $a;\n $b;\n $c;\n}"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'call a function' do
4
+ it "should parse various function calls" do
5
+ compile("f()").should == "f();"
6
+ compile("f a").should == "f($a);"
7
+ compile("f(a)").should == "f($a);"
8
+ compile("f g a").should == "f(g($a));"
9
+ compile("f g(a)").should == "f(g($a));"
10
+ compile("a b c d, e, f()").should == "a(b(c($d, $e, f())));"
11
+ end
12
+
13
+ it "should not add brackets when calling global and echo" do
14
+ compile("echo a").should == "echo $a;"
15
+ compile("echo a, b").should == "echo $a, $b;"
16
+ compile("global a").should == "global $a;"
17
+ compile("global a, b").should == "global $a, $b;"
18
+ end
19
+ end
20
+
21
+ describe Phaad::Generator, 'define a function' do
22
+ it "should define a function without params" do
23
+ compile("def f\nend").should start_with("function f()")
24
+ end
25
+
26
+ it "should define a function with basic params" do
27
+ compile("def f a\nend").should start_with("function f($a)")
28
+ compile("def f(a)\nend").should start_with("function f($a)")
29
+ compile("def f a, b\nend").should start_with("function f($a, $b)")
30
+ compile("def f(a, b)\nend").should start_with("function f($a, $b)")
31
+ end
32
+
33
+ it "should define a function with params with default values" do
34
+ compile("def f a = 4\nend").should start_with("function f($a = 4)")
35
+ compile("def f a, b = 4\nend").should start_with("function f($a, $b = 4)")
36
+ end
37
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, "if" do
4
+ it "should parse if statements" do
5
+ compile("if a\nb\nend").should == "if($a) {\n $b;\n}"
6
+ compile("if a\nb\nc\nend").should == "if($a) {\n $b;\n $c;\n}"
7
+ end
8
+
9
+ it "should parse if else statements" do
10
+ compile("if a\nb\nelse\nc\nend").should == "if($a) {\n $b;\n} else {\n $c;\n}"
11
+ end
12
+
13
+ it "should parse if elsif statements" do
14
+ compile("if a\nb\nelsif c\nd\nend").should ==
15
+ "if($a) {\n $b;\n} elseif($c) {\n $d;\n}"
16
+ end
17
+
18
+ it "should parse if elsif else statements" do
19
+ compile("if a\nb\nelsif c\nd\nelse\n e\nend").should ==
20
+ "if($a) {\n $b;\n} elseif($c) {\n $d;\n} else {\n $e;\n}"
21
+ end
22
+
23
+ it "should parse if then end statements" do
24
+ compile("if a then b end").should == "if($a) {\n $b;\n}"
25
+ end
26
+
27
+ it "should parse if then else end statements" do
28
+ compile("if a then b else c end").should == "if($a) {\n $b;\n} else {\n $c;\n}"
29
+ end
30
+
31
+ it "should parse if then elsif else end statements" do
32
+ compile("if a then b elsif c then d else e end").should ==
33
+ "if($a) {\n $b;\n} elseif($c) {\n $d;\n} else {\n $e;\n}"
34
+ end
35
+
36
+ it "should parse one line if statements" do
37
+ compile("b if a").should == "if($a) {\n $b;\n}"
38
+ end
39
+
40
+ it "should parse unless statements" do
41
+ compile("unless a\nb\nend").should == "if(!($a)) {\n $b;\n}"
42
+ compile("unless a\nb\nc\nend").should == "if(!($a)) {\n $b;\n $c;\n}"
43
+ compile("unless a + b\nc\nend").should == "if(!($a + $b)) {\n $c;\n}"
44
+ end
45
+
46
+ it "should parse one line if statements" do
47
+ compile("b unless a").should == "if(!($a)) {\n $b;\n}"
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, "instance variables" do
4
+ it "should parse instance variable correctly" do
5
+ compile("@a").should == "$this->a;"
6
+ compile("@a = 1").should == "$this->a = 1;"
7
+ end
8
+ end
@@ -2,36 +2,36 @@ require 'spec_helper'
2
2
 
3
3
  describe Phaad::Generator, "Literals" do
4
4
  it "should parse integers" do
5
- compile("1").should == "1"
5
+ compile("1").should == "1;"
6
6
  end
7
7
 
8
8
  it "should parse floats" do
9
- compile("1.0").should == "1.0"
9
+ compile("1.0").should == "1.0;"
10
10
  end
11
11
 
12
12
  it "should parse strings" do
13
- compile('"foo bar"').should == '"foo bar"'
14
- compile("'foo bar'").should == '"foo bar"'
15
- compile('"foo\nbar"').should == '"foo\nbar"'
13
+ compile('"foo bar"').should == '"foo bar";'
14
+ compile("'foo bar'").should == '"foo bar";'
15
+ compile('"foo\nbar"').should == '"foo\nbar";'
16
16
  end
17
17
 
18
18
  it "should parse regexes" do
19
- compile("/ab/").should == '"/ab/"'
20
- compile("/ab/i").should == '"/ab/i"'
21
- compile('/a\b/i').should == '"/a\\b/i"'
19
+ compile("/ab/").should == '"/ab/";'
20
+ compile("/ab/i").should == '"/ab/i";'
21
+ compile('/a\b/i').should == '"/a\\b/i";'
22
22
  end
23
23
 
24
24
  it "should parse booleans" do
25
- compile("true").should == "TRUE"
26
- compile("false").should == "FALSE"
25
+ compile("true").should == "TRUE;"
26
+ compile("false").should == "FALSE;"
27
27
  end
28
28
 
29
29
  it "should parse nil" do
30
- compile("nil").should == "NULL"
30
+ compile("nil").should == "NULL;"
31
31
  end
32
32
 
33
33
  it "should parse symbols as strings" do
34
- compile(":foo").should == '"foo"'
35
- compile(':"foo"').should == '"foo"'
34
+ compile(":foo").should == '"foo";'
35
+ compile(':"foo"').should == '"foo";'
36
36
  end
37
37
  end
@@ -0,0 +1,11 @@
1
+ describe Phaad::Generator, 'massign' do
2
+ it "should multi-assign variables" do
3
+ compile("a, b = 1, 2").should == "$a = 1;\n$b = 2;"
4
+ compile('a, b = "foo", /foo/').should == "$a = \"foo\";\n$b = \"/foo/\";"
5
+ compile("a, b, c = 1, 2, 3").should == "$a = 1;\n$b = 2;\n$c = 3;"
6
+ compile("a, b, c, d = 1, 2, 3, 4").should == "$a = 1;\n$b = 2;\n$c = 3;\n$d = 4;"
7
+ compile("a, b, c, d, e, f, g, h, i, j = 1, 1, 2, 3, 5, 8, 13, 21, 34, 55").should ==
8
+ "$a = 1;\n$b = 1;\n$c = 2;\n$d = 3;\n$e = 5;\n$f = 8;\n$g = 13;\n$h = 21;\n" +
9
+ "$i = 34;\n$j = 55;"
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'paren' do
4
+ it "should parse statements with parentheses" do
5
+ compile("(1)").should == "(1);"
6
+ compile("a + (b c)").should == "$a + (b($c));"
7
+ compile("a * (b + c * (d + e))").should == "$a * ($b + $c * ($d + $e));"
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'return' do
4
+ it "should parse return statements" do
5
+ compile("return a").should == "return $a;"
6
+ #TODO: allow return a, b -> return array($a, $b);
7
+ end
8
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'string' do
4
+ it "should parse empty strings" do
5
+ compile("''").should == '"";'
6
+ compile('""').should == '"";'
7
+ end
8
+ context "escaping" do
9
+ it "should parse double quotes" do
10
+ compile(%q{'"'}).should == %q{"\"";}
11
+ compile(%q{'\"'}).should == %q{"\\\"";}
12
+ compile(%q{'\\"'}).should == %q{"\\\"";}
13
+ end
14
+
15
+ ##
16
+ # Ripper parses single quotes the same as double quotes. There's no way to
17
+ # know if a single quote was used or a double quote was.
18
+ # it "should respect escape sequences in single and double quotes" do
19
+ # compile(%q{"\n"}).should == %q{"\\n";}
20
+ # compile(%q{'\n'}).should == %q{"\\\\n";}
21
+ # end
22
+ end
23
+ context "string interpolation" do
24
+ it "should parse simple interpolation" do
25
+ compile('"a #{b}"').should == '"a " . $b;'
26
+ compile('"a #{b c}"').should == '"a " . b($c);'
27
+ end
28
+
29
+ it "should parse complex interpolation" do
30
+ compile('"a #{b} #{foo("bar", :baz)} "').should ==
31
+ '"a " . $b . " " . foo("bar", "baz") . " ";'
32
+ end
33
+
34
+ it "should handle empty \#{} properly" do
35
+ compile('"a #{b} #{} "').should == '"a " . $b . " " . " ";'
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, 'ternary' do
4
+ it "should parse simple ternary operations" do
5
+ compile("a ? b : c").should == "$a ? $b : $c;"
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ describe Phaad::Generator, 'Unary' do
2
+ it "should parse +" do
3
+ compile("+a").should == "+$a;"
4
+ end
5
+ it "should parse -" do
6
+ compile("-a").should == "-$a;"
7
+ end
8
+ it "should parse ~" do
9
+ compile("~a").should == "~$a;"
10
+ end
11
+ it "should parse !" do
12
+ compile("!a").should == "!$a;"
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, "while" do
4
+ it "should parse while statements" do
5
+ compile("while a\nb\nend").should == "while($a) {\n $b;\n}"
6
+ end
7
+
8
+ it "should parse one line while statements" do
9
+ compile("b while a").should == "while($a) {\n $b;\n}"
10
+ end
11
+
12
+ it "should parse until statements" do
13
+ compile("until a\nb\nend").should == "while(!($a)) {\n $b;\n}"
14
+ compile("until a + b\nc\nend").should == "while(!($a + $b)) {\n $c;\n}"
15
+ end
16
+
17
+ it "should parse one line until statements" do
18
+ compile("b until a").should == "while(!($a)) {\n $b;\n}"
19
+ end
20
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe Phaad::Generator, "Whitespace" do
4
+ it "should correctly parse this huge program" do
5
+ input = <<EOT
6
+ def complex_hello_world(name = "Foo Bar", greeting = "Howdy")
7
+ print_greeting greeting
8
+ print_name name
9
+ echo "Howdy is cooler!" if greeting != "Howdy"
10
+ echo "Impossible" unless 1 == 1
11
+ end
12
+
13
+ def print_name(name)
14
+ if name == "Foo Bar"
15
+ i = 0
16
+ while i < 10
17
+ echo "Your name is not 'Foo Bar'"
18
+ i = i + 1
19
+ end
20
+ elsif name == "1337"
21
+ echo "l33t" while true
22
+ else
23
+ echo name
24
+ end
25
+ end
26
+
27
+ def print_greeting(greeting)
28
+ if greeting == "Howdy"
29
+ complex_hello_world "Foo Bar", "Bug"
30
+ echo greeting until true
31
+ else
32
+ echo greeting
33
+ end
34
+ end
35
+
36
+ complex_hello_world "Utkarsh"
37
+ EOT
38
+
39
+ expected_output = <<EOT
40
+ function complex_hello_world($name = "Foo Bar", $greeting = "Howdy") {
41
+ print_greeting($greeting);
42
+ print_name($name);
43
+ if($greeting != "Howdy") {
44
+ echo "Howdy is cooler!";
45
+ }
46
+ if(!(1 == 1)) {
47
+ echo "Impossible";
48
+ }
49
+ }
50
+ function print_name($name) {
51
+ if($name == "Foo Bar") {
52
+ $i = 0;
53
+ while($i < 10) {
54
+ echo "Your name is not 'Foo Bar'";
55
+ $i = $i + 1;
56
+ }
57
+ } elseif($name == "1337") {
58
+ while(TRUE) {
59
+ echo "l33t";
60
+ }
61
+ } else {
62
+ echo $name;
63
+ }
64
+ }
65
+ function print_greeting($greeting) {
66
+ if($greeting == "Howdy") {
67
+ complex_hello_world("Foo Bar", "Bug");
68
+ while(!(TRUE)) {
69
+ echo $greeting;
70
+ }
71
+ } else {
72
+ echo $greeting;
73
+ }
74
+ }
75
+ complex_hello_world("Utkarsh");
76
+ EOT
77
+
78
+ compile(input).should == expected_output.chomp
79
+ end
80
+
81
+ it "should parse this class program" do
82
+ input = <<EOT
83
+ class Foo
84
+ def bar
85
+ @came = 1
86
+ return true
87
+ end
88
+ end
89
+ EOT
90
+
91
+ expected_output = <<EOT
92
+ class Foo {
93
+ function bar() {
94
+ $this->came = 1;
95
+ return TRUE;
96
+ }
97
+ }
98
+ EOT
99
+ compile(input).should == expected_output.chomp
100
+ end
101
+ end
data/spec/phaad_spec.rb CHANGED
@@ -4,4 +4,8 @@ describe Phaad do
4
4
  it "should have a version" do
5
5
  Phaad::VERSION.should be_a(String)
6
6
  end
7
+
8
+ it "should parse a blank string" do
9
+ compile("").should == ""
10
+ end
7
11
  end
data/spec/spec_helper.rb CHANGED
@@ -5,4 +5,10 @@ RSpec.configure do |c|
5
5
  def compile(string)
6
6
  Phaad::Generator.new(string).emitted
7
7
  end
8
+
9
+ RSpec::Matchers.define :start_with do |substring|
10
+ match do |string|
11
+ string.start_with?(substring)
12
+ end
13
+ end
8
14
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phaad
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,33 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-05-26 00:00:00.000000000Z
12
+ date: 2011-06-01 00:00:00.000000000Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: slop
16
+ requirement: &75976660 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *75976660
25
+ - !ruby/object:Gem::Dependency
26
+ name: fssm
27
+ requirement: &76004900 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.2.7
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *76004900
14
36
  - !ruby/object:Gem::Dependency
15
37
  name: rspec
16
- requirement: &78725830 !ruby/object:Gem::Requirement
38
+ requirement: &76018460 !ruby/object:Gem::Requirement
17
39
  none: false
18
40
  requirements:
19
41
  - - ~>
@@ -21,7 +43,7 @@ dependencies:
21
43
  version: 2.6.0
22
44
  type: :development
23
45
  prerelease: false
24
- version_requirements: *78725830
46
+ version_requirements: *76018460
25
47
  description: A beautiful way to write PHP
26
48
  email:
27
49
  - utkarshkukreti@gmail.com
@@ -33,6 +55,7 @@ files:
33
55
  - .gitignore
34
56
  - .rspec
35
57
  - Gemfile
58
+ - README.md
36
59
  - Rakefile
37
60
  - bin/phaad
38
61
  - lib/phaad.rb
@@ -40,10 +63,27 @@ files:
40
63
  - lib/phaad/generator.rb
41
64
  - lib/phaad/version.rb
42
65
  - phaad.gemspec
66
+ - spec/generator/array_spec.rb
67
+ - spec/generator/assign_spec.rb
68
+ - spec/generator/binary_spec.rb
69
+ - spec/generator/class_spec.rb
70
+ - spec/generator/constants_spec.rb
71
+ - spec/generator/for_spec.rb
72
+ - spec/generator/function_spec.rb
73
+ - spec/generator/if_spec.rb
74
+ - spec/generator/ivar_spec.rb
43
75
  - spec/generator/literal_spec.rb
76
+ - spec/generator/massign_spec.rb
77
+ - spec/generator/paren_spec.rb
78
+ - spec/generator/return_spec.rb
79
+ - spec/generator/string_spec.rb
80
+ - spec/generator/ternary_spec.rb
81
+ - spec/generator/unary_spec.rb
82
+ - spec/generator/while_spec.rb
83
+ - spec/generator/whitespace_spec.rb
44
84
  - spec/phaad_spec.rb
45
85
  - spec/spec_helper.rb
46
- homepage: ''
86
+ homepage: https://github.com/utkarshkukreti/phaad/
47
87
  licenses: []
48
88
  post_install_message:
49
89
  rdoc_options: []