gpp 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 768357e29236ee25c7bdfad581346d092c16ffea
4
- data.tar.gz: a17f96873ebdf6f6e8db3bd8ae108e909f4c7f36
3
+ metadata.gz: 539e909aab402cd55e8238e07e0f70e691716751
4
+ data.tar.gz: f3982fd692c0fb4df5a66b42abf53cb2d4715ee3
5
5
  SHA512:
6
- metadata.gz: 85c4310438cd96db69747dca9c31c1de02dccbc385d691bacfd044722281fabff8c39210f2caa1719bfb3f755fa20015309257b1c178d1fd79df9796602ccfd9
7
- data.tar.gz: 6140d1bc380ede98ca30cc49fcc2450c8799b0bc1430a6b2becea8a01bd71cf61117feeac3c96cd25dc5d471affd9419028462007265986ee1617fc518d6a527
6
+ metadata.gz: 29e53154b0804e2f60f62755a5ffd6b22e244f9d6f7212895076c8e9c43ae0b90ad85e8c3f5a786b917c70e2e2d8677ea22849faccc22ae5492213be6d1f812d
7
+ data.tar.gz: f27500f9231e066812ede4af8c26716e73cec2cb6216ee5150c9ea6275a7289f6fc7073f23d530d2c01ec9225262d88f366d7f6eb0ab4b5827122455bc66888b
data/bin/gpp CHANGED
@@ -3,15 +3,26 @@ require 'gpp'
3
3
  require 'pathname'
4
4
  require 'optparse'
5
5
 
6
+ from, to = nil, nil
7
+
6
8
  OptionParser.new do |opts|
7
9
  opts.version = GPP::VERSION
8
10
  opts.banner = "Usage: #{opts.program_name} [-hv] [files]"
11
+ opts.on "-r PATTERN" do |value|
12
+ _, from, to = value.split("/")
13
+ from = /#{from}/
14
+ end
9
15
  end.order!
10
16
 
11
17
  args = ARGV.empty? ? ["-"] : ARGV
12
18
 
13
19
  args.each do |arg|
14
20
  file = arg == "-" ? STDIN.read : File.read(arg)
15
- rpp = GPP::Processor.new(file, STDOUT)
21
+ out = if from
22
+ File.open(arg.gsub(from, to), "w")
23
+ else
24
+ STDOUT
25
+ end
26
+ rpp = GPP::Processor.new(file, out, arg, 1)
16
27
  rpp.scan_all
17
28
  end
@@ -3,10 +3,15 @@ require 'strscan'
3
3
  module GPP
4
4
  class Processor < StringScanner
5
5
 
6
- def initialize (in_, out, defs = {})
6
+ attr_reader :path, :trace
7
+
8
+ def initialize (in_, out, path, offset, defs = {}, trace = [])
7
9
  super(in_)
8
10
  @out = out
9
11
  @defs = defs.to_h
12
+ @path = path
13
+ @offset = offset
14
+ @trace = trace
10
15
  end
11
16
 
12
17
  def loopcat (into = "")
@@ -40,9 +45,18 @@ module GPP
40
45
  end
41
46
  end
42
47
 
