phaad 0.0.1 → 0.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.
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: []