pprb 0.9.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.
- data/.document +5 -0
- data/LICENSE +20 -0
- data/Rakefile +67 -0
- data/bin/pprb +26 -0
- data/examples/hw.c +29 -0
- data/lib/pprb.rb +235 -0
- data/test/pprb_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +103 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Zahary Karadjov, Stefan Dragnev
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require './lib/pprb'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "pprb"
|
10
|
+
gem.authors = ["Zahary Karadjov", "Stefan Dragnev"]
|
11
|
+
gem.summary = "PPRB The little preprocessor that could"
|
12
|
+
gem.description = "PPRB is a code preprocessor and text templating engine with minimalistic syntax and strong emphasis on eliminating any code duplication in template files"
|
13
|
+
gem.email = "zahary@gmail.com"
|
14
|
+
gem.homepage = "http://github.com/zah/pprb"
|
15
|
+
gem.version = PPRB::Version::STRING
|
16
|
+
|
17
|
+
gem.executable = 'pprb'
|
18
|
+
|
19
|
+
gem.files.exclude 'CMake'
|
20
|
+
gem.files.exclude 'VisualStudio'
|
21
|
+
|
22
|
+
gem.add_development_dependency "thoughtbot-shoulda"
|
23
|
+
gem.add_dependency "trollop"
|
24
|
+
end
|
25
|
+
|
26
|
+
#Jeweler::GemcutterTasks.new
|
27
|
+
rescue LoadError
|
28
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'rake/testtask'
|
32
|
+
Rake::TestTask.new(:test) do |test|
|
33
|
+
test.libs << 'lib' << 'test'
|
34
|
+
test.pattern = 'test/**/*_test.rb'
|
35
|
+
test.verbose = true
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'rcov/rcovtask'
|
40
|
+
Rcov::RcovTask.new do |test|
|
41
|
+
test.libs << 'test'
|
42
|
+
test.pattern = 'test/**/*_test.rb'
|
43
|
+
test.verbose = true
|
44
|
+
end
|
45
|
+
rescue LoadError
|
46
|
+
task :rcov do
|
47
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
task :test => :check_dependencies
|
52
|
+
|
53
|
+
task :default => :test
|
54
|
+
|
55
|
+
require 'rake/rdoctask'
|
56
|
+
Rake::RDocTask.new do |rdoc|
|
57
|
+
if File.exist?('VERSION')
|
58
|
+
version = File.read('VERSION')
|
59
|
+
else
|
60
|
+
version = ""
|
61
|
+
end
|
62
|
+
|
63
|
+
rdoc.rdoc_dir = 'rdoc'
|
64
|
+
rdoc.title = "pprb #{version}"
|
65
|
+
rdoc.rdoc_files.include('README*')
|
66
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
67
|
+
end
|
data/bin/pprb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'pprb'
|
7
|
+
require 'trollop'
|
8
|
+
|
9
|
+
options = Trollop::options do
|
10
|
+
banner "PPRB #{PPRB::Version::STRING} usage: pprb source [options]"
|
11
|
+
opt :out, "Output file. Unless supplied prints to STDOUT", :short => 'o', :type => String
|
12
|
+
end
|
13
|
+
|
14
|
+
source = ARGV.shift
|
15
|
+
|
16
|
+
Trollop::die "Please, provide a source file" unless source
|
17
|
+
|
18
|
+
open(source, "r") do |input|
|
19
|
+
target_output = PPRB::render input
|
20
|
+
|
21
|
+
unless options.out
|
22
|
+
puts target_output
|
23
|
+
else
|
24
|
+
open(opts[:out], 'w').write target_output
|
25
|
+
end
|
26
|
+
end
|
data/examples/hw.c
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
> hw = "Hello World"
|
2
|
+
|
3
|
+
% rb
|
4
|
+
puts "Arbitrary ruby code is executed here"
|
5
|
+
puts "Functions could be defined"
|
6
|
+
|
7
|
+
def test a, b
|
8
|
+
a + b
|
9
|
+
end
|
10
|
+
-
|
11
|
+
|
12
|
+
> # this defines a function, but keeps the standard mode for
|
13
|
+
> # "lines without special symbols are output"
|
14
|
+
% def hello_world_template
|
15
|
+
puts("Hello World")
|
16
|
+
% 3.times
|
17
|
+
puts("Hello Loop")
|
18
|
+
-
|
19
|
+
-
|
20
|
+
|
21
|
+
void main()
|
22
|
+
{
|
23
|
+
> hello_world_template
|
24
|
+
|
25
|
+
% 6.times
|
26
|
+
printf("%d", `test 5, 10`);
|
27
|
+
-
|
28
|
+
}
|
29
|
+
|
data/lib/pprb.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
module PPRB
|
2
|
+
module Version
|
3
|
+
MAJOR = 0
|
4
|
+
MINOR = 9
|
5
|
+
PATCH = 0
|
6
|
+
|
7
|
+
STRING = [MAJOR, MINOR, PATCH].compact.join('.')
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.render source
|
11
|
+
PPRB.new(source).run
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.compile source
|
15
|
+
PPRB.new(source)
|
16
|
+
end
|
17
|
+
|
18
|
+
class PPRB
|
19
|
+
# source is either a File object or a string
|
20
|
+
# start_state is either :pp or :rb
|
21
|
+
def initialize source, start_state = :pp
|
22
|
+
@source = source
|
23
|
+
@path = File.expand_path(source.path) if source.class == File
|
24
|
+
|
25
|
+
@BLOCK = @LINE = @END = @TICKS = ''
|
26
|
+
profile :default
|
27
|
+
|
28
|
+
@ruby_output = ''
|
29
|
+
@target_output = ''
|
30
|
+
|
31
|
+
@input_state = [start_state]
|
32
|
+
@last_emitted = :code
|
33
|
+
|
34
|
+
load_rules
|
35
|
+
source.lines.each { |l| process_line l }
|
36
|
+
@ruby_output << ']' if @last_emitted == :text
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
run_and_translate_errors @ruby_output
|
41
|
+
@target_output
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :ruby_output, :target_output
|
45
|
+
|
46
|
+
PROFILES = {
|
47
|
+
:default => {
|
48
|
+
:block => /^%(.*)/,
|
49
|
+
:line => /^>(.*)/,
|
50
|
+
:end => /^-$/,
|
51
|
+
:ticks => /`(.*?)`/
|
52
|
+
},
|
53
|
+
|
54
|
+
:c_parsable => {
|
55
|
+
:block => %r[^//\s*%(.*)],
|
56
|
+
:line => %r[^//\s*>(.*)],
|
57
|
+
:end => %r[^//\s*-$],
|
58
|
+
:ticks => /\brb_(\w+?)_\b/
|
59
|
+
},
|
60
|
+
}
|
61
|
+
|
62
|
+
def profile filter = nil, prof = nil, overlay = nil
|
63
|
+
# sort-out the passed arguments
|
64
|
+
overlay = filter if filter.class == Hash
|
65
|
+
overlay = prof if prof.class == Hash
|
66
|
+
prof = filter if filter.class == Symbol
|
67
|
+
filter = nil if filter != nil and filter.class != String
|
68
|
+
prof = nil if prof != nil and prof.class != Symbol
|
69
|
+
|
70
|
+
raise "No profile or overlay specified" if overlay == nil and prof == nil
|
71
|
+
|
72
|
+
unless filter
|
73
|
+
if prof
|
74
|
+
profile = PROFILES[prof]
|
75
|
+
raise "Unknown profile: #{p}" if profile == nil
|
76
|
+
config profile
|
77
|
+
end
|
78
|
+
|
79
|
+
config overlay if overlay
|
80
|
+
else
|
81
|
+
profile nil, prof, overlay if File.fnmatch(filter, @path)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def config options
|
86
|
+
for key, value in options
|
87
|
+
var = "@#{key.to_s.upcase}"
|
88
|
+
raise "Unknown option: #{key}" if instance_variable_get(var) == nil
|
89
|
+
instance_variable_set var, value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def process_line line
|
95
|
+
case line.strip
|
96
|
+
when @BLOCK then begin_block $1+"\n"
|
97
|
+
when @END then end_block
|
98
|
+
when @LINE then emit $1+"\n", antistate(@input_state.last)
|
99
|
+
else emit line, @input_state.last
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def end_block
|
104
|
+
raise "Unexpected block end (no matching block start)." if @input_state.size == 1
|
105
|
+
|
106
|
+
if @input_state.pop == :pp
|
107
|
+
emit "end\n", :rb
|
108
|
+
else
|
109
|
+
emit '', :pp
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def begin_block line
|
114
|
+
if @input_state.last == :pp
|
115
|
+
case line
|
116
|
+
when /^\s*rb/ then
|
117
|
+
@input_state.push :rb
|
118
|
+
emit '', :pp
|
119
|
+
when /\bdef|\bfor\b|\bwhile\b|\buntil\b|\bdo\b|\bif\b|\bunless\b|\case\b|\bclass\b|\bmodule\b|\bbegin\b/ then
|
120
|
+
@input_state.push :pp
|
121
|
+
emit line, :rb
|
122
|
+
else
|
123
|
+
@input_state.push :pp
|
124
|
+
emit line.rstrip + " do\n", :rb
|
125
|
+
end
|
126
|
+
else # state :rb
|
127
|
+
@input_state.push :pp
|
128
|
+
emit "begin ", :rb
|
129
|
+
emit line, :pp
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def emit line, state
|
134
|
+
if state == :rb
|
135
|
+
if @last_emitted == :text
|
136
|
+
@ruby_output << "]; #{line}"
|
137
|
+
@last_emitted = :code
|
138
|
+
else
|
139
|
+
@ruby_output << line
|
140
|
+
end
|
141
|
+
else # state :pp
|
142
|
+
if @last_emitted == :code
|
143
|
+
@ruby_output << "target_out %[#{escape_code line}"
|
144
|
+
@last_emitted = :text
|
145
|
+
else
|
146
|
+
@ruby_output << escape_code(line)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def antistate state
|
152
|
+
(state == :pp) ? (:rb) : (:pp)
|
153
|
+
end
|
154
|
+
|
155
|
+
def escape_code str
|
156
|
+
ruby_snippets = []
|
157
|
+
# we don't want anything escaped withing ruby snippets, so first we move them out
|
158
|
+
str.gsub!(@TICKS) { ruby_snippets << $1; "%%#{ruby_snippets.size - 1}" }
|
159
|
+
# after escaping what's necessary, we move them back in
|
160
|
+
str.gsub('\\', '\\\\\\').gsub(']', '\\]').gsub('[', '\\[').gsub(/%%(\d+)/) { "\#{#{ruby_snippets[ $1.to_i ]}}" }
|
161
|
+
end
|
162
|
+
|
163
|
+
def pprb_include_dirs
|
164
|
+
return @pprb_include_dirs if @pprb_include_dirs != nil
|
165
|
+
return [] if @path == nil
|
166
|
+
|
167
|
+
dir = File.dirname @path
|
168
|
+
dirs = []
|
169
|
+
|
170
|
+
while true
|
171
|
+
dirs << dir
|
172
|
+
|
173
|
+
modules_dir = dir + "/pprb_modules"
|
174
|
+
dirs << modules_dir if File.directory? modules_dir
|
175
|
+
|
176
|
+
break if dir == '/' or dir == '/cygdrive'
|
177
|
+
|
178
|
+
parent_dir = File.dirname dir
|
179
|
+
break if parent_dir == dir # this a terminating condition on Windows
|
180
|
+
|
181
|
+
dir = parent_dir
|
182
|
+
end
|
183
|
+
|
184
|
+
@pprb_include_dirs = dirs.reverse
|
185
|
+
end
|
186
|
+
|
187
|
+
def load_rules
|
188
|
+
pprb_include_dirs.each do |dir|
|
189
|
+
next unless File.exist?(rules = "#{dir}/pprb.rules")
|
190
|
+
open(rules) { |f| run_and_translate_errors(f.read, rules) }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def use file
|
195
|
+
found = false
|
196
|
+
|
197
|
+
pprb_include_dirs.each do |dir|
|
198
|
+
next unless File.exist?(include = "#{dir}/#{file}.pprb.i")
|
199
|
+
found = true
|
200
|
+
open(include) do |file|
|
201
|
+
pprb = PPRB.new(file.read, :rb)
|
202
|
+
run_and_translate_errors pprb.ruby_output, include
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
raise "Could not find PPRB module '#{file}'" unless found
|
207
|
+
end
|
208
|
+
|
209
|
+
def target_out text
|
210
|
+
@target_output << text
|
211
|
+
end
|
212
|
+
|
213
|
+
def run_and_translate_errors code, filename = @path
|
214
|
+
instance_eval code
|
215
|
+
rescue RuntimeError, Exception => err
|
216
|
+
source_line = nil
|
217
|
+
p err
|
218
|
+
err.backtrace.grep(/\(eval\):(\d+)/) { source_line ||= $1.to_i }
|
219
|
+
|
220
|
+
if source_line
|
221
|
+
$stderr.puts "#{filename}:#{source_line}: error: #{err.to_s}"
|
222
|
+
$stderr.puts
|
223
|
+
|
224
|
+
$stderr.puts "Ruby backtrace:"
|
225
|
+
$stderr.puts err.backtrace
|
226
|
+
|
227
|
+
$stderr.puts "Working directory: #{Dir.pwd}"
|
228
|
+
else
|
229
|
+
raise err
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
data/test/pprb_test.rb
ADDED
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pprb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 59
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Zahary Karadjov
|
14
|
+
- Stefan Dragnev
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-04-14 00:00:00 +03:00
|
20
|
+
default_executable: pprb
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: thoughtbot-shoulda
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
34
|
+
type: :development
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: trollop
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
description: PPRB is a code preprocessor and text templating engine with minimalistic syntax and strong emphasis on eliminating any code duplication in template files
|
51
|
+
email: zahary@gmail.com
|
52
|
+
executables:
|
53
|
+
- pprb
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files:
|
57
|
+
- LICENSE
|
58
|
+
files:
|
59
|
+
- .document
|
60
|
+
- LICENSE
|
61
|
+
- Rakefile
|
62
|
+
- bin/pprb
|
63
|
+
- examples/hw.c
|
64
|
+
- lib/pprb.rb
|
65
|
+
- test/pprb_test.rb
|
66
|
+
- test/test_helper.rb
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: http://github.com/zah/pprb
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
hash: 3
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
requirements: []
|
95
|
+
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.5.0
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: PPRB The little preprocessor that could
|
101
|
+
test_files:
|
102
|
+
- test/pprb_test.rb
|
103
|
+
- test/test_helper.rb
|