43
- def scan_word (bare = /\S+/)
44
- if scan(/{\n?/)
45
- scan_block()
48
+ def indent_block (block, indent)
49
+ block.gsub(/^\n/, "").gsub(/\n$/, "").gsub("\n", "\n#{indent}")
50
+ end
51
+
52
+ def undent_block (block)
53
+ indent = block[/\A\n[ \t]+/]
54
+ indent ? block.gsub(/#{indent}/, "\n") : block
55
+ end
56
+
57
+ def scan_arg (bare = /\S+/)
58
+ if scan(/{/)
59
+ undent_block(scan_block())
46
60
  elsif scan(/"/)
47
61
  scan_string()
48
62
  else
@@ -52,84 +66,117 @@ module GPP
52
66
 
53
67
  def scan_args
54
68
  if scan(/\(/)
55
- loopcat [] do
69
+ scan(/[ \t]+/)
70
+ loopcat [scan_arg(/[^,)]*/)] do
56
71
  if scan(/\)/)
57
72
  nil
58
- else
59
- scan_word(/[^,)]+/).tap do
60
- scan(/,/)
61
- end
73
+ elsif scan(/,/)
74
+ scan(/[ \t]+/)
75
+ scan_arg(/[^,)]*/)
62
76
  end
63
77
  end
64
78
  else
65
79
  loopcat [] do
66
- if scan(/\n/)
80
+ if scan(/(?=\n)/)
67
81
  nil
68
82
  else
69
- scan(/ +/)
70
- scan_word
83
+ scan(/[ \t]+/)
84
+ scan_arg
71
85
  end
72
86
  end
73
87
  end
74
88
  end
75
89
 
76
- def run (string, args = nil)
90
+ def run (string, args, path, line)
91
+ trace = self.trace + [tracer]
77
92
  if args == nil
78
- self.class.new(string, s = "", @defs).scan_all
93
+ self.class.new(string, s = "", path, line, @defs, trace).scan_all
79
94
  else
80
- self.class.new(string, s = "", @defs.merge(args.to_h)).scan_all
95
+ self.class.new(string, s = "", path, line, @defs.merge(args.to_h), trace).scan_all
81
96
  end
82
97
  s
83
98
  end
84
99
 
85
- def run_block (args, body)
86
- theargs = scan_args
87
- run(body, args.zip(theargs))
88
- end
100
+ Definition = Struct.new(:type, :args, :body, :path, :line)
89
101
 
90
102
  def run_macro (id)
91
- if d = @defs[id]
92
- if d.is_a?(String)
93
- run(d)
94
- else
95
- theargs = scan_args
96
- run(d[1], d[0].zip(theargs))
103
+ rpos = pos - id.length - 2
104
+ w = string[(string.rindex("\n", rpos) || 0) + 1..rpos][/[ \t]+/]
105
+ res = if d = @defs[id]
106
+ case d.type
107
+ when :var
108
+ run(d.body, {}, d.path, d.line)
109
+ when :fun
110
+ args = scan_args.map{|arg| Definition.new(:var, {}, run(arg, {}, path, line), path, line)}
111
+ if d.args[-1] == "..."
112
+ la = d.args.length - 1
113
+ args[la..-1] = Definition.new(:var, {}, args[la..-1].map(&:body).join(" "), args[la].path, args[la].line)
114
+ end
115
+ if args.length != d.args.length
116
+ error "wrong argument count for #{id}: expected #{d.args.length} but got #{args.length}"
117
+ end
118
+ run(d.body, d.args.zip(args), d.path, d.line)
97
119
  end
98
120
  else
99
- STDERR.puts "undefined macro: #{id}"
100
- exit 1
121
+ error "undefined macro: #{id}"
101
122
  end
123
+ indent_block(res, w)
102
124
  end
103
125
 
104
126
  def scan_define
127
+ line = self.line
105
128
  name, *args, body = scan_args
106
- @defs[name] = [args, body]
129
+ if args == []
130
+ @defs[name] = Definition.new(:var, nil, body, @path, line)
131
+ else
132
+ @defs[name] = Definition.new(:fun, args, body, @path, line)
133
+ end
107
134
  end
108
135
 
109
136
  def scan_import ()
110
137
  args = scan_args
111
138
  args.each do |arg|
112
- run File.read(arg)
139
+ run File.read(arg), nil, arg, 1
113
140
  end
114
141
  end
115
142
 
116
143
  def scan_all
117
- scan(/\s+/)
118
144
  while !eos?
119
145
  @out << (scan(/[^#@]+/) || "")
120
146
  if scan(/#define\b/)
121
147
  scan_define
148
+ scan(/\s+/)
122
149
  elsif scan(/#import\b/)
123
150
  scan_import
151
+ scan(/\s+/)
152
+ #elsif scan(/#(\w+)/)
153
+ # error "undefined meta-macro: #{self[1]}"
124
154
  elsif scan(/@@/)
125
155
  @out << "@"
126
- elsif scan(/@(\w+)/)
156
+ elsif scan(/@(\.\.\.|\w+)/)
127
157
  @out << (run_macro(self[1]) || "")
128
- else
129
- scan(/./)
158
+ elsif s = scan(/./)
159
+ @out << s
130
160
  end
131
161
  end
132
162
  end
133
163
 
164
+ def line
165
+ @offset + string[0..pos - 1].count("\n")
166
+ end
167
+
168
+ def tracer
169
+ [path, line]
170
+ end
171
+
172
+ def error (message)
173
+ STDERR.puts "#{message}"
174
+ STDERR.puts " in #{path}:#{line}"
175
+ trace.reverse.each do |path, line|
176
+ STDERR.puts " #{path}:#{line}"
177
+ end
178
+ exit 1
179
+ end
180
+
134
181
  end
135
182
  end
@@ -1,3 +1,3 @@
1
1
  module GPP
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gpp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Baum
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-11 00:00:00.000000000 Z
11
+ date: 2015-12-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A text pre-processor
14
14
  email: n@p12a.org.uk