Fix -R output, and disable unstable use of parameterized chunks
[newfangle.git] / newfangle
blob15b55e18fe2bda3a22f08a9904db37ee0f25defa
1 #! /usr/bin/awk -f
2 #newfangle - fully featured notangle replacement in awk
4 #Copyright (C) Sam Liddicott 2009
6 #This program is free software: you can redistribute it and/or modify
7 #it under the terms of the GNU General Public License as published by
8 #the Free Software Foundation, either version 3 of the License, or
9 #(at your option) any later version.
11 #This program is distributed in the hope that it will be useful,
12 #but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 #GNU General Public License for more details.
16 #You should have received a copy of the GNU General Public License
17 #along with this program. If not, see <http://www.gnu.org/licenses/>.
19 # NOTE: Arnold Robbins public domain getopt for awk is also used:
20 # getopt.awk --- do C library getopt(3) function in awk
22 # Arnold Robbins, arnold@skeeve.com, Public Domain
24 # Initial version: March, 1991
25 # Revised: May, 1993
27 function getopt(argc, argv, options, thisopt, i)
29 if (length(options) == 0) # no options given
30 return -1
31 if (argv[Optind] == "--") { # all done
32 Optind++
33 _opti = 0
34 return -1
35 } else if (argv[Optind] !~ /^-[^: \t\n\f\r\v\b]/) {
36 _opti = 0
37 return -1
39 if (_opti == 0)
40 _opti = 2
41 thisopt = substr(argv[Optind], _opti, 1)
42 Optopt = thisopt
43 i = index(options, thisopt)
44 if (i == 0) {
45 if (Opterr)
46 printf("%c -- invalid option\n",
47 thisopt) > "/dev/stderr"
48 if (_opti >= length(argv[Optind])) {
49 Optind++
50 _opti = 0
51 } else
52 _opti++
53 return "?"
55 if (substr(options, i + 1, 1) == ":") {
56 # get option argument
57 if (length(substr(argv[Optind], _opti + 1)) > 0)
58 Optarg = substr(argv[Optind], _opti + 1)
59 else
60 Optarg = argv[++Optind]
61 _opti = 0
62 } else
63 Optarg = ""
64 if (_opti == 0 || _opti >= length(argv[Optind])) {
65 Optind++
66 _opti = 0
67 } else
68 _opti++
69 return thisopt
72 function error(message)
74 print message > "/dev/stderr";
75 exit 1;
77 function new_mode(language, mode) {
78 mode["language"] = language;
79 mode["state"]="";
81 function new_chunk(chunk_name, params,
82 # local vars
83 p )
85 # HACK WHILE WE CHANGE TO ( ) for PARAM CHUNKS
86 gsub("\\(\\)$", "", chunk_name);
87 active_chunk = chunk_name;
88 if (! (chunk_name in chunk_names)) {
89 if (debug) print "New chunk " chunk_name;
90 chunk_names[chunk_name];
91 for (p in params) {
92 chunks[chunk_name, p] = params[p];
95 prime_chunk(chunk_name);
98 function prime_chunk(chunk_name)
100 chunks[chunk_name, "part", ++chunks[chunk_name, "part"] ] = \
101 chunk_name SUBSEP "chunklet" SUBSEP "" ++chunks[chunk_name, "chunklet"];
102 chunks[chunk_name, "part", chunks[chunk_name, "part"], "FILENAME"] = FILENAME;
103 chunks[chunk_name, "part", chunks[chunk_name, "part"], "LINENO"] = FNR + 1;
106 function chunk_line(chunk_name, line){
107 chunks[chunk_name, "chunklet", chunks[chunk_name, "chunklet"],
108 ++chunks[chunk_name, "chunklet", chunks[chunk_name, "chunklet"], "line"] ] = line;
111 function chunk_include(chunk_name, chunk_ref, indent, tail)
113 chunks[chunk_name, "part", ++chunks[chunk_name, "part"] ] = chunk_ref;
114 chunks[chunk_name, "part", chunks[chunk_name, "part"], "type" ] = part_type_chunk;
115 chunks[chunk_name, "part", chunks[chunk_name, "part"], "indent" ] = indent_string(indent);
116 chunks[chunk_name, "part", chunks[chunk_name, "part"], "tail" ] = tail;
117 prime_chunk(chunk_name);
120 function indent_string(indent) {
121 return sprintf("%" indent "s", "");
123 function output_chunk_names( c, prefix, suffix)
125 if (notangle_mode) {
126 prefix="<<";
127 suffix=">>";
129 for (c in chunk_names) {
130 print prefix c suffix "\n";
133 function output_chunks( a)
135 for (a in chunk_names) {
136 output_chunk(chunk_names[a]);
140 function output_chunk(chunk) {
141 newline = 1;
142 lineno_needed = linenos;
144 write_chunk(chunk);
147 function write_chunk(chunk_name, indent, tail,
148 # optional vars
149 chunk_path, chunk_args,
150 # local vars
151 part, max_part, part_line, frag, max_frag, text,
152 chunklet, only_part, call_chunk_args, mode)
154 if (match(chunk_name, "^(.*)\\[([0-9]*)\\]$", chunk_name_parts)) {
155 chunk_name = chunk_name_parts[1];
156 only_part = chunk_name_parts[2];
158 split("", mode);
159 new_mode(chunks[chunk_name, "language"], mode);
160 split(chunks[chunk_name, "params"], chunk_params, " *; *");
161 if (! (chunk_name in chunk_names)) {
162 error(sprintf(_"The root module <<%s>> was not defined.\nUsed by: %s",\
163 chunk_name, chunk_path));
166 max_part = chunks[chunk_name, "part"];
167 for(part = 1; part <= max_part; part++) {
168 if (! only_part || part == only_part) {
169 if (linenos && (chunk_name SUBSEP "part" SUBSEP part SUBSEP "FILENAME" in chunks)) {
170 a_filename = chunks[chunk_name, "part", part, "FILENAME"];
171 a_lineno = chunks[chunk_name, "part", part, "LINENO"];
172 if (a_filename != filename || a_lineno != lineno) {
173 lineno_needed++;
177 chunklet = chunks[chunk_name, "part", part];
178 if (chunks[chunk_name, "part", part, "type"] == part_type_chunk) {
179 if (match(chunklet, "^([^\\[]*)\\((.*)\\)$", chunklet_parts)) {
180 chunklet = chunklet_parts[1];
181 get_chunk_args(chunklet_parts[2], chunk_args);
182 # TO BE parse_chunk_args SOON
183 for (c in call_chunk_args) {
184 call_chunk_args[c] = expand_chunk_args(call_chunk_args[c], chunk_params, chunk_args);
186 } else {
187 split("", call_chunk_args);
189 write_chunk(chunklet,
190 chunks[chunk_name, "part", part, "indent"] indent,
191 chunks[chunk_name, "part", part, "tail"],
192 chunk_path "\n " chunk_name,
193 call_chunk_args);
194 } else if (chunklet SUBSEP "line" in chunks) {
195 max_frag = chunks[chunklet, "line"];
196 for(frag = 1; frag <= max_frag; frag++) {
197 if (newline && lineno_needed && ! lineno_suppressed) {
198 filename = a_filename;
199 lineno = a_lineno;
200 print "#line " lineno " \"" filename "\"\n"
201 lineno_needed = 0;
204 text = chunks[chunklet, frag];
206 /* check params */
207 text = expand_chunk_args(text, chunk_params, chunk_args);
209 if (text == "\n") {
210 lineno++;
211 if (part == max_part && frag == max_frag && length(chunk_path)) {
212 text = "";
213 break;
214 } else {
215 newline = 1;
217 } else {
218 if (newline) text = indent text;
219 newline = 0;
222 text = text tail;
223 # track_mode(mode, text);
224 print text;
225 if (linenos) {
226 lineno_suppressed = substr(lastline, length(lastline)) == "\\";
229 } else {
230 # empty last chunklet
235 function expand_chunk_args(text, params, args,
236 p, text_array, next_text, v, t, l)
238 if (split(text, text_array, "\\${")) {
239 for(p in params) {
240 v[params[p]]=args[p];
242 text=text_array[1];
243 for(t in text_array) if (t>1) {
244 if (match(text_array[t], "^([a-zA-Z_][a-zA-Z0-9_]*)}", l) &&
245 l[1] in v)
247 text = text v[l[1]] substr(text_array[t], length(l[1])+2);
248 } else {
249 text = text "${" text_array[t];
253 return text;
255 BEGIN {
256 part_type_chunk=1;
257 debug=0;
258 linenos=0;
259 notangle_mode=0;
260 SUBSEP=",";
261 root="*";
263 Optind = 1 # skip ARGV[0]
264 while(getopt(ARGC, ARGV, "R:Ldhr")!=-1) {
265 if (Optopt == "R") root = Optarg;
266 else if (Optopt == "r") root="";
267 else if (Optopt == "L") linenos = 1;
268 else if (Optopt == "d") debug = 1;
269 else if (Optopt == "h") help();
270 else if (Optopt == "?") help();
272 for (i=1; i<Optind; i++) { ARGV[i]=""; }
274 /^\\Chunk{/ {
275 if (match($0, "^\\\\Chunk{ *([^ ,}]*),?(.*)}", line)) {
276 next_chunk_name = line[1];
277 get_chunk_args(line[2], next_chunk_args);
279 next;
281 /^\\begin{lstlisting}|^\\begin{Chunk}/ {
282 if (match($0, "}.*[[,] *name= *{? *([^], }]*)", line)) {
283 new_chunk(line[1]);
284 } else {
285 new_chunk(next_chunk_name, next_chunk_args);
287 chunking=1;
288 next;
290 /^[<]<.*[>]>=/ {
291 if (match($0, "^[<]<(.*)[>]>= *$", line)) {
292 chunking=1;
293 notangle_mode=1;
294 new_chunk(line[1]);
295 next;
298 /^\\[e]nd{lstlisting}|^\\[e]nd{Chunk}/ {
299 chunking=0;
300 active_chunk="";
301 next;
303 /^@/ {
304 chunking=0;
305 active_chunk="";
307 ! chunking { next; }
308 length(active_chunk) {
309 chunk = $0;
310 indent = 0;
311 while(match(chunk,
312 "([=]<\\\\chunkref{([^}>]*)}(\\(.*\\)|)>|<<([a-zA-Z_][-a-zA-Z0-9_]*)>>)",
313 line)\
315 chunklet = substr(chunk, 1, RSTART - 1);
316 indent += length(chunklet);
317 chunk_line(active_chunk, chunklet);
318 chunk = substr(chunk, RSTART + RLENGTH);
319 if (substr(line[1], 1, 1) == "=") {
320 # chunk name up to }
321 chunk_include(active_chunk, line[2] line[3], indent);
322 } else if (substr(line[1], 1, 1) == "<") {
323 chunk_include(active_chunk, line[4], indent);
324 } else {
325 error("Unknown chunk fragment: " line[1]);
328 chunk_line(active_chunk, chunk);
329 chunk_line(active_chunk, "\n");
331 END {
332 if (debug) {
333 print "------ chunk names "
334 output_chunk_names();
335 print "====== chunks"
336 output_chunks();
337 print "++++++ debug"
338 for (a in chunks) {
339 print a "=" chunks[a];
342 ORS="";
343 if (length(root)) output_chunk(root);
344 else output_chunk_names();
346 function get_chunk_args(text, values,
347 # optional parameters
348 path, # hierarchical precursors
349 # local vars
350 a, name)
352 while(length(text)) {
353 if (match(text, "^ *}(.*)", a)) {
354 return a[1];
356 if (! match(text, " *([^,=]*[^,= ]) *(([,=]) *(([^,}]*) *,* *(.*))|)$", a)) {
357 return text;
359 name=a[1];
360 if (a[3] == "=") {
361 if (substr(a[4],1,1) == "{") {
362 text = get_chunk_args(substr(a[4],2), values, path name SUBSEP);
363 } else {
364 values[path name]=a[5];
365 text = a[6];
367 } else {
368 values[path name]="";
369 text = a[2];
372 return text;