Class: DSL

Inherits:
Object
  • Object
show all
Defined in:
tools/dsl.rb

Overview

Simple DSL implementation for Ripper code generation

input: /*% ripper: stmts_add!(stmts_new!, void_stmt!) %*/ output:

VALUE v1, v2;
v1 = dispatch0(stmts_new);
v2 = dispatch0(void_stmt);
$$ = dispatch2(stmts_add, v1, v2);
  • The code must be a single line.

  • The code is basically Ruby code, even if it appears like in C and the result will be processed as C. e.g., comments need to be in Ruby style.

Defined Under Namespace

Classes: Var

Constant Summary collapse

TAG_PATTERN =
/(?><[a-zA-Z0-9_]+>)/.source
NAME_PATTERN =
/(?>\$|\d+|[a-zA-Z_][a-zA-Z0-9_]*|\[[a-zA-Z_.][-a-zA-Z0-9_.]*\])(?>(?:\.|->)[a-zA-Z_][a-zA-Z0-9_]*)*/.source
NOT_REF_PATTERN =
/(?>\#.*|[^\"$@]*|"(?>\\.|[^\"])*")/.source

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(code, options, lineno = nil, indent: "\t\t\t") ⇒ DSL

Returns a new instance of DSL.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'tools/dsl.rb', line 108

def initialize(code, options, lineno = nil, indent: "\t\t\t")
  @lineno = lineno
  @indent = indent
  @events = {}
  @error = options.include?("error")
  if options.include?("final")
    @final = "p->result"
  else
    @final = (options.grep(/\A\$#{NAME_PATTERN}\z/o)[0] || "p->s_lvalue")
  end

  bind = dsl_binding
  @var_table = Var::Table.new {|arg| "get_value(#{arg})"}
  code = code.gsub(%r[\G#{NOT_REF_PATTERN}\K(\$|\$:|@)#{TAG_PATTERN}?#{NAME_PATTERN}]o) {
    if (arg = $&) == "$:$"
      '"p->s_lvalue"'
    elsif arg.start_with?("$:")
      "(#{@var_table[arg]}=@var_table[#{arg.dump}])"
    else
      arg.dump
    end
  }
  @last_value = bind.eval(code)
rescue SyntaxError
  $stderr.puts "error on line #{@lineno}" if @lineno
  raise
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(event, *args) ⇒ Object



168
169
170
171
172
173
174
175
176
# File 'tools/dsl.rb', line 168

def method_missing(event, *args)
  if event.to_s =~ /!\z/
    add_event(event, args)
  elsif args.empty? and (/\Aid[A-Z_]/ =~ event or @var_table.defined?(event))
    event
  else
    "#{ event }(#{ args.map(&:to_s).join(", ") })"
  end
end

Instance Attribute Details

#eventsObject (readonly)

Returns the value of attribute events.



141
142
143
# File 'tools/dsl.rb', line 141

def events
  @events
end

Class Method Details

.comma_split(str) ⇒ Object



29
30
31
32
# File 'tools/dsl.rb', line 29

def self.comma_split(str)
  str or return []
  str.scan(/(([^(,)]+|\((?:,|\g<0>)*\))+)/).map(&:first)
end

.const_missing(name) ⇒ Object



178
179
180
# File 'tools/dsl.rb', line 178

def self.const_missing(name)
  name
end

.line?(line, lineno = nil, indent: nil) ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
26
27
# File 'tools/dsl.rb', line 23

def self.line?(line, lineno = nil, indent: nil)
  if %r<(?<space>\s*)/\*% *ripper(?:\[(?<option>.*?)\])?: *(?<code>.*?) *%\*/> =~ line
    new(code, comma_split(option), lineno, indent: indent || space)
  end
end

Instance Method Details

#add_event(event, args) ⇒ Object



157
158
159
160
161
162
163
164
165
166
# File 'tools/dsl.rb', line 157

def add_event(event, args)
  event = event.to_s.sub(/!\z/, "")
  @events[event] = args.size
  vars = []
  args.each do |arg|
    arg = @var_table.add {arg} unless Var === arg
    vars << arg
  end
  @var_table.add {"dispatch#{ args.size }(#{ [event, *vars].join(",") })"}
end

#dsl_binding(p = "p") ⇒ Object



136
137
138
139
# File 'tools/dsl.rb', line 136

def dsl_binding(p = "p")
  # struct parser_params *p
  binding
end

#generateObject



147
148
149
150
151
152
153
154
155
# File 'tools/dsl.rb', line 147

def generate
  s = "#@final=#@last_value;"
  s << "ripper_error(p);" if @error
  unless @var_table.empty?
    vars = @var_table.map {|_, v| "#{v.var}=#{v.value}"}.join(", ")
    s = "VALUE #{ vars }; #{ s }"
  end
  "#{@indent}{#{s}}"
end