Addbasic -T tab support, fix basic parameterization again
[newfangle.git] / newfangle
blobfcfe26b3542518c8381bcfe1a32f607d92d564db
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 # parse_chunk_args(chunklet_parts[2], chunk_args);
182 # TO BE parse_chunk_args SOON
183 split(chunklet_parts[2], call_chunk_args, " *, *");
184 for (c in call_chunk_args) {
185 call_chunk_args[c] = expand_chunk_args(call_chunk_args[c], chunk_params, chunk_args);
187 } else {
188 split("", call_chunk_args);
190 write_chunk(chunklet,
191 chunks[chunk_name, "part", part, "indent"] indent,
192 chunks[chunk_name, "part", part, "tail"],
193 chunk_path "\n " chunk_name,
194 call_chunk_args);
195 } else if (chunklet SUBSEP "line" in chunks) {
196 max_frag = chunks[chunklet, "line"];
197 for(frag = 1; frag <= max_frag; frag++) {
198 if (newline && lineno_needed && ! lineno_suppressed) {
199 filename = a_filename;
200 lineno = a_lineno;
201 print "#line " lineno " \"" filename "\"\n"
202 lineno_needed = 0;
205 text = chunks[chunklet, frag];
207 /* check params */
208 text = expand_chunk_args(text, chunk_params, chunk_args);
210 if (text == "\n") {
211 lineno++;
212 if (part == max_part && frag == max_frag && length(chunk_path)) {
213 text = "";
214 break;
215 } else {
216 newline = 1;
218 } else {
219 if (newline) text = indent text;
220 newline = 0;
223 text = text tail;
224 # track_mode(mode, text);
225 print text;
226 if (linenos) {
227 lineno_suppressed = substr(lastline, length(lastline)) == "\\";
230 } else {
231 # empty last chunklet
236 function expand_chunk_args(text, params, args,
237 p, text_array, next_text, v, t, l)
239 if (split(text, text_array, "\\${")) {
240 for(p in params) {
241 v[params[p]]=args[p];
243 text=text_array[1];
244 for(t in text_array) if (t>1) {
245 if (match(text_array[t], "^([a-zA-Z_][a-zA-Z0-9_]*)}", l) &&
246 l[1] in v)
248 text = text v[l[1]] substr(text_array[t], length(l[1])+2);
249 } else {
250 text = text "${" text_array[t];
254 return text;
256 BEGIN {
257 part_type_chunk=1;
258 debug=0;
259 linenos=0;
260 notangle_mode=0;
261 SUBSEP=",";
262 root="*";
263 tabs = "";
265 Optind = 1 # skip ARGV[0]
266 while(getopt(ARGC, ARGV, "R:LdT:hr")!=-1) {
267 if (Optopt == "R") root = Optarg;
268 else if (Optopt == "r") root="";
269 else if (Optopt == "L") linenos = 1;
270 else if (Optopt == "d") debug = 1;
271 else if (Optopt == "T") tabs = indent_string(Optarg+0);
272 else if (Optopt == "h") help();
273 else if (Optopt == "?") help();
275 for (i=1; i<Optind; i++) { ARGV[i]=""; }
277 /^\\Chunk{/ {
278 if (match($0, "^\\\\Chunk{ *([^ ,}]*),?(.*)}", line)) {
279 next_chunk_name = line[1];
280 get_chunk_args(line[2], next_chunk_args);
282 next;
284 /^\\begin{lstlisting}|^\\begin{Chunk}/ {
285 if (match($0, "}.*[[,] *name= *{? *([^], }]*)", line)) {
286 new_chunk(line[1]);
287 } else {
288 new_chunk(next_chunk_name, next_chunk_args);
290 chunking=1;
291 next;
293 /^[<]<.*[>]>=/ {
294 if (match($0, "^[<]<(.*)[>]>= *$", line)) {
295 chunking=1;
296 notangle_mode=1;
297 new_chunk(line[1]);
298 next;
301 /^\\[e]nd{lstlisting}|^\\[e]nd{Chunk}/ {
302 chunking=0;
303 active_chunk="";
304 next;
306 /^@ *$/ {
307 chunking=0;
308 active_chunk="";
310 ! chunking { next; }
311 length(active_chunk) {
312 if (length(tabs)) {
313 gsub("\t", tabs);
315 chunk = $0;
316 indent = 0;
317 while(match(chunk,
318 "([=]<\\\\chunkref{([^}>]*)}(\\(.*\\)|)>|<<([a-zA-Z_][-a-zA-Z0-9_]*)>>)",
319 line)\
321 chunklet = substr(chunk, 1, RSTART - 1);
322 indent += length(chunklet);
323 chunk_line(active_chunk, chunklet);
324 chunk = substr(chunk, RSTART + RLENGTH);
325 if (substr(line[1], 1, 1) == "=") {
326 # chunk name up to }
327 chunk_include(active_chunk, line[2] line[3], indent);
328 } else if (substr(line[1], 1, 1) == "<") {
329 chunk_include(active_chunk, line[4], indent);
330 } else {
331 error("Unknown chunk fragment: " line[1]);
334 chunk_line(active_chunk, chunk);
335 chunk_line(active_chunk, "\n");
337 END {
338 if (debug) {
339 print "------ chunk names "
340 output_chunk_names();
341 print "====== chunks"
342 output_chunks();
343 print "++++++ debug"
344 for (a in chunks) {
345 print a "=" chunks[a];
348 ORS="";
349 if (length(root)) output_chunk(root);
350 else output_chunk_names();
352 function get_chunk_args(text, values,
353 # optional parameters
354 path, # hierarchical precursors
355 # local vars
356 a, name)
358 while(length(text)) {
359 if (match(text, "^ *}(.*)", a)) {
360 return a[1];
362 if (! match(text, " *([^,=]*[^,= ]) *(([,=]) *(([^,}]*) *,* *(.*))|)$", a)) {
363 return text;
365 name=a[1];
366 if (a[3] == "=") {
367 if (substr(a[4],1,1) == "{") {
368 text = get_chunk_args(substr(a[4],2), values, path name SUBSEP);
369 } else {
370 values[path name]=a[5];
371 text = a[6];
373 } else {
374 values[path name]="";
375 text = a[2];
378 return text;