lilp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gemspec
4
+ gemspec
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+
3
+ # This adds 'lib/' to the $LOAD_PATH
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ task :default => :test
7
+
8
+ task :test do
9
+ Dir["test/**_test.rb"].each{ |r| require_relative r }
10
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'lilp'
4
+
5
+ options = Lilp::Option.new( ARGV.dup )
6
+ options.parse
7
+
8
+ runner = Lilp::Runner.new
9
+ runner.run( options.params, options.files )
@@ -0,0 +1,12 @@
1
+ # This file lists all the the dependencies of lilp.
2
+ # The `optparse` library is used to handle the command line options.
3
+ # The redcarpet library is a markdown parser with a very nice API.
4
+ # We are going to use it in order to create the lilp renderer.
5
+ require "optparse"
6
+ require 'redcarpet'
7
+
8
+ # Next, let's require our own files. They are located under the lilp/
9
+ # directory.
10
+ require 'lilp/version'
11
+ require 'lilp/base.rb'
12
+
@@ -0,0 +1,279 @@
1
+ # lilp is a literate programming pre-processor that allows you to write literate programs
2
+ # using Markdown. In its current state, it is mainly an experiment, a toy.
3
+ # This file is the main program file of the lilp application. Here is how we are going to
4
+ # decompose it.
5
+ # I propose we dive directly into the subject's by specifying how to parse and render source
6
+ # code from lilp files. After that, we'll see how to handle option parsing for our command
7
+ # line application. And lastly, we'll tie everything together.
8
+ # lilp files are simply files that end with a 'md' extension. They are valid markdown files.
9
+ # In order to simplify the parsing phase, I am using the `redcarpet` library. This library
10
+ # is easy to extend.
11
+ # What we want to do is to create a 'render'. A render is an object that outputs a stream.
12
+ # For that, it defines a number of hooks called by redcarpet's parser. What we need to do
13
+ # is to specify a valid 'render' object, and make it output a program. Let's do that now.
14
+ # _Literate Render_
15
+ # Let's define here our render. It is a class that inherite from Redcarpet::Render::Base
16
+ # class LiterateRender < Redcarpet::Render::Base
17
+ # COMMENT_SIGN = "# "
18
+ #
19
+ # Now, redcarpet's parser will call a number of hook, depending on what has been found
20
+ # in the given lilp file. These hooks can be either `header`, `paragraph`, `block_code`
21
+ # or other. Here is the formal specification:
22
+ # def preprocess(full_document)
23
+ # @macro = {}
24
+ # full_document
25
+ # end
26
+ #
27
+ # def header(text, header_level)
28
+ # if header_level == 3
29
+ # @macro[$1.to_sym] if text =~ /Call\: (.*)/
30
+ # end
31
+ # end
32
+ #
33
+ # def paragraph(text)
34
+ # text += "\n"
35
+ # text.gsub(/^/, COMMENT_SIGN)
36
+ # end
37
+ #
38
+ # def block_code(code, language)
39
+ # if @define_macro and @current_macro
40
+ # @macro[@current_macro] = code += "\n"
41
+ # code.gsub(/^/, COMMENT_SIGN)
42
+ # else
43
+ # code += "\n"
44
+ # end
45
+ # end
46
+ #
47
+ # def hrule()
48
+ # @define_macro = ( not @define_macro )
49
+ # @current_macro = nil if @define_macro
50
+ # end
51
+ #
52
+ # def emphasis(text)
53
+ # @current_macro = text.to_sym if @define_macro
54
+ # nil
55
+ # end
56
+ #
57
+ # end
58
+ #
59
+ # The code above lists all the rules our render will live by. If there is anything to
60
+ # change in our render, it's in this part of the code.
61
+ # To a regular user, lilp is only a command he can invoke from the terminal. In order
62
+ # to work with it, we need to get some information from the user, such as the files
63
+ # he wants to preprocess, or the output directory where he wants the preprocessed files
64
+ # to go.
65
+ # For this, we use the `optparse` library. We create an Option object that uses that library's
66
+ # API.
67
+ # _The option parser_
68
+ # class Option
69
+ # attr_reader :files, :params
70
+ #
71
+ # def initialize( args )
72
+ # @params = {}
73
+ # @parser = OptionParser.new
74
+ # @args = args
75
+ #
76
+ # @parser.banner = "Usage: lilp file_name.pl [other_file.pl] [-o output_dir]"
77
+ # @parser.on("-o", "--output D", String, "Output directory") { |val| @params[:output] = File.join('.', "#{val}") }
78
+ # end
79
+ #
80
+ # def parse
81
+ # begin
82
+ # @files = @parser.parse(@args)
83
+ # if @files.empty?
84
+ # puts "Missing file names"
85
+ # puts @parser
86
+ # exit
87
+ # end
88
+ # rescue OptionParser::InvalidOption => opt
89
+ # puts "Unknown option #{opt}"
90
+ # puts @parser
91
+ # exit
92
+ # rescue OptionParser::MissingArgument => opt
93
+ # puts opt
94
+ # puts @parser
95
+ # exit
96
+ # end
97
+ # end
98
+ # end
99
+ #
100
+ # We have a lilp render class, an option class that takes care of the command line
101
+ # options, now we need a way to tie the to together.
102
+ # To do this, let's create a third object. The class will be "Runner". It's goal
103
+ # is to render files passed to it while taking into account the options passed by
104
+ # the command line.
105
+ # _Runner class_
106
+ # class Runner
107
+ #
108
+ # def run( params, files_path )
109
+ # lilp_parser = Redcarpet::Markdown.new(LiterateRender, :fenced_code_blocks => true)
110
+ #
111
+ # files_path.each do |file_path|
112
+ # puts "#{file_path}: "
113
+ #
114
+ # if File.extname( file_path ) != '.md'
115
+ # puts 'Skipping (file must have a .lp extension)'
116
+ # next
117
+ # end
118
+ #
119
+ # output_path = String.new
120
+ # if params[:output]
121
+ # # Creates the output directory if it doesn't exist
122
+ # if File.exists?(params[:output])
123
+ # puts "Folder #{params[:output]} already exists"
124
+ # else
125
+ # puts "Creating folder #{params[:output]}"
126
+ # Dir.mkdir(params[:output])
127
+ # end
128
+ #
129
+ # file_name = File.basename(file_path).chomp( File.extname(file_path) )
130
+ # output_path = File.join(params[:output], file_name)
131
+ # else
132
+ # output_path = file_path.chomp( File.extname(file_path) )
133
+ # end
134
+ #
135
+ # begin
136
+ # file = File.open( file_path, 'r' )
137
+ # out = File.open( output_path, 'w' )
138
+ #
139
+ # out.write( lilp_parser.render( file.read ) )
140
+ #
141
+ # out.close
142
+ # file.close
143
+ #
144
+ # puts "Wrote #{output_path}"
145
+ #
146
+ # rescue
147
+ # puts "Error while parsing file '#{file_path}': #{$!}"
148
+ # end
149
+ # end
150
+ #
151
+ # end
152
+ # end
153
+ #
154
+ # Now, the last thing we need to do is to attach each of the parts in the correct order, under
155
+ # the Lilp module.
156
+ module Lilp
157
+
158
+ class Option
159
+ attr_reader :files, :params
160
+
161
+ def initialize( args )
162
+ @params = {}
163
+ @parser = OptionParser.new
164
+ @args = args
165
+
166
+ @parser.banner = "Usage: lilp file_name.pl [other_file.pl] [-o output_dir]"
167
+ @parser.on("-o", "--output D", String, "Output directory") { |val| @params[:output] = File.join('.', "#{val}") }
168
+ end
169
+
170
+ def parse
171
+ begin
172
+ @files = @parser.parse(@args)
173
+ if @files.empty?
174
+ puts "Missing file names"
175
+ puts @parser
176
+ exit
177
+ end
178
+ rescue OptionParser::InvalidOption => opt
179
+ puts "Unknown option #{opt}"
180
+ puts @parser
181
+ exit
182
+ rescue OptionParser::MissingArgument => opt
183
+ puts opt
184
+ puts @parser
185
+ exit
186
+ end
187
+ end
188
+ end
189
+
190
+ class LiterateRender < Redcarpet::Render::Base
191
+ COMMENT_SIGN = "# "
192
+
193
+ def preprocess(full_document)
194
+ @macro = {}
195
+ full_document
196
+ end
197
+
198
+ def header(text, header_level)
199
+ if header_level == 3
200
+ @macro[$1.to_sym] if text =~ /Call\: (.*)/
201
+ end
202
+ end
203
+
204
+ def paragraph(text)
205
+ text += "\n"
206
+ text.gsub(/^/, COMMENT_SIGN)
207
+ end
208
+
209
+ def block_code(code, language)
210
+ if @define_macro and @current_macro
211
+ @macro[@current_macro] = code += "\n"
212
+ code.gsub(/^/, COMMENT_SIGN)
213
+ else
214
+ code += "\n"
215
+ end
216
+ end
217
+
218
+ def hrule()
219
+ @define_macro = ( not @define_macro )
220
+ @current_macro = nil if @define_macro
221
+ end
222
+
223
+ def emphasis(text)
224
+ @current_macro = text.to_sym if @define_macro
225
+ nil
226
+ end
227
+
228
+ end
229
+
230
+ class Runner
231
+
232
+ def run( params, files_path )
233
+ lilp_parser = Redcarpet::Markdown.new(LiterateRender, :fenced_code_blocks => true)
234
+
235
+ files_path.each do |file_path|
236
+ puts "#{file_path}: "
237
+
238
+ if File.extname( file_path ) != '.md'
239
+ puts 'Skipping (file must have a .lp extension)'
240
+ next
241
+ end
242
+
243
+ output_path = String.new
244
+ if params[:output]
245
+ # Creates the output directory if it doesn't exist
246
+ if File.exists?(params[:output])
247
+ puts "Folder #{params[:output]} already exists"
248
+ else
249
+ puts "Creating folder #{params[:output]}"
250
+ Dir.mkdir(params[:output])
251
+ end
252
+
253
+ file_name = File.basename(file_path).chomp( File.extname(file_path) )
254
+ output_path = File.join(params[:output], file_name)
255
+ else
256
+ output_path = file_path.chomp( File.extname(file_path) )
257
+ end
258
+
259
+ begin
260
+ file = File.open( file_path, 'r' )
261
+ out = File.open( output_path, 'w' )
262
+
263
+ out.write( lilp_parser.render( file.read ) )
264
+
265
+ out.close
266
+ file.close
267
+
268
+ puts "Wrote #{output_path}"
269
+
270
+ rescue
271
+ puts "Error while parsing file '#{file_path}': #{$!}"
272
+ end
273
+ end
274
+
275
+ end
276
+ end
277
+
278
+ end
279
+
@@ -0,0 +1,6 @@
1
+ # To keep up with the version, we declare it as a constant under
2
+ # the Lilp module.
3
+ module Lilp
4
+ VERSION = '0.1.0'
5
+ end
6
+
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "lilp/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "lilp"
7
+ s.version = Lilp::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Michael Sokol"]
10
+ s.email = ["mikaa123@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Lilp is lightweight literate programming}
13
+
14
+ s.add_dependency 'redcarpet'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lilp
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Michael Sokol
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-11-16 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: redcarpet
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ description:
27
+ email:
28
+ - mikaa123@gmail.com
29
+ executables:
30
+ - lilp
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - Gemfile
37
+ - Rakefile
38
+ - bin/lilp
39
+ - lib/lilp.rb
40
+ - lib/lilp/base.rb
41
+ - lib/lilp/version.rb
42
+ - lilp.gemspec
43
+ homepage: ""
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.8
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Lilp is lightweight literate programming
70
+ test_files: []
71
+