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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
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.
@@ -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
@@ -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
@@ -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
+
@@ -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
+
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class PprbTest < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'pprb'
8
+
9
+ class Test::Unit::TestCase
10
+ end
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