lilp 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/.gitignore +1 -0
  2. data/README.md +37 -0
  3. data/lib/lilp/base.rb +66 -241
  4. data/lib/lilp/version.rb +3 -3
  5. metadata +3 -1
@@ -0,0 +1 @@
1
+ pkg/
@@ -0,0 +1,37 @@
1
+ Lightweight Literate programming
2
+ ================================
3
+
4
+ lilp is a preprocessor that allows you to write literate programming code using the Markdown syntax.
5
+
6
+ It is an experiment and shouldn't be used for any serious matter. Or
7
+ should it?!
8
+
9
+ ## How it works
10
+
11
+ Simply write your source file as if you were writing a normal Markdown document.
12
+
13
+ ~~~~ ruby
14
+ puts "code inside code blocks will be extracted, while the rest will
15
+ be tranformed as comments"
16
+ ~~~~
17
+
18
+ Everything but code blocks will be transformed as comments.
19
+
20
+ # Compiling
21
+ lilp comes with a command line tool that takes as an input file names, and a "-o" option to specify an output directory.
22
+
23
+ If your files are under the `lib` directory, and you want to compile them into the `tangled` directory, then do:
24
+
25
+ ~~~~
26
+ lilp lib/* -o tangled/
27
+ ~~~~
28
+
29
+ ## License
30
+
31
+ Copyright (C) 2011 Michael Sokol
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,279 +1,104 @@
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
1
  module Lilp
157
2
 
158
- class Option
159
- attr_reader :files, :params
3
+ class Option
4
+ attr_reader :files, :params
160
5
 
161
- def initialize( args )
162
- @params = {}
163
- @parser = OptionParser.new
164
- @args = args
6
+ def initialize( args )
7
+ @params = {}
8
+ @parser = OptionParser.new
9
+ @args = args
165
10
 
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
11
+ @parser.banner =
12
+ "Usage: lilp file_name.pl [other_file.pl] [-o output_dir]"
13
+
14
+ @parser.on("-o", "--output D", String, "Output directory") do |val|
15
+ @params[:output] = File.join('.', "#{val}")
16
+ end
17
+ end
169
18
 
170
- def parse
171
- begin
172
- @files = @parser.parse(@args)
173
- if @files.empty?
174
- puts "Missing file names"
19
+ def parse
20
+ begin
21
+ @files = @parser.parse(@args)
22
+ if @files.empty?
23
+ puts "Missing file names"
24
+ puts @parser
25
+ exit
26
+ end
27
+ rescue OptionParser::InvalidOption => opt
28
+ puts "Unknown option #{opt}"
29
+ puts @parser
30
+ exit
31
+ rescue OptionParser::MissingArgument => opt
32
+ puts opt
175
33
  puts @parser
176
34
  exit
177
35
  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
36
  end
187
37
  end
188
- end
189
38
 
190
- class LiterateRender < Redcarpet::Render::Base
39
+ class LiterateRender < Redcarpet::Render::Base
191
40
  COMMENT_SIGN = "# "
192
41
 
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\: (.*)/
42
+ def paragraph(text)
43
+ text += "\n"
44
+ text.gsub(/^/, COMMENT_SIGN)
201
45
  end
202
- end
203
-
204
- def paragraph(text)
205
- text += "\n"
206
- text.gsub(/^/, COMMENT_SIGN)
207
- end
208
46
 
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
47
+ def block_code(code, language)
214
48
  code += "\n"
215
49
  end
216
- end
217
50
 
218
- def hrule()
219
- @define_macro = ( not @define_macro )
220
- @current_macro = nil if @define_macro
221
51
  end
222
52
 
223
- def emphasis(text)
224
- @current_macro = text.to_sym if @define_macro
225
- nil
226
- end
53
+ class Runner
227
54
 
228
- end
55
+ def run( params, files_path )
56
+ lilp_parser = Redcarpet::Markdown.new(LiterateRender,
57
+ :fenced_code_blocks => true)
229
58
 
230
- class Runner
59
+ files_path.each do |file_path|
60
+ puts "#{file_path}: "
231
61
 
232
- def run( params, files_path )
233
- lilp_parser = Redcarpet::Markdown.new(LiterateRender, :fenced_code_blocks => true)
62
+ if File.extname( file_path ) != '.md'
63
+ puts 'Skipping (file must have a .md extension)'
64
+ next
65
+ end
234
66
 
235
- files_path.each do |file_path|
236
- puts "#{file_path}: "
67
+ output_path = String.new
68
+ if params[:output]
69
+ # Creates the output directory if it doesn't exist
70
+ if File.exists?(params[:output])
71
+ puts "Folder #{params[:output]} already exists"
72
+ else
73
+ puts "Creating folder #{params[:output]}"
74
+ Dir.mkdir(params[:output])
75
+ end
237
76
 
238
- if File.extname( file_path ) != '.md'
239
- puts 'Skipping (file must have a .md extension)'
240
- next
241
- end
77
+ file_name = File.basename(file_path).chomp(
78
+ File.extname(file_path) )
242
79
 
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"
80
+ output_path = File.join(params[:output], file_name)
248
81
  else
249
- puts "Creating folder #{params[:output]}"
250
- Dir.mkdir(params[:output])
82
+ output_path = file_path.chomp( File.extname(file_path) )
251
83
  end
252
84
 
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
85
+ begin
86
+ file = File.open( file_path, 'r' )
87
+ out = File.open( output_path, 'w' )
258
88
 
259
- begin
260
- file = File.open( file_path, 'r' )
261
- out = File.open( output_path, 'w' )
89
+ out.write( lilp_parser.render( file.read ) )
262
90
 
263
- out.write( lilp_parser.render( file.read ) )
91
+ out.close
92
+ file.close
264
93
 
265
- out.close
266
- file.close
94
+ puts "Wrote #{output_path}"
267
95
 
268
- puts "Wrote #{output_path}"
269
-
270
- rescue
271
- puts "Error while parsing file '#{file_path}': #{$!}"
96
+ rescue
97
+ puts "Error while parsing file '#{file_path}': #{$!}"
98
+ end
272
99
  end
273
- end
274
100
 
101
+ end
275
102
  end
276
- end
277
103
 
278
104
  end
279
-
@@ -1,6 +1,6 @@
1
1
  # To keep up with the version, we declare it as a constant under
2
2
  # the Lilp module.
3
- module Lilp
4
- VERSION = '0.1.1'
5
- end
3
+ module Lilp
4
+ VERSION = '0.2.0'
5
+ end
6
6
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: lilp
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.1
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Michael Sokol
@@ -33,7 +33,9 @@ extensions: []
33
33
  extra_rdoc_files: []
34
34
 
35
35
  files:
36
+ - .gitignore
36
37
  - Gemfile
38
+ - README.md
37
39
  - Rakefile
38
40
  - bin/lilp
39
41
  - lib/lilp.rb