mulang-php 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.
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ [![Build Status](https://travis-ci.org/mumuki/mulang-php.svg?branch=master)](https://travis-ci.org/mumuki/mulang-php)
2
+ [![Code Climate](https://codeclimate.com/github/mumuki/mulang-php/badges/gpa.svg)](https://codeclimate.com/github/mumuki/mulang-php)
3
+ [![Test Coverage](https://codeclimate.com/github/mumuki/mulang-php/badges/coverage.svg)](https://codeclimate.com/github/mumuki/mulang-php)
4
+
5
+ # Mulang::PHP
6
+
7
+ ## Contributing
8
+
9
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mumuki/mulang-php. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
10
+
11
+
12
+ ## License
13
+
14
+ The gem is available as open source under the terms of the [GPLv3](http://opensource.org/licenses/gpl-license).
15
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/.gitkeep ADDED
File without changes
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mulang/php"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ #require "irb"
14
+ #IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/mulang/php.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'ast'
2
+ require 'parser/current'
3
+ require 'mumukit/core'
4
+
5
+ require_relative "./php/version"
6
+
7
+ module Mulang
8
+ module PHP
9
+ def self.parse(php_ast)
10
+ Mulang::PHP::AstProcessor.new.process_block php_ast
11
+ end
12
+
13
+ def self.language
14
+ Mumukit::Language::External.new { |it| parse(it) }
15
+ end
16
+ end
17
+ end
18
+
19
+ require_relative "./php/sexp"
20
+ require_relative './php/ast_processor'
@@ -0,0 +1,271 @@
1
+ module Mulang::PHP
2
+ class AstProcessor
3
+ include AST::Sexp
4
+ include Mulang::PHP::Sexp
5
+
6
+ def initialize
7
+ define_binary_operators!
8
+ end
9
+
10
+ def process_block(stmts)
11
+ sequence *process(stmts)
12
+ end
13
+
14
+ def process(node)
15
+ return ms(:None) if node.nil?
16
+
17
+ if node.is_a?(Array)
18
+ return node.map { |it| process it }
19
+ end
20
+
21
+ if node.is_a?(Hash)
22
+ return send "on_#{node[:nodeType]}", node
23
+ end
24
+
25
+ ms(:Other)
26
+ end
27
+
28
+ def process_array(array, &f)
29
+ array.map f
30
+ end
31
+
32
+ def get_name(node)
33
+ node[:parts].first
34
+ end
35
+
36
+ def define_binary_operators!
37
+ [
38
+ { token: '===', name: 'Identical', supports_assign?: false },
39
+ { token: '!==', name: 'NotIdentical', supports_assign?: false },
40
+ { token: '.', name: 'Concat', supports_assign?: true },
41
+ { token: '+', name: 'Plus', supports_assign?: true },
42
+ { token: '-', name: 'Minus', supports_assign?: true },
43
+ { token: '*', name: 'Mul', supports_assign?: true },
44
+ { token: '/', name: 'Div', supports_assign?: true },
45
+ { token: '%', name: 'Mod', supports_assign?: true },
46
+ { token: '**', name: 'Pow', supports_assign?: true },
47
+ { token: '>', name: 'Greater', supports_assign?: false },
48
+ { token: '<', name: 'Smaller', supports_assign?: false },
49
+ { token: '>=', name: 'GreaterOrEqual', supports_assign?: false },
50
+ { token: '<=', name: 'SmallerOrEqual', supports_assign?: false },
51
+ { token: '&', name: 'BitwiseAnd', supports_assign?: true },
52
+ { token: '|', name: 'BitwiseOr', supports_assign?: true },
53
+ { token: '&&', name: 'BooleanAnd', supports_assign?: false },
54
+ { token: '||', name: 'BooleanOr', supports_assign?: false },
55
+ { token: 'and', name: 'LogicalAnd', supports_assign?: false },
56
+ { token: 'or', name: 'LogicalOr', supports_assign?: false },
57
+ { token: 'xor', name: 'LogicalXOr', supports_assign?: false }
58
+ ].each { |it|
59
+ self.class.redefine_method(:"on_Expr_BinaryOp_#{it[:name]}") { |node|
60
+ process_binary_operator it[:token], node
61
+ }
62
+
63
+ if it[:supports_assign?]
64
+ self.class.redefine_method(:"on_Expr_AssignOp_#{it[:name]}") { |node|
65
+ binary_operator "#{it[:token]}=", process(node[:var]), process(node[:expr])
66
+ }
67
+ end
68
+ }
69
+ end
70
+
71
+ def process_binary_operator(operator, node)
72
+ binary_operator operator, process(node[:left]), process(node[:right])
73
+ end
74
+
75
+ # ---
76
+
77
+ # VALUES
78
+
79
+ def on_Stmt_Expression(node)
80
+ process node[:expr]
81
+ end
82
+
83
+ def on_Expr_Variable(node)
84
+ ms :Reference, node[:name]
85
+ end
86
+
87
+ def on_Scalar_DNumber(node)
88
+ ms :MuNumber, node[:value]
89
+ end
90
+ alias on_Scalar_LNumber on_Scalar_DNumber
91
+
92
+ def on_Scalar_String(node)
93
+ ms :MuString, node[:value]
94
+ end
95
+
96
+ def on_Expr_ConstFetch(node)
97
+ value = get_name(node[:name]).downcase
98
+
99
+ case value
100
+ when 'true'
101
+ ms :MuBool, true
102
+ when 'false'
103
+ ms :MuBool, false
104
+ when 'null'
105
+ ms :None
106
+ else
107
+ ms :Reference, value
108
+ end
109
+ end
110
+
111
+ def on_Expr_Array(node)
112
+ items = node[:items]
113
+ is_array = items.all? { |it| it[:key].nil? }
114
+
115
+ is_array ? ms(:MuList, process(items))
116
+ : ms(:MuObject, process_block(items))
117
+ end
118
+
119
+ def on_Expr_ArrayItem(node)
120
+ value = process(node[:value])
121
+
122
+ node[:key] ? ms(:Attribute, node[:key][:value].to_s, value)
123
+ : value
124
+ end
125
+
126
+ def on_Expr_Closure(node)
127
+ ms :Lambda, process(node[:params]), process_block(node[:stmts])
128
+ end
129
+
130
+ def on_Param(node)
131
+ ms :VariablePattern, node[:var][:name]
132
+ end
133
+
134
+ # OPERATORS
135
+
136
+ def on_Expr_BinaryOp_Equal(node)
137
+ ms :Equal, [process(node[:left]), process(node[:right])]
138
+ end
139
+
140
+ def on_Expr_BinaryOp_NotEqual(node)
141
+ ms :NotEqual, [process(node[:left]), process(node[:right])]
142
+ end
143
+
144
+ def on_Expr_PostInc(node)
145
+ binary_operator '+', process(node[:var]), ms(:MuNumber, 1)
146
+ end
147
+ alias on_Expr_PreInc on_Expr_PostInc
148
+
149
+ def on_Expr_PostDec(node)
150
+ binary_operator '-', process(node[:var]), ms(:MuNumber, 1)
151
+ end
152
+ alias on_Expr_PreDec on_Expr_PostDec
153
+
154
+ # FUNCTION CALLS
155
+
156
+ def on_Expr_FuncCall(node)
157
+ application get_name(node[:name]), process(node[:args])
158
+ end
159
+
160
+ def on_Arg(node)
161
+ process node[:value]
162
+ end
163
+
164
+ # DECLARATIONS
165
+
166
+ def on_Stmt_Function(node)
167
+ simple_function node[:name][:name], process(node[:params]), process_block(node[:stmts])
168
+ end
169
+
170
+ def on_Stmt_Return(node)
171
+ ms :Return, process(node[:expr])
172
+ end
173
+
174
+ # STATEMENTS
175
+
176
+ def on_Expr_Assign(node)
177
+ left = node[:var]
178
+ exp = process(node[:expr])
179
+
180
+ if left[:nodeType] == 'Expr_PropertyFetch'
181
+ simple_send process(left[:var]), "#{left[:name][:name]}=", [exp]
182
+ else
183
+ ms :Assignment, left[:name], exp
184
+ end
185
+ end
186
+
187
+ def on_Stmt_Echo(node)
188
+ ms :Print, process_block(node[:exprs])
189
+ end
190
+
191
+ def on_Stmt_If(node)
192
+ condition = node[:cond]
193
+ body = node[:stmts]
194
+ else_block = node[:else]
195
+
196
+ ms :If, process(condition), process_block(body), process(else_block)
197
+ end
198
+ alias on_Stmt_ElseIf on_Stmt_If
199
+
200
+ def on_Stmt_Else(node)
201
+ process_block node[:stmts]
202
+ end
203
+
204
+ def on_Stmt_For(node)
205
+ ms :ForLoop, process_block(node[:init]), process_block(node[:cond]), process_block(node[:loop]), process_block(node[:stmts])
206
+ end
207
+
208
+ def on_Stmt_While(node)
209
+ ms :While, process(node[:cond]), process_block(node[:stmts])
210
+ end
211
+
212
+ def on_Stmt_Foreach(node)
213
+ ms :For, [ms(:Generator, ms(:VariablePattern, node[:valueVar][:name]), process(node[:expr]))], process_block(node[:stmts])
214
+ end
215
+
216
+ # OOP
217
+
218
+ def on_Expr_PropertyFetch(node)
219
+ simple_send process(node[:var]), node[:name][:name], []
220
+ end
221
+
222
+ def on_Expr_MethodCall(node)
223
+ simple_send process(node[:var]), node[:name][:name], process(node[:args])
224
+ end
225
+
226
+ def on_Expr_New(node)
227
+ ms :New, process(node[:class]), process(node[:args])
228
+ end
229
+
230
+ def on_Name(node)
231
+ ms :Reference, get_name(node)
232
+ end
233
+
234
+ def on_Stmt_Class(node)
235
+ superclass = node.dig(:extends, :parts)&.first
236
+ interfaces = node[:implements].map { |it| { nodeType: 'Stmt_Implement', name: get_name(it) } }
237
+
238
+ ms :Class, node[:name][:name], superclass, process_block(interfaces.concat(node[:stmts]))
239
+ end
240
+
241
+ def on_Stmt_Implement(node)
242
+ ms :Implement, ms(:Reference, node[:name])
243
+ end
244
+
245
+ def on_Stmt_Property(node)
246
+ prop = node[:props].first
247
+ ms :Attribute, prop[:name][:name], process(prop[:default])
248
+ end
249
+
250
+ def on_Stmt_ClassMethod(node)
251
+ name = node[:name][:name]
252
+ params = process(node[:params])
253
+
254
+ if node[:stmts].nil?
255
+ ms :TypeSignature, name, ms(:ParameterizedType, params.map { |it| 'Any' }, 'Any', [])
256
+ else
257
+ simple_method name, params, process_block(node[:stmts])
258
+ end
259
+ end
260
+
261
+ def on_Stmt_Interface(node)
262
+ parents = node[:extends].map { |it| get_name(it) }
263
+ ms :Interface, node[:name][:name], parents, process_block(node[:stmts])
264
+ end
265
+
266
+ def method_missing(m, *args, &block)
267
+ puts m, args
268
+ ms :Other, "#{m}: #{args.to_s}", nil
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,55 @@
1
+ module Mulang::PHP
2
+ module Sexp
3
+ def sequence(*contents)
4
+ if contents.empty?
5
+ ms(:None)
6
+ elsif contents.size == 1
7
+ contents[0]
8
+ else
9
+ ms(:Sequence, *contents)
10
+ end
11
+ end
12
+
13
+ def ms(tag, *contents)
14
+ if contents.empty?
15
+ {tag: tag}
16
+ elsif contents.size == 1
17
+ {tag: tag, contents: contents.first}
18
+ else
19
+ {tag: tag, contents: contents}
20
+ end
21
+ end
22
+
23
+ def simple_function(name, args, body)
24
+ callable :Function, name, args, body
25
+ end
26
+
27
+ def simple_method(name, args, body)
28
+ callable :Method, name, args, body
29
+ end
30
+
31
+ def callable(type, name, args, body)
32
+ {
33
+ tag: type,
34
+ contents: [
35
+ name,
36
+ [
37
+ [ args, {tag: :UnguardedBody, contents: body }]
38
+ ]
39
+ ]
40
+ }
41
+ end
42
+
43
+ def application(name, args)
44
+ ms :Application, [ms(:Reference, name), args]
45
+ end
46
+
47
+ def binary_operator(operator, left, right)
48
+ application operator, [left, right]
49
+ end
50
+
51
+ def simple_send(sender, message, args)
52
+ ms(:Send, sender, ms(:Reference, message), args)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ module Mulang
2
+ module PHP
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mulang/php/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mulang-php"
8
+ spec.version = Mulang::PHP::VERSION
9
+ spec.authors = ["Rodrigo Alfonso", "Franco Leonardo Bulgarelli"]
10
+ spec.email = ["rodri042@gmail.com", "franco@mumuki.org"]
11
+
12
+ spec.summary = "PHP integration for the Mulang Language Analyzer"
13
+ spec.description = "PHP - Mulang Parser"
14
+ spec.homepage = "https://mumuki.io"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "pry"
28
+
29
+ spec.add_dependency "parser"
30
+ spec.add_dependency "ast"
31
+ spec.add_dependency "mumukit-core", "~> 1.0"
32
+ spec.add_dependency "mulang", "~> 4.0"
33
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mulang-php
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rodrigo Alfonso
8
+ - Franco Leonardo Bulgarelli
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2018-08-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.14'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.14'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: parser
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: ast
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: mumukit-core
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '1.0'
105
+ type: :runtime
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '1.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: mulang
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '4.0'
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '4.0'
126
+ description: PHP - Mulang Parser
127
+ email:
128
+ - rodri042@gmail.com
129
+ - franco@mumuki.org
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".rspec"
136
+ - ".ruby-version"
137
+ - ".travis.yml"
138
+ - CODE_OF_CONDUCT.md
139
+ - COPYRIGHT.txt
140
+ - Gemfile
141
+ - LICENSE.txt
142
+ - README.md
143
+ - Rakefile
144
+ - bin/.gitkeep
145
+ - bin/console
146
+ - bin/setup
147
+ - lib/mulang/php.rb
148
+ - lib/mulang/php/ast_processor.rb
149
+ - lib/mulang/php/sexp.rb
150
+ - lib/mulang/php/version.rb
151
+ - mulang-php.gemspec
152
+ homepage: https://mumuki.io
153
+ licenses:
154
+ - MIT
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.5.1
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: PHP integration for the Mulang Language Analyzer
176
+ test_files: []