mucgly 0.0.1

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/CHANGELOG.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = Version history
2
+
3
+ [0.0.1] Initial version.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 tero.isannainen@gmail.com
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/README.rdoc ADDED
@@ -0,0 +1,292 @@
1
+ = Mucgly
2
+
3
+ == Introduction
4
+
5
+ Mucgly is a macro expander for inline macros that exist in the middle of
6
+ body text. The macros are mostly regular Ruby code, but a few special
7
+ commands is also provided.
8
+
9
+ A very simple example:
10
+ Adding 1 + 3 results: -<write (1+3).to_s>-
11
+
12
+ After macro expansion the results is:
13
+ Adding 1 + 3 results: 4
14
+
15
+ By default macro starts with "-<" and ends with ">-". These limiters
16
+ are called hooks, hookbeg and hookend respectively. The code between
17
+ hooks is regular Ruby code. The "write" call writes to selected IO
18
+ stream.
19
+
20
+ Ruby code is executed within a class instance reserved for this
21
+ purpose. The instance is called the Execution Environment. It enables
22
+ values from one macro to be visible in others.
23
+
24
+ Previous example with multiple macros:
25
+ -<@a = 1>--<@b = 3>-\
26
+ Adding 1 + 3 results: -<write (@a+@b).to_s>-
27
+
28
+ Result is exactly the same as in the previous execution. The first
29
+ macro "-<@a = 1>-" produces no output, it just sets the variable "@a"
30
+ to "1". Second macro is similar. The default "escape" character is
31
+ "\\". When placed before newline character, it "eats" the newline and
32
+ nothing is output. Thus the first line outputs nothing.
33
+
34
+ The second line refers to settings from previous macros. Instance
35
+ variables has to be used to maintain the data between macro calls (due
36
+ to instance evaluation).
37
+
38
+
39
+ == Features
40
+
41
+ * User settable hooks to define macro boundaries. Can be set from
42
+ command line, configuration files, or from macro file.
43
+
44
+ * Multiple sources for configuration: default config, environment
45
+ variable, command line.
46
+
47
+ * Multiple passes for macro file.
48
+
49
+ * Convention based output file naming.
50
+
51
+ * Multiple convenience functions for macros to use.
52
+
53
+ * Macro file introspection: line number, file name
54
+
55
+ * Output stream de-muxing.
56
+
57
+ * Many special commands: include, source, etc...
58
+
59
+
60
+
61
+ == Applications
62
+
63
+ * Replacement for M4 macro processor.
64
+
65
+ * Code generation.
66
+
67
+ * Document formatting.
68
+
69
+ * Etc.
70
+
71
+
72
+
73
+ == Special characters
74
+
75
+ === Hooks
76
+
77
+ Hooks start and end the macro definition. By default hookbeg is "-<"
78
+ and hookend is ">-". These values are not easily conflicting with the
79
+ macro file body text.
80
+
81
+ If literal hook string is required, the escape character should be
82
+ place before the hook. For example "\-<" will produce literal "-<" to
83
+ the output.
84
+
85
+ User can set the hooks to whatever string value desired. The hooks can
86
+ also have the same value. However nested macros are not possible
87
+ then. Hooks can also be the same as the escape character, but this
88
+ makes usage somewhat complicated.
89
+
90
+ === Escape
91
+
92
+ Default escape character is "\\". It can be used to escape hooks,
93
+ cancel space (" ") output, and cancel newline ("\\n") output.
94
+
95
+ User can set the escape character to any character. Only single
96
+ character value is accepted.
97
+
98
+ === Multipass
99
+
100
+ If the macro starts with "#" character, the macro is not expanded. One
101
+ "#" character is removed and the rest of the macro is output as it
102
+ is. Each pass will remove one "#" character and when all are removed,
103
+ the macro is evaluated. Usually two passes are enough and one "#"
104
+ character is used to save a macro to the later pass.
105
+
106
+ Multipass can be used for example to create a Table Of Contents. First
107
+ pass collects information about document content and second pass will
108
+ insert the Table Of Contents.
109
+
110
+ Example, with functions (insert_toc, header...) defined elsewhere:
111
+ -<#insert_toc>-
112
+ -<header(1, "My header 1")>-
113
+ Paragraph1
114
+ -<header(2, "My header 1.1")>-
115
+ Paragraph2
116
+ -<header(1, "My header 2")>-
117
+ Paragraph3
118
+
119
+
120
+ == Special commands
121
+
122
+ In addition to regular Ruby code the macros are allowed to include so
123
+ called Special Commands. These commands start with the ":" characters
124
+ and with ".".
125
+
126
+ Example:
127
+ ...
128
+ -<:hook [ ]->\
129
+ ...
130
+
131
+ Would change the hookbeg and hookend to "[" and "]" respectively. One
132
+ special command is allowed per macro and it can't be mixed normal Ruby
133
+ code. Command name is separated by ":" in the beginning and by space
134
+ (" ") in the end. The rest of the macro is taken as argument to the
135
+ macro. Note that a command without an argument has to end with space
136
+ as well.
137
+
138
+ List of ":" style special commands:
139
+
140
+ [include] Include reverts the input stream to the file given in the
141
+ argument. Command can be used to multiplex multiple files
142
+ into one, for example.
143
+
144
+ [output] Select a new output file. Use with "close" command, when the
145
+ new output file is ready.
146
+
147
+ [comment] Comment is used to add comments into the macro file. No
148
+ output is produced from comment macros.
149
+
150
+ [source] Source reads a plain Ruby file into the Execution Environment.
151
+
152
+ [hook] Sets both hookbeg and hookend to values separated by space. The
153
+ new hook values are valid immediately after the enclosing
154
+ macro.
155
+
156
+ [hookbeg] Sets hookbeg.
157
+
158
+ [hookend] Sets hookend.
159
+
160
+ [escape] Sets escape character.
161
+
162
+ [exit] Aborts the macro file.
163
+
164
+ Variable value reference command starts with "." and is followed by
165
+ the (instance) variable name.
166
+
167
+ For example:
168
+ ... -<.my_name>- ...
169
+
170
+ Would write out the value of the "@my_name" variable, thus it is equal
171
+ to writing:
172
+ ... -<write @my_name>- ...
173
+
174
+
175
+
176
+ == API methods
177
+
178
+ API methods are instance methods of the Execution Environment,
179
+ i.e. they are predefined and visible for the macro code. These methods
180
+ are organized into two categories: Published and Hidden.
181
+
182
+ === Published methods
183
+
184
+ Published methods are the most commonly used methods in macros. These are:
185
+
186
+ [write] Writes string into selected output IO.
187
+ [puts] Writes string (+newline) into selected output IO.
188
+ [source] Sources a plain Ruby file.
189
+
190
+
191
+ === Hidden methods
192
+
193
+ Hidded methods start with underscore ("_"). The naming is used to
194
+ prevent poluting the Execution Environment's namespace.
195
+
196
+ [_processFilePair] Process a pair of files. First half is input file
197
+ and second is output file. File pair argument is an
198
+ Array of two ([<fileI>, <fileO>]).
199
+
200
+ [_processFilePairs] Uses "_processFilePair" to process a list of file pairs.
201
+
202
+ [_openInput] Sets input stream to given file. When this file is
203
+ closed, the input stream is reverted to the original
204
+ file.
205
+
206
+ [_openOutput] Sets output stream to given file. When this file is
207
+ closed, the input stream is reverted to the original
208
+ file.
209
+
210
+ [_openString] Open a StringIO file for output.
211
+
212
+ [_pushInput] Makes given file as top input stream. When this stream is
213
+ closed, the input stream is reverted to the original
214
+ file.
215
+
216
+ [_pushOutput] Makes given file as top output stream. When this stream
217
+ is closed, the output stream is reverted to the original
218
+ file.
219
+
220
+ [_closeInput] Close input stream.
221
+
222
+ [_closeOutput] Close output stream.
223
+
224
+ [_ofilename] Output file name as String.
225
+
226
+ [_olinenumber] Output file line number.
227
+
228
+ [_ifilename] Input file name as String.
229
+
230
+ [_ilinenumber] Input file line number.
231
+
232
+ [_eval] Perform instance_eval on the given argument.
233
+
234
+ [_inIO] Handle to input stream (get/set).
235
+
236
+ [_outIO] Handle to output stream (get/set).
237
+
238
+ [_separators] Handle to Separators object. Separotors object includes
239
+ the "escapeChar", "hookBegChars", and "hookEndChars"
240
+ setter and getter methods.
241
+
242
+
243
+ == Command line interface
244
+
245
+ Please execute:
246
+ mucgly -h
247
+
248
+ for an overview of the command line options.
249
+
250
+
251
+ === Input and Output files
252
+
253
+ Input files can be listed to "-i" option. The default input stream for
254
+ Mucgly is STDIN.
255
+
256
+ Output files can be listed to "-o" option. The default output stream
257
+ for Mucgly is STDOUT.
258
+
259
+ If "-o" option is given without arguments and the input file list
260
+ includes ".rx" in each name, the output file list is created by
261
+ extracting ".rx" from each input file name.
262
+
263
+ For example with:
264
+ mucgly -i foo.rx.txt bar.rx.txt -o
265
+
266
+ Two files, foo.txt and bar.txt, would be created. This holds true
267
+ unless the output streams are manipulated with macro commands.
268
+
269
+ If the number of input and output files match, then they are used in pairs.
270
+
271
+
272
+ == Configuration
273
+
274
+ Mucgly checks for the existance of the "MUCGLY" environment
275
+ variable. If the variable exists, the file in variable value is read
276
+ in as plain Ruby.
277
+
278
+ User configuration is searched from the users home directory:
279
+ $HOME/.mucgly
280
+ It is read as plain Ruby file if it exists.
281
+
282
+ Configuration files can also be given on command line using the "-c"
283
+ option. These settings can be used to override the settings above.
284
+
285
+ Additionally plain Ruby code can be given straight on the command line
286
+ with "-l" option.
287
+
288
+
289
+ == More information
290
+
291
+ Check out the "test" directory within installation for detailed
292
+ examples about usage.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.libs << 'lib'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
10
+
11
+ task :cleanup_test do
12
+ sh "rm -rf test/test"
13
+ end
14
+
15
+ task :build => :doc do
16
+ sh "gem build mucgly.gemspec"
17
+ end
18
+
19
+ task :doc do
20
+ sh "yardoc lib/* - README.rdoc CHANGELOG.rdoc"
21
+ end
22
+
23
+ task :publish do
24
+ if Dir.glob('mucgly-*gem').length == 1
25
+ sh "gem push mucgly*.gem"
26
+ else
27
+ raise "Multiple gems in the directory..."
28
+ end
29
+ end
data/bin/mucgly ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'como'
4
+ include Como
5
+ require_relative '../lib/easyfile'
6
+ require_relative '../lib/mucgly'
7
+
8
+
9
+ # Define command line arguments:
10
+ Spec.command( "mucgly", "Tero Isannainen", "2009, 2013",
11
+ [
12
+ [ :opt_multi, "inputs", "-i", "Input file (default: <STDIN>)." ],
13
+ [ :opt_multi, "configs", "-c", "Ruby configure file(s)" ],
14
+ [ :opt_any, "output", "-o", "Output file (default: <STDOUT>)." ],
15
+ [ :opt_multi, "cl_cmds", "-l", "Command line configuration(s)." ],
16
+ [ :opt_any, "multipass", "-m", "Multipass rounds (default without arg: 2)." ],
17
+ [ :opt_single, "beg_sep", "-sb", "Set hookbeg separator (default: '-<')." ],
18
+ [ :opt_single, "end_sep", "-se", "Set hookend separator (default: '->')." ],
19
+ [ :opt_single, "seps", "-s", "Set both hookbeg and hookend separators." ],
20
+ [ :opt_single, "esc_char", "-e", "Set escape character (default: '\\')." ],
21
+ [ :opt_single, "all_sep", "-sa", "Set all separators and escape." ],
22
+ [ :switch, "no_inits", nil, "No initializations." ],
23
+ [ :switch, "no_user_init", nil, "No user initialization." ],
24
+ [ :switch, "no_env_init", nil, "No env initialization." ],
25
+ [ :switch, "warn_only", "-w", "Warnings only." ],
26
+ [ :switch, "debug", "-d", "Display debug messages." ],
27
+ ], { :tab => 15, } )
28
+
29
+
30
+ filePairs = []
31
+
32
+
33
+ # Set input streams.
34
+ unless Opt['inputs'].given
35
+ filePairs.push [ '<STDIN>', nil ]
36
+ else
37
+ Opt['inputs'].value.each do |i|
38
+ filePairs.push [ i, nil ]
39
+ end
40
+ end
41
+
42
+
43
+ # Set output streams.
44
+ if Opt['output'].given
45
+
46
+ if Opt['output'].value.empty?
47
+
48
+ # Map all input files to output files where '.rx' is removed
49
+ # in the name.
50
+
51
+ # Check that all inputs have '.rx' in the name.
52
+ noRx = false
53
+ name = nil
54
+ filePairs.each do |pair|
55
+ if /\.rx/.match( pair[0] )
56
+ pair[1] = pair[0].gsub( /\.rx/, '' )
57
+ else
58
+ noRx = true
59
+ name = pair[0]
60
+ break
61
+ end
62
+ end
63
+
64
+ if noRx
65
+ Mucgly::error( "Input file must have '.rx' in name\
66
+ when automapping: \"#{name}\"" )
67
+ end
68
+
69
+ elsif Opt['output'].value.length == filePairs.length
70
+
71
+ filePairs.each_index do |idx|
72
+ filePairs[ idx ][1] = Opt['output'].value[ idx ]
73
+ end
74
+
75
+ else
76
+
77
+ Mucgly::error( "Input and output file counts does not match:\
78
+ #{Opt['output'].value.length}/#{filePairs.length}" )
79
+
80
+ end
81
+
82
+ else
83
+
84
+ # Default output, STDOUT.
85
+ filePairs[0][1] = '<STDOUT>'
86
+
87
+ end
88
+
89
+
90
+ execEnv = Mucgly::Env.new
91
+ execEnv._separators = Mucgly::Separators.new
92
+
93
+
94
+ # Change separators:
95
+ if Opt['beg_sep'].given
96
+ execEnv._separators.hookBegChars = Opt['beg_sep'].value
97
+ end
98
+
99
+ if Opt['end_sep'].given
100
+ execEnv._separators.hookEndChars = Opt['end_sep'].value
101
+ end
102
+
103
+ if Opt['seps'].given
104
+ rest = Opt['seps'].value
105
+
106
+ if rest.split.length == 2
107
+ execEnv._separators.hookBegChars = rest.split[0]
108
+ execEnv._separators.hookEndChars = rest.split[1]
109
+ else
110
+ execEnv._separators.hookBegChars = rest
111
+ execEnv._separators.hookEndChars = rest
112
+ end
113
+ end
114
+
115
+
116
+ # Change escape:
117
+ if Opt['esc_char'].given
118
+ execEnv._separators.escapeChar = Opt['esc_char'].value
119
+ end
120
+
121
+
122
+ # Change all specials.
123
+ if Opt['all_sep'].given
124
+ execEnv._separators.hookBegChars = Opt['all_sep'].value
125
+ execEnv._separators.hookEndChars = Opt['all_sep'].value
126
+ execEnv._separators.escapeChar = Opt['all_sep'].value
127
+ end
128
+
129
+
130
+ # Read Mucgly env config.
131
+ unless ( Opt['no_env_init'].given || Opt['no_inits'].given )
132
+ p = ENV["MUCGLY"]
133
+ execEnv.source( p ) if p
134
+ end
135
+
136
+
137
+ # Read default user config.
138
+ unless ( Opt['no_user_init'].given || Opt['no_inits'].given )
139
+ userInit = "#{ENV['HOME']}/.mucgly"
140
+ if FileTest.exist? userInit
141
+ # load userInit
142
+ execEnv.source( userInit )
143
+ end
144
+ end
145
+
146
+
147
+ # Process provided configure files.
148
+ Opt['configs'].given do |value|
149
+ value.each do |i|
150
+ execEnv.source( i )
151
+ end
152
+ end
153
+
154
+
155
+ # Process command line.
156
+ Opt['cl_cmds'].given do |value|
157
+ value.each do |i|
158
+ execEnv.eval( i )
159
+ end
160
+ end
161
+
162
+
163
+ # Multipass processing.
164
+ if Opt['multipass'].given
165
+
166
+ if Opt['multipass'].value.empty?
167
+ passes = 2
168
+ else
169
+ passes = Opt['multipass'].value[0].to_i
170
+ end
171
+
172
+
173
+ if passes <= 1
174
+
175
+ execEnv._processFilePairs( filePairs )
176
+
177
+ else
178
+
179
+ filePairs.each do |filePair|
180
+
181
+ pass = 0
182
+
183
+ # Pair (ping-pong) of StringIO handles.
184
+ sio = [nil]*2
185
+
186
+ while pass < passes
187
+
188
+ # Use StringIO to pass content between passes.
189
+ if pass == 0
190
+ # First pass.
191
+ execEnv._openInput( filePair[ 0 ] )
192
+ sio[(pass+0)%2] = execEnv._openString( "_pass0" )
193
+ sio[(pass+1)%2] = execEnv._openString( "_pass1" )
194
+ execEnv._outIO = sio[(pass+0)%2]
195
+ elsif pass >= ( passes-1 )
196
+ # Last pass.
197
+ execEnv._inIO = sio[(pass+1)%2]
198
+ execEnv._inIO.rewind
199
+ execEnv._openOutput( filePair[ 1 ] )
200
+ else
201
+ # Middle pass.
202
+ execEnv._inIO = sio[(pass+1)%2]
203
+ execEnv._inIO.rewind
204
+ execEnv._outIO = sio[(pass+0)%2]
205
+ end
206
+
207
+ Mucgly::MucglyFile.new( execEnv )
208
+ pass += 1
209
+
210
+ end
211
+
212
+ end
213
+
214
+ end
215
+
216
+ else
217
+
218
+ execEnv._processFilePairs( filePairs )
219
+
220
+ end