lilp 0.1.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/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
+