Class: GPP::Processor

Inherits:
StringScanner
  • Object
show all
Defined in:
lib/gpp/processor.rb

Defined Under Namespace

Classes: Definition

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(in_, out, path, offset, defs = {}, trace = []) ⇒ Processor

Returns a new instance of Processor.



8
9
10
11
12
13
14
15
# File 'lib/gpp/processor.rb', line 8

def initialize (in_, out, path, offset, defs = {}, trace = [])
  super(in_)
  @out = out
  @defs = defs.to_h
  @path = path
  @offset = offset
  @trace = trace
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



6
7
8
# File 'lib/gpp/processor.rb', line 6

def path
  @path
end

#traceObject (readonly)

Returns the value of attribute trace.



6
7
8
# File 'lib/gpp/processor.rb', line 6

def trace
  @trace
end

Instance Method Details

#error(message) ⇒ Object



172
173
174
175
176
177
178
179
# File 'lib/gpp/processor.rb', line 172

def error (message)
  STDERR.puts "#{message}"
  STDERR.puts " in #{path}:#{line}"
  trace.reverse.each do |path, line|
    STDERR.puts "    #{path}:#{line}"
  end
  exit 1
end

#indent_block(block, indent) ⇒ Object



48
49
50
# File 'lib/gpp/processor.rb', line 48

def indent_block (block, indent)
  block.gsub(/^\n/, "").gsub(/\n$/, "").gsub("\n", "\n#{indent}")
end

#lineObject



164
165
166
# File 'lib/gpp/processor.rb', line 164

def line
  @offset + string[0..pos - 1].count("\n")
end

#loopcat(into = "") ⇒ Object



17
18
19
20
21
22
23
24
# File 'lib/gpp/processor.rb', line 17

def loopcat (into = "")
  loop do
    val = yield
    break unless val
    into << val
  end
  into
end

#run(string, args, path, line) ⇒ Object



90
91
92
93
94
95
96
97
98
# File 'lib/gpp/processor.rb', line 90

def run (string, args, path, line)
  trace = self.trace + [tracer]
  if args == nil
    self.class.new(string, s = "", path, line, @defs, trace).scan_all
  else
    self.class.new(string, s = "", path, line, @defs.merge(args.to_h), trace).scan_all
  end
  s
end

#run_macro(id) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/gpp/processor.rb', line 102

def run_macro (id)
  rpos = pos - id.length - 2
  w = string[(string.rindex("\n", rpos) || 0) + 1..rpos][/[ \t]+/]
  res = if d = @defs[id]
    case d.type
    when :var
      run(d.body, {}, d.path, d.line)
    when :fun
      args = scan_args.map{|arg| Definition.new(:var, {}, run(arg, {}, path, line), path, line)}
      if d.args[-1] == "..."
        la = d.args.length - 1
        args[la..-1] = Definition.new(:var, {}, args[la..-1].map(&:body).join(" "), args[la].path, args[la].line)
      end
      if args.length != d.args.length
        error "wrong argument count for #{id}: expected #{d.args.length} but got #{args.length}"
      end
      run(d.body, d.args.zip(args), d.path, d.line)
    end
  else
    error "undefined macro: #{id}"
  end
  indent_block(res, w)
end

#scan_allObject



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/gpp/processor.rb', line 143

def scan_all
  while !eos?
    @out << (scan(/[^#@]+/) || "")
    if scan(/#define\b/)
      scan_define
      scan(/\s+/)
    elsif scan(/#import\b/)
      scan_import
      scan(/\s+/)
    #elsif scan(/#(\w+)/)
    #  error "undefined meta-macro: #{self[1]}"
    elsif scan(/@@/)
      @out << "@"
    elsif scan(/@(\.\.\.|\w+)/)
      @out << (run_macro(self[1]) || "")
    elsif s = scan(/./)
      @out << s
    end
  end
end

#scan_arg(bare = /\S+/) ⇒ Object



57
58
59
60
61
62
63
64
65
# File 'lib/gpp/processor.rb', line 57

def scan_arg (bare = /\S+/)
  if scan(/{/)
    undent_block(scan_block())
  elsif scan(/"/)
    scan_string()
  else
    scan(bare)
  end
end

#scan_argsObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/gpp/processor.rb', line 67

def scan_args
  if scan(/\(/)
    scan(/[ \t]+/)
    loopcat [scan_arg(/[^,)]*/)] do
      if scan(/\)/)
        nil
      elsif scan(/,/)
        scan(/[ \t]+/)
        scan_arg(/[^,)]*/)
      end
    end
  else
    loopcat [] do
      if scan(/(?=\n)/)
        nil
      else
        scan(/[ \t]+/)
        scan_arg
      end
    end
  end
end

#scan_blockObject



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/gpp/processor.rb', line 26

def scan_block
  loopcat do
    if scan(/{/)
      "{" + scan_block + "}"
    elsif scan(/}/)
      nil
    else
      scan(/[^{}]+/)
    end
  end
end

#scan_defineObject



126
127
128
129
130
131
132
133
134
# File 'lib/gpp/processor.rb', line 126

def scan_define
  line = self.line
  name, *args, body = scan_args
  if args == []
    @defs[name] = Definition.new(:var, nil, body, @path, line)
  else
    @defs[name] = Definition.new(:fun, args, body, @path, line)
  end
end

#scan_importObject



136
137
138
139
140
141
# File 'lib/gpp/processor.rb', line 136

def scan_import ()
  args = scan_args
  args.each do |arg|
    run File.read(arg), nil, arg, 1
  end
end

#scan_stringObject



38
39
40
41
42
43
44
45
46
# File 'lib/gpp/processor.rb', line 38

def scan_string
  loopcat do
    if scan(/"/)
      nil
    else
      scan(/\\?.|[^"\\]/)
    end
  end
end

#tracerObject



168
169
170
# File 'lib/gpp/processor.rb', line 168

def tracer
  [path, line]
end

#undent_block(block) ⇒ Object



52
53
54
55
# File 'lib/gpp/processor.rb', line 52

def undent_block (block)
  indent = block[/\A\n[ \t]+/]
  indent ? block.gsub(/#{indent}/, "\n") : block
end