linguify 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +45 -0
- data/README.md +167 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/linguify/linguified.rb +116 -0
- data/lib/linguify/proc.rb +26 -0
- data/lib/linguify/reduction.rb +153 -0
- data/lib/linguify/sexp.rb +60 -0
- data/lib/linguify/string.rb +9 -0
- data/lib/linguify/translators/javascript.rb +513 -0
- data/lib/linguify.rb +34 -0
- data/spec/linguify/linguify_spec.rb +77 -0
- data/tasks/spec.rake +8 -0
- metadata +136 -0
data/.document
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
file-tail (1.0.7)
|
6
|
+
tins (~> 0.3)
|
7
|
+
git (1.2.5)
|
8
|
+
jeweler (1.6.4)
|
9
|
+
bundler (~> 1.0)
|
10
|
+
git (>= 1.2.5)
|
11
|
+
rake
|
12
|
+
rake (0.9.2)
|
13
|
+
rcov (0.9.11)
|
14
|
+
rspec (2.6.0)
|
15
|
+
rspec-core (~> 2.6.0)
|
16
|
+
rspec-expectations (~> 2.6.0)
|
17
|
+
rspec-mocks (~> 2.6.0)
|
18
|
+
rspec-core (2.6.4)
|
19
|
+
rspec-expectations (2.6.0)
|
20
|
+
diff-lcs (~> 1.1.2)
|
21
|
+
rspec-mocks (2.6.0)
|
22
|
+
ruby2ruby (1.3.1)
|
23
|
+
ruby_parser (~> 2.0)
|
24
|
+
sexp_processor (~> 3.0)
|
25
|
+
ruby_parser (2.3.1)
|
26
|
+
sexp_processor (~> 3.0)
|
27
|
+
sexp_processor (3.0.7)
|
28
|
+
shoulda (2.11.3)
|
29
|
+
sourcify (0.5.0)
|
30
|
+
file-tail (>= 1.0.5)
|
31
|
+
ruby2ruby (>= 1.2.5)
|
32
|
+
ruby_parser (>= 2.0.5)
|
33
|
+
sexp_processor (>= 3.0.5)
|
34
|
+
tins (0.3.1)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler (~> 1.0.0)
|
41
|
+
jeweler (~> 1.6.4)
|
42
|
+
rcov
|
43
|
+
rspec
|
44
|
+
shoulda
|
45
|
+
sourcify
|
data/README.md
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# Linguify
|
2
|
+
|
3
|
+
Linguify is a linguistic compiler allowing you to compile and execute plain english.
|
4
|
+
And thus allows you to program in plain english provided you have the reduction rules needed.
|
5
|
+
|
6
|
+
Since the code ends up compiled like all code should be you can execute your code so amazingly fast I am in awe just to be able to write this divine text to you about it.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
gem install linguify
|
11
|
+
|
12
|
+
## Basic usage
|
13
|
+
|
14
|
+
require 'linguify'
|
15
|
+
|
16
|
+
reduce /all directories/ => 'directories' do
|
17
|
+
Dir.entries('.').select{ |f| f[0] != '.' && File.directory?(f) }
|
18
|
+
end
|
19
|
+
|
20
|
+
reduce /({directories:[^}]*}) recursively/ => 'directories' do |dirs|
|
21
|
+
all_dirs = dirs
|
22
|
+
Find.find(dirs) do |path|
|
23
|
+
if FileTest.directory?(path)
|
24
|
+
if File.basename(path)[0] == '.'
|
25
|
+
Find.prune # Don't look any further into this directory.
|
26
|
+
else
|
27
|
+
all_dirs << path
|
28
|
+
next
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
all_dirs
|
33
|
+
end
|
34
|
+
|
35
|
+
reduce /all files inside ({directories:[^}]*})/ => 'files' do |dirs|
|
36
|
+
dirs.map{ |f| File.new(f, "r") }
|
37
|
+
end
|
38
|
+
|
39
|
+
reduce /view ({files:[^}]*})/ => '' do |files|
|
40
|
+
files.each do |file|
|
41
|
+
pp file
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
"view all files inside all directories recursively".linguify.to_ruby
|
46
|
+
# => "code = lambda do
|
47
|
+
# directories_0 = Dir.entries(\".\").select { |f| ((not (f[0] == \".\")) and File.directory?(f)) }
|
48
|
+
# directories_1 = (all_dirs = directories_0
|
49
|
+
# Find.find(directories_0) do |path|
|
50
|
+
# if FileTest.directory?(path) then
|
51
|
+
# if (File.basename(path)[0] == \".\") then
|
52
|
+
# Find.prune
|
53
|
+
# else
|
54
|
+
# (all_dirs << path)
|
55
|
+
# next
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
# all_dirs)
|
60
|
+
# files_2 = directories_1.map { |f| File.new(f, \"r\") }
|
61
|
+
# files_2.each { |file| pp(file) }
|
62
|
+
# end
|
63
|
+
# "
|
64
|
+
|
65
|
+
And if you simply want to execute your magnificent piece of art:
|
66
|
+
|
67
|
+
"view all files inside all directories recursively".linguify.run
|
68
|
+
|
69
|
+
Or even:
|
70
|
+
|
71
|
+
# compile once, run plenty
|
72
|
+
code = "view all files inside all directories recursively".linguify
|
73
|
+
loop do
|
74
|
+
code.run
|
75
|
+
end
|
76
|
+
|
77
|
+
## More advanced usage
|
78
|
+
|
79
|
+
Linguify supports mixing javascript and ruby.
|
80
|
+
A typical case would be to express NOSQL queries in plain English for everyones convenience.
|
81
|
+
|
82
|
+
require 'linguify'
|
83
|
+
|
84
|
+
reduce /a possible javascript NOSQL query/ => {:to => 'query', :lang => :js} do
|
85
|
+
@db.forEach(lambda{ |record|
|
86
|
+
emit(record);
|
87
|
+
}
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
reduce /execute ({query:[^}]*})/ => '' do |query|
|
92
|
+
db.map query
|
93
|
+
end
|
94
|
+
|
95
|
+
"execute a possible javascript NOSQL query".linguify.to_ruby
|
96
|
+
# => "code = lambda do
|
97
|
+
# query = \"function(){\\n this.db.forEach(function(record){\\n emit(record)\\n });\\n}\"
|
98
|
+
# db.map(query)
|
99
|
+
# end
|
100
|
+
# "
|
101
|
+
|
102
|
+
The nature of Linguify's expression reduction face pragmatic programmers with a urge to inline the code the arguments represents.
|
103
|
+
Luckily Linguify has evolved to embrace such minds. Linguify is not for the general masses. It is for the mighty few pragmatics.
|
104
|
+
|
105
|
+
require 'linguify'
|
106
|
+
|
107
|
+
reduce /inlined code/ => {:to => 'code', :lang => :ruby, :inline => true} do
|
108
|
+
something.each do |foobar| # life is not worth living without psedo foobars
|
109
|
+
pp foobar
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
reduce /execute ({code:[^}]*})/ => '' do |code|
|
114
|
+
pp "hey mum"
|
115
|
+
code
|
116
|
+
pp "you will never know what I just did"
|
117
|
+
end
|
118
|
+
|
119
|
+
"execute inlined code".linguify.to_ruby
|
120
|
+
# => "code = lambda do
|
121
|
+
# (pp(\"hey mum\")
|
122
|
+
# (something.each { |foobar| pp(foobar) })
|
123
|
+
# pp(\"you will never know what I just did\"))
|
124
|
+
# end
|
125
|
+
# "
|
126
|
+
|
127
|
+
And you can even inline sub-expressions:
|
128
|
+
|
129
|
+
require 'linguify'
|
130
|
+
|
131
|
+
reduce /sub expression/ => {:to => 'sub_expression', :lang => :ruby, :inline => true} do
|
132
|
+
pp "this is the sub expression code"
|
133
|
+
end
|
134
|
+
|
135
|
+
reduce /({sub_expression:[^}]*}) of inlined code/ => {:to => 'code', :lang => :ruby, :inline => true} do |sub|
|
136
|
+
something.each do |foobar| # life is not worth living without psedo foobars
|
137
|
+
pp foobar
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
reduce /execute ({code:[^}]*})/ => '' do |code|
|
142
|
+
pp "hey mum"
|
143
|
+
code
|
144
|
+
code[:sub]
|
145
|
+
pp "you will never know what I just did"
|
146
|
+
end
|
147
|
+
|
148
|
+
"execute sub expression of inlined code".linguify.to_ruby
|
149
|
+
# => "code = lambda do
|
150
|
+
# (pp(\"hey mum\")
|
151
|
+
# (something.each { |foobar| pp(foobar) })
|
152
|
+
# pp(\"this is the sub expression code\")
|
153
|
+
# pp(\"you will never know what I just did\"))
|
154
|
+
# end
|
155
|
+
# "
|
156
|
+
|
157
|
+
## License
|
158
|
+
|
159
|
+
(The MIT License)
|
160
|
+
|
161
|
+
Copyright (c) 2011 Patrick Hanevold
|
162
|
+
|
163
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
164
|
+
|
165
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
166
|
+
|
167
|
+
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
gem.name = "linguify"
|
17
|
+
gem.homepage = "http://github.com/patrickhno/linguify"
|
18
|
+
gem.license = "MIT"
|
19
|
+
gem.summary = %Q{Linguify, the linguistic compiler.}
|
20
|
+
gem.description = %Q{Linguify is a linguistic compiler allowing you to compile and execute plain english.}
|
21
|
+
gem.email = "patrick.hanevold@gmail.com"
|
22
|
+
gem.authors = ["Patrick Hanevold"]
|
23
|
+
# dependencies defined in Gemfile
|
24
|
+
end
|
25
|
+
Jeweler::RubygemsDotOrgTasks.new
|
26
|
+
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
test.rcov_opts << '--exclude "gems/*"'
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "linguify #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
46
|
+
|
47
|
+
Dir['tasks/*.rake'].sort.each { |f| load f }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'sourcify'
|
4
|
+
require 'linguify/translators/javascript'
|
5
|
+
|
6
|
+
module Linguify
|
7
|
+
|
8
|
+
class Linguified
|
9
|
+
|
10
|
+
attr_accessor :proc, :sentence
|
11
|
+
|
12
|
+
# Lingquify a sentence
|
13
|
+
#
|
14
|
+
# * +str+ - A plain English string, or a plain English string with reductions in it.
|
15
|
+
# * +replace+ - In which "scope" to bind the code during compilation
|
16
|
+
#
|
17
|
+
def initialize str,bind
|
18
|
+
|
19
|
+
#
|
20
|
+
# reduction loop
|
21
|
+
#
|
22
|
+
@sentence = str.dup
|
23
|
+
@bind = bind
|
24
|
+
loop do
|
25
|
+
rule = find_rule(str)
|
26
|
+
|
27
|
+
reduction = rule[:proc].to_reduction :returns => rule[:result],
|
28
|
+
:lang => rule[:lang],
|
29
|
+
:inline => rule[:inline],
|
30
|
+
:regexp => rule[:match].inspect,
|
31
|
+
:args => rule[:match].match(str).to_a[1..-1]
|
32
|
+
|
33
|
+
str.gsub!(rule[:match],reduction.to_rexp)
|
34
|
+
break if /^{.*}$/ =~ str
|
35
|
+
end
|
36
|
+
|
37
|
+
@encoded = str
|
38
|
+
|
39
|
+
@merged_code = []
|
40
|
+
if /^{(?<code>.*)}$/ =~ @encoded
|
41
|
+
# successfully reduced entire string, compile it
|
42
|
+
code = Reduction::parse(code).compile
|
43
|
+
|
44
|
+
# and wrap it up
|
45
|
+
@sexy = Sexp.new(:block,
|
46
|
+
Sexp.new(:lasgn,:code, Sexp.new(:iter,
|
47
|
+
Sexp.new(:call, nil, :lambda, Sexp.new(:arglist)), nil,
|
48
|
+
Sexp.new(:block,
|
49
|
+
*code
|
50
|
+
)
|
51
|
+
)
|
52
|
+
),
|
53
|
+
)
|
54
|
+
|
55
|
+
@@me = self
|
56
|
+
eval to_ruby(
|
57
|
+
Sexp.new(:call,
|
58
|
+
Sexp.new(:colon2, Sexp.new(:const, :Linguify), :Linguified), :trampoline, Sexp.new(:arglist, Sexp.new(:lvar, :code))
|
59
|
+
)
|
60
|
+
),bind
|
61
|
+
raise "hell" unless @proc
|
62
|
+
else
|
63
|
+
raise "hell"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Find a reduction rule for the string
|
68
|
+
#
|
69
|
+
# * +str+ - A plain English string, or a plain English string with reductions in it.
|
70
|
+
#
|
71
|
+
def find_rule str
|
72
|
+
found = Linguify.rules.select do |rule|
|
73
|
+
if rule[:match] =~ str
|
74
|
+
true
|
75
|
+
else
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
raise "no step definition for #{str}" if found.size == 0
|
80
|
+
|
81
|
+
found[0]
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_sexp
|
85
|
+
@sexy
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_ruby additional=nil
|
89
|
+
clone = Marshal.load(Marshal.dump(@sexy)) # sexy is not cleanly duplicated
|
90
|
+
clone << additional if additional
|
91
|
+
Ruby2Ruby.new.process(clone)
|
92
|
+
end
|
93
|
+
|
94
|
+
def run
|
95
|
+
begin
|
96
|
+
@proc.call
|
97
|
+
rescue Exception => e
|
98
|
+
$stderr.puts e
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def register_code code
|
103
|
+
@proc = code
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.trampoline code
|
107
|
+
@@me.register_code code
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.cache
|
111
|
+
@@cache ||= {}
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Proc
|
4
|
+
def to_reduction args={}
|
5
|
+
Linguify::Reduction.new(
|
6
|
+
:returns => args[:returns] || '',
|
7
|
+
:lang => args[:lang] || :ruby,
|
8
|
+
:inline => args[:inline] || false,
|
9
|
+
:location => source_location[0],
|
10
|
+
:line => source_location[1],
|
11
|
+
:regexp => args[:regexp] || //.inspect,
|
12
|
+
:args => args[:args] || [],
|
13
|
+
:sexp => self.to_sexp
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_code collection
|
18
|
+
reduction = to_reduction
|
19
|
+
|
20
|
+
sexy = reduction.compile
|
21
|
+
code = Marshal.load(Marshal.dump(sexy.first)) # sexy is not cleanly duplicated
|
22
|
+
code.replace_variable_references!(Linguify::Replacement.new(:sexp => collection.name),:collection)
|
23
|
+
code
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Linguify
|
4
|
+
|
5
|
+
class Replacement
|
6
|
+
attr_reader :sexp
|
7
|
+
|
8
|
+
def initialize args
|
9
|
+
@sexp = args[:sexp]
|
10
|
+
@inline = args[:inline] || false
|
11
|
+
end
|
12
|
+
|
13
|
+
def inline?
|
14
|
+
@inline
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Reduction
|
19
|
+
|
20
|
+
attr_accessor :returns, :location, :line, :regexp, :args, :sexp, :rule_args, :from, :reduction_id, :lang, :inline, :named_args
|
21
|
+
|
22
|
+
@@reductions = []
|
23
|
+
|
24
|
+
def initialize params
|
25
|
+
@returns = params[:returns]
|
26
|
+
@lang = params[:lang]
|
27
|
+
@inline = params[:inline]
|
28
|
+
@location = params[:location]
|
29
|
+
@line = params[:line]
|
30
|
+
@regexp = params[:regexp]
|
31
|
+
@rule_args= @regexp.split(')').map{ |sub| sub.split('(')[1] }.select{ |v| v }
|
32
|
+
@args = params[:args]
|
33
|
+
@sexp = params[:sexp]
|
34
|
+
@reduction_id = @@reductions.size
|
35
|
+
determine_arguments
|
36
|
+
@@reductions << self
|
37
|
+
end
|
38
|
+
|
39
|
+
def determine_arguments
|
40
|
+
s = Marshal.load(Marshal.dump(self)) # sexp handling is not clean cut
|
41
|
+
raise "what is this?" unless s.sexp.sexp_type == :iter && s.sexp[1].sexp_type == :call && s.sexp[1][1] == nil && s.sexp[1][2] == :proc && s.sexp[1][3].sexp_type == :arglist
|
42
|
+
|
43
|
+
block_args = s.sexp[2]
|
44
|
+
if block_args
|
45
|
+
if block_args[0]==:lasgn
|
46
|
+
# single argument
|
47
|
+
args = [block_args[1]]
|
48
|
+
elsif block_args[0]==:masgn
|
49
|
+
# multiple arguments
|
50
|
+
args = block_args[1]
|
51
|
+
raise "unsupported argument type #{args}" unless args[0]==:array
|
52
|
+
args = args[1..-1].map{ |arg|
|
53
|
+
raise "unsupported argument type #{arg}" unless arg[0]==:lasgn
|
54
|
+
arg[1]
|
55
|
+
}
|
56
|
+
else
|
57
|
+
raise "unsupported argument type #{args}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# args[] now has the symbolized argument names of the code block
|
61
|
+
@named_args = Hash[*args.zip(@args).flatten] if args
|
62
|
+
@named_args ||= {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.parse str
|
66
|
+
if /^{(?<return>[^:]*):(?<rid>[0-9]+)}$/ =~ str
|
67
|
+
@@reductions[rid.to_i]
|
68
|
+
elsif /^(?<return>[^:]*):(?<rid>[0-9]+)$/ =~ str
|
69
|
+
@@reductions[rid.to_i]
|
70
|
+
else
|
71
|
+
raise "hell #{str}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def compile
|
76
|
+
compile_with_return_to_var
|
77
|
+
end
|
78
|
+
|
79
|
+
# Compile self
|
80
|
+
#
|
81
|
+
# * +return_variable+ - The return variable. Can either be a symbol representing the variable name or nil to skip variable assigning.
|
82
|
+
# * +replace+ - A list of variables in need of a new unique name or replacement with inlined code
|
83
|
+
#
|
84
|
+
def compile_with_return_to_var(return_variable=nil, replace = {})
|
85
|
+
s = Marshal.load(Marshal.dump(self)) # sexp handling is not clean cut
|
86
|
+
args = @named_args.keys
|
87
|
+
# args[] now has the symbolized argument names of the code block
|
88
|
+
|
89
|
+
args_code = []
|
90
|
+
s.args.each_with_index do |arg,i|
|
91
|
+
if /^{(?<ret>[^:]*):(?<n>[0-9]+)}$/ =~ arg
|
92
|
+
# got a argument that referes to a reduction
|
93
|
+
# pseudo allocate a return variable name and compile the reduction
|
94
|
+
red = Reduction::parse(arg)
|
95
|
+
if red.lang != lang && red.lang == :js && lang == :ruby
|
96
|
+
# paste javascript code into a ruby variable
|
97
|
+
code = red.compile_with_return_to_var(nil,replace)
|
98
|
+
clone = Marshal.load(Marshal.dump(code)) # code is not cleanly duplicated
|
99
|
+
code = Sexp.new(:iter,Sexp.new(:call, nil, :lambda, Sexp.new(:arglist)), nil,
|
100
|
+
Sexp.new(:block,
|
101
|
+
*clone
|
102
|
+
)
|
103
|
+
)
|
104
|
+
code = Ruby2Js.new.process(code)
|
105
|
+
code = [Sexp.new(:lasgn, args[i], Sexp.new(:lit, code))]
|
106
|
+
args_code += code
|
107
|
+
else
|
108
|
+
raise "trying to reference #{red.lang} code in #{lang} code" if red.lang != lang
|
109
|
+
if red.inline
|
110
|
+
code = red.compile_with_return_to_var(nil,replace)
|
111
|
+
replace[args[i]] = Replacement.new(:sexp => Sexp.new(:block,*code), :inline => true)
|
112
|
+
else
|
113
|
+
code = red.compile_with_return_to_var("#{ret}_#{n}".to_sym,replace)
|
114
|
+
args_code += code
|
115
|
+
replace[args[i]] = Replacement.new(:sexp => "#{ret}_#{n}".to_sym)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
elsif /^[0-9]+$/ =~ arg
|
119
|
+
# got a number argument, stuff it in a integer variable
|
120
|
+
args_code << Sexp.new(:lasgn, args[i], Sexp.new(:lit, arg.to_i))
|
121
|
+
else
|
122
|
+
raise "hell"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
if return_variable
|
127
|
+
if s.sexp[3][0] == :block
|
128
|
+
code = Sexp.new(:lasgn, return_variable,
|
129
|
+
Sexp.new(:block,
|
130
|
+
*(s.sexp[3][1..-1].map{ |s| s.dup })
|
131
|
+
)
|
132
|
+
)
|
133
|
+
else
|
134
|
+
code = Sexp.new(:lasgn, return_variable, s.sexp[3].dup)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
code = s.sexp[3].dup
|
138
|
+
end
|
139
|
+
|
140
|
+
replace.each do |k,v|
|
141
|
+
code.replace_variable_references!(v,k,@named_args)
|
142
|
+
end
|
143
|
+
|
144
|
+
return *args_code + [code]
|
145
|
+
end
|
146
|
+
|
147
|
+
def to_rexp
|
148
|
+
raise "hell" if returns.kind_of?(Array)
|
149
|
+
"{#{returns}:#{reduction_id}}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Sexp < Array
|
4
|
+
|
5
|
+
# Recurcively replace all references in a code section
|
6
|
+
#
|
7
|
+
# * +code+ - The code haystack to search and replace in
|
8
|
+
# * +replacement+ - The replacement code. Either a Sexp (containing code to inline) or a symbol
|
9
|
+
# * +needle+ - The search needle
|
10
|
+
#
|
11
|
+
def replace_variable_references!(replacement,needle,named_args="")
|
12
|
+
|
13
|
+
case sexp_type
|
14
|
+
when :lasgn
|
15
|
+
self[1]=replacement.sexp if self[1] == needle
|
16
|
+
when :lvar
|
17
|
+
if self[1] == needle
|
18
|
+
unless replacement.inline?
|
19
|
+
self[1]=replacement.sexp
|
20
|
+
end
|
21
|
+
end
|
22
|
+
when :call
|
23
|
+
self[2]=replacement.sexp if self[2] == needle
|
24
|
+
when :lvar
|
25
|
+
self[1]=replacement.sexp if self[1] == needle
|
26
|
+
end
|
27
|
+
# inlining requires complex code:
|
28
|
+
if replacement.inline? && [:iter, :block].include?(sexp_type)
|
29
|
+
# we have a inline and a block, replace any references with the sexp
|
30
|
+
self[1..-1].each_with_index do |h,i|
|
31
|
+
if h && h.kind_of?(Sexp) && h == Sexp.new(:lvar, needle)
|
32
|
+
# inline references like s(:lvar, :needle)
|
33
|
+
# indicates a call to the needle, thus code wants to inline
|
34
|
+
h[0] = replacement.sexp[0]
|
35
|
+
h[1] = replacement.sexp[1]
|
36
|
+
elsif h && h.kind_of?(Sexp) && named_args.has_key?(needle) &&
|
37
|
+
Linguify::Reduction.parse(named_args[needle]).named_args.select{ |k,v|
|
38
|
+
h == Sexp.new(:call, Sexp.new(:lvar, needle), :[], Sexp.new(:arglist, Sexp.new(:lit, k)))
|
39
|
+
}.size == 1
|
40
|
+
# code is asking for a injection of one of the argument's with:
|
41
|
+
# s(:call, s(:lvar, :needle), :[], s(:arglist, s(:lit, :argumen)))
|
42
|
+
# which in ruby looks like:
|
43
|
+
# needle[:argument]
|
44
|
+
# which again is the way we support calling arguments of the neede
|
45
|
+
arg = h[3][1][1]
|
46
|
+
sexy = Marshal.load(Marshal.dump(Linguify::Reduction.parse(Linguify::Reduction.parse(named_args[needle]).named_args[arg]).sexp)) # sexp handling is not clean cut
|
47
|
+
self[i+1] = sexy[3]
|
48
|
+
else
|
49
|
+
h.replace_variable_references!(replacement,needle,named_args) if h && h.kind_of?(Sexp)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
else
|
53
|
+
self[1..-1].each do |h|
|
54
|
+
h.replace_variable_references!(replacement,needle,named_args) if h && h.kind_of?(Sexp)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|