Class: Linguify::Reduction

Inherits:
Object
  • Object
show all
Defined in:
lib/linguify/reduction.rb

Constant Summary collapse

@@reductions =
[]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params) ⇒ Reduction

Returns a new instance of Reduction.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/linguify/reduction.rb', line 45

def initialize params
  @returns  = params[:returns]
  @lang     = params[:lang]
  @inline   = params[:inline]
  @location = params[:location]
  @line     = params[:line]
  @regexp   = params[:regexp]
  @rule_args= @regexp.split(')').map{ |sub| sub.split('(')[1] }.select{ |v| v }
  @args     = params[:args]
  @sexp     = params[:sexp]
  @reduction_id = @@reductions.size
  determine_arguments
  @@reductions << self
end

Instance Attribute Details

#argsObject

Returns the value of attribute args.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def args
  @args
end

#fromObject

Returns the value of attribute from.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def from
  @from
end

#inlineObject

Returns the value of attribute inline.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def inline
  @inline
end

#langObject

Returns the value of attribute lang.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def lang
  @lang
end

#lineObject

Returns the value of attribute line.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def line
  @line
end

#locationObject

Returns the value of attribute location.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def location
  @location
end

#named_argsObject

Returns the value of attribute named_args.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def named_args
  @named_args
end

#reduction_idObject

Returns the value of attribute reduction_id.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def reduction_id
  @reduction_id
end

#regexpObject

Returns the value of attribute regexp.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def regexp
  @regexp
end

#returnsObject

Returns the value of attribute returns.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def returns
  @returns
end

#rule_argsObject

Returns the value of attribute rule_args.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def rule_args
  @rule_args
end

#sexpObject

Returns the value of attribute sexp.



41
42
43
# File 'lib/linguify/reduction.rb', line 41

def sexp
  @sexp
end

Class Method Details

.parse(str) ⇒ Object

Parse the string and return its reduction rule.



97
98
99
100
101
102
103
104
105
# File 'lib/linguify/reduction.rb', line 97

def self.parse str
  if /^{(?<return>[^:]*):(?<rid>[0-9]+)}$/ =~ str
    @@reductions[rid.to_i]
  elsif /^(?<return>[^:]*):(?<rid>[0-9]+)$/ =~ str
    @@reductions[rid.to_i]
  else
    raise "hell #{str}"
  end
end

Instance Method Details

#allocate_variable(name, not_in) ⇒ Object



111
112
113
114
115
116
117
118
# File 'lib/linguify/reduction.rb', line 111

def allocate_variable name,not_in
  n = 0
  begin
    var = "#{name}_#{n}".to_sym
    n += 1
  end while not_in.select{ |sexp| sexp.variable_exists?(var) }.size > 0
  var
end

#compileObject



107
108
109
# File 'lib/linguify/reduction.rb', line 107

def compile
  compile_with_return_to_var
end

#compile_with_return_to_var(params = {}) ⇒ Object

Compile self

Parameters:

  • return_variable (Symbol, nil)

    The variable in the code wanting the result.

  • replacement (Array<Symbol,Sexp>)

    A list of variables in need of a new unique name or replacement with inlined code



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/linguify/reduction.rb', line 126

def compile_with_return_to_var params={}
  replace = params[:replace] || {}

  s = Marshal.load(Marshal.dump(self)) # sexp handling is not clean cut
  args = @named_args.keys
  # args[] now has the symbolized argument names of the code block

  args_code = []
  s.args.each_with_index do |arg,i|
    if /^{(?<ret>[^:]*):(?<n>[0-9]+)}$/ =~ arg
      # got a argument that referes to a reduction
      # pseudo allocate a return variable name and compile the reduction
      red = Reduction::parse(arg)
      if red.lang != lang && red.lang == :js && lang == :ruby
        # paste javascript code into a ruby variable
        code = red.compile_with_return_to_var :replace => replace
        clone = Marshal.load(Marshal.dump(code)) # code is not cleanly duplicated
        code = Sexp.new(:iter,Sexp.new(:call, nil, :lambda, Sexp.new(:arglist)), nil,
          Sexp.new(:block,
            *clone
          )
        )
        code = Ruby2Js.new.process(code)
        code = [Sexp.new(:lasgn, args[i], Sexp.new(:lit, code))]
        args_code += code
      else
        raise "trying to reference #{red.lang} code in #{lang} code" if red.lang != lang
        if red.inline
          code = red.compile_with_return_to_var :replace => replace
          replace[args[i]] = Replacement.new(:sexp => Sexp.new(:block,*code), :inline => true)
        else
          var = allocate_variable(ret,[*args_code,sexp])
          code = red.compile_with_return_to_var :return_variable => var, :replace => replace
          args_code += code
          replace[args[i]] = Replacement.new(:sexp => var)
        end
      end
    elsif /^[0-9]+$/ =~ arg
      # got a number argument, stuff it in a integer variable
      args_code << Sexp.new(:lasgn, args[i], Sexp.new(:lit, arg.to_i))
    else
      # got a string
      replace[args[i]] = Replacement.new(:sexp => Sexp.new(:str, arg)) if args[i]
    end
  end

  if params[:return_variable]
    if s.sexp[3][0] == :block
      code = Sexp.new(:lasgn, params[:return_variable],
        Sexp.new(:block,
          *(s.sexp[3][1..-1].map{ |s| s.dup })
        )
      )
    else
      code = Sexp.new(:lasgn, params[:return_variable], s.sexp[3].dup)
    end
  else
    code = s.sexp[3].dup
  end

  replace.each do |k,v|
    code.replace_variable_references! :replacement => v, :needle => k, :named_args => @named_args
  end

  return *args_code + [code]
end

#determine_argumentsObject

Extract the arguments from the code block of this Reduction.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/linguify/reduction.rb', line 64

def determine_arguments
  s = Marshal.load(Marshal.dump(self)) # sexp handling is not clean cut
  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

  block_args = s.sexp[2]
  if block_args
    if block_args[0]==:lasgn
      # single argument
      args = [block_args[1]]
    elsif block_args[0]==:masgn
      # multiple arguments
      args = block_args[1]
      raise "unsupported argument type #{args}" unless args[0]==:array
      args = args[1..-1].map{ |arg|
        raise "unsupported argument type #{arg}" unless arg[0]==:lasgn
        arg[1]
      }
    else
      raise "unsupported argument type #{args}"
    end
  end

  # maybe we can fix the input so we don't have to repair it here?
  @args = @args[-args.size..-1] if args and args.size != @args.size

  @named_args = Hash[*args.zip(@args[-args.size..-1]).flatten] if args
  @named_args ||= {}
end

#to_rexpObject

Get the reduction reference for this Reduction.



197
198
199
200
# File 'lib/linguify/reduction.rb', line 197

def to_rexp
  raise "hell" if returns.kind_of?(Array)
  "{#{returns}:#{reduction_id}}"
end