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



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



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