# jsp.awk -- # a script to simplify hand-writing code for the JavaSketchpad applet # within HTML pages # # Version 2.8 # Copyright (C) 1997, 1998, 1999, 2009 Sebastian Lisken # mail: Uni Bielefeld, FSP Mathematisierung, # Postfach 100131, 33501 Bielefeld, Federal Republic of Germany # email: lisken@Mathematik.Uni-Bielefeld.DE # WWW: http://www.Mathematik.Uni-Bielefeld.DE/~lisken/jsp/ # # Please keep the attribution in this notice and in jsp.awk's output intact # if you use or redistribute the program in original or modified form. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should be able to find a copy of the GNU General Public License # on the World Wide Web; if not, write to the # Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # WWW address for the GNU General Public License at the time of writing: # http://www.gnu.org/copyleft/gpl.html # # - End of copyright notice - # this should be a built-in, single AWK command # function clear (array) \ { # this is canonical ... # for (key in array) delete array [key] # this is tricky, but portable split ("", array) } # add1 # # adds a value to a numbered array. # The array's entries 1..n are filled. # The value n is stored in array [0]. # function add1 (array, value) \ { array [++ array [0]] = value } # normalize # # Implements "normalization" as used in add2, getkey and existskey, # determining what is to be ignored in the arrays implemented with # those functions. (Case, spaces, punctuation, ...) # function normalize (string) { string = tolower (string) gsub ("[^a-z0-9]", "", string) return string } # add2 # # adds a value to a string-indexed array with "normalized" lookup. # A second array, keytable, holds the size and order of the array # as well as the "normalized" keys. # function add2 (array, key, value, keytable) \ { okey = getkey(key, keytable) if (! (okey in array)) { add1(keytable, okey) } array [okey] = value } # getkey # # adds a key to a table of "normalized" keys, # or gets the original version if it already exists. # function getkey (key, keytable) \ { if (! existskey(key, keytable)) { keytable [nkey] = key } return keytable [nkey] } # existskey # # looks for a key in a table of "normalized" keys. # Sets a variable "nkey" that provides access to the "normalized" version. # function existskey (key, keytable) \ { nkey = "_" normalize(key) return (nkey in keytable) } # the following two functions implement a line buffer # # get_line replaces AWK's getline # function get_line() \ { if (buffer == "") { return (getline) > 0 } else if ((n = index (buffer, "\n")) > 0) { $0 = substr (buffer, 1, n-1) buffer = substr (buffer, n+1) } else { $0 = buffer buffer = "" } return 1 } # # # buffer_line places a line before the rest of the input # function buffer_line (line) \ { buffer = line "\n" buffer } # # # buffer_file reads a file and places it into the input stream # function buffer_file (filename) { filestatus = 1 fileline = "" filebuffer = "" file_content = "" file_sep = "" while (filestatus > 0) { if (filebuffer != "") { fileline = filebuffer filebuffer = "" } else { n = (getline fileline < filename) if (n < 0) add_warning_noobj("error reading file '" filename "'") if (n <= 0) filestatus = 0 } if (filestatus == 1) { if ((n = index (fileline, "")) > 0) { filestatus = 2 fileline = substr (fileline, n+5) sub ("[ \t]+$", "", fileline) if (fileline == "") continue } } if (filestatus > 1) { if ((n = index (fileline, "")) > 0) { filestatus = 1 filebuffer = substr (fileline, n+6) fileline = substr (fileline, 1, n-1) sub ("[ \t]+$", "", fileline) if (fileline == "") continue } file_content = file_content file_sep fileline file_sep = "\n" } } close (filename) buffer_jsp_line(file_content) file_content = "" } # no_further_jsp_line # # tries to fetch the next line from the JSP section of the input. # Return value: 0 on success, 1 on failure. # function no_further_jsp_line () \ { if (status < 0) { return 1 } else if (! get_line()) { return 1 } else { # for debugging purposes, needs UNIX-style cat and stderr # print "*" NR "* " $0 | "cat >&2" check_jsp_line() # if this is the last line and it's empty, we'll say no return (status < 0 && $0 == "") } } # check_jsp_line # # checks if this is the last line of the applet # function check_jsp_line () \ { # is there a tag? if ((n = index (toupper ($0), "")) > 0) { # remember text following it after_applet = substr ($0, n + 6) # restrict analysis to text preceding it $0 = substr ($0, 1, n-1) # set status status = -status } } # complete_jsp_line # # extends JSP lines that end in a backslash. # function complete_jsp_line () \ { while (match ($0, "[ \t]*[\\\\]$")) { $0 = line1 = substr ($0, 1, RSTART - 1) if (no_further_jsp_line()) { break } if (NF > 0) { $0 = line1 " " substr ($0, index ($0, substr ($1, 1, 1))) } else { $0 = line1 } } } # complete_fields # # extends JSP lines to a minimum number of fields # (expects that complete_jsp_line has already been called) # function complete_fields (min_fields) \ { while (NF < min_fields) { line2 = $0 if (no_further_jsp_line()) { break } complete_jsp_line() if (NF > 0) { $0 = line2 "\t" substr ($0, index ($0, substr ($1, 1, 1))) } else { $0 = line2 } } } # buffer_jsp_line # # calls buffer_line and adjusts status function buffer_jsp_line (line) { if (status < 0) { status = -status line = line "" after_applet } buffer_line(line) } # make_jsp_line # # As JSP under Java 6 can't handle tabs, we need to expand to 8 spaces. function make_jsp_line (number, content) { jsp_line = "{" number "}" jsp_line = jsp_line substr (" ", length (jsp_line) + 1) content sub ("[ \t]+$", "", jsp_line) return jsp_line } # get_directive # # parses a directive to the converter. # # If the directive is an assignment of something to a name, the function # stores the assignment in aparam or jparam, depending on the name. # # If the directive is an "include" statement, it tries to # read the file named in the statement and buffers it. # function get_directive (string) \ { if (match (string, "^[A-Za-z0-9_]+[ \t]*[=][ \t]*.+$")) { match (string, "[ \t]*[=][ \t]*") name = substr (string, 1, RSTART - 1) value = substr (string, RSTART + RLENGTH) sub ("[ \t]+$", "", value) if (tolower (name) == "construction") return if (tolower (name) == "background") { if (split (value, part, "[ ,]+") == 3) { add2(jparam, "BackRed", part [1], jparams) add2(jparam, "BackGreen", part [2], jparams) add2(jparam, "BackBlue", part [3], jparams) return } } if (existskey(name, jparams)) { add2(jparam, name, value, jparams) } else { add2(aparam, name, value, aparams) } } else if (match (string, "^[ \t]*include[ \t+]\"[^\"]+\"[ \t]*$")) { match (string, "\".*\"") buffer_file(substr (string, RSTART + 1, RLENGTH - 2)) } } # this records a warning # function add_warning (message) \ { add1(warning, NR "\n" this_object "\n" message) } # this records a warning for a line that does not produce an object # function add_warning_noobj (message) \ { add1(warning, NR "\n-\n" message) } # this records the number of the last object created # function record_object (jsp_reference, pseudo_object) \ { jsp_object [this_object] = jsp_reference if (pseudo_object) pseudo_objects [this_object] = "" } # this checks and returns an earlier recorded object # function get_object (jsp_table, reference) \ { entry = jsp_table [reference] if (substr (entry, 1, 1) == "?") { add_warning("using failed reference") } return entry } # convert_refs # # handles all that $ business # the macro parameter tells the function to substitute just macro parameters # the macro_call parameter signals that we are converting parameters # to a macro call, this changes the interpretation of relative numbers function convert_refs (refstring, macro, macro_call) \ { parts = split (refstring, part, "[$]") # start with the first part, preceding any $ result = part [1] for (j = 2; j <= parts; ++j) { this_part = part [j] # this part of the string follows a $ sign if (match (this_part, "^[!]?" namepat)) { # it's a name - which one? nameref = substr (this_part, 1, RLENGTH) # using ! in name substitution is irrelevant if (substr (nameref, 1, 1) == "!") { nameref = substr (nameref, 2) extra = "!" } else { extra = "" } # get the associated string and put that into the part if (macro) { if (nameref in macro_param) { replacement = macro_param [nameref] } else { replacement = "$" extra nameref } } else { if (nameref in named_object) { replacement = get_object(named_object, nameref) } else { add_warning("Name '" nameref "' unknown") replacement = "?" nameref "?" } } this_part = replacement substr (this_part, RLENGTH + 1) } else if (macro) { # For macros, we don't want any other $s this_part = "$" this_part } else if (match (this_part, "^" numpat)) { # it's a relative number offset = substr (this_part, 1, RLENGTH) + 0 # convert to absolute and put it back into the part other_object = this_object - offset + macro_call if (other_object in jsp_object) { replacement = get_object(jsp_object, other_object) } else { add_warning("Relative number out of range: " offset) replacement = "?" offset "?" } this_part = replacement substr (this_part, RLENGTH + 1) } else if (this_part == "") { # it's an empty part - we've found two $'s. Keep one. this_part = "$" (j < parts ? part [++j] : "") } else { # We don't know what it is - better hand the $ back this_part = "$" this_part } # add the adjusted part result = result this_part } return result } # parse_object # # parses a string of the form 'object-name (parameter, ...)' # As parameters, a list containing object references, numeric constants # and strings is expected, separated by commas and white space. # (Object references in the form they have before $ processing.) # # Parameters: # objstring is a string representing the parameter list. # just_names, when true, tells the function to expect just names in the list. # allow_special tells it to accept anything in double quotes as a parameter, # removing the quotes. # # Returns 1 on success, 0 otherwise. # Parameters are left in array 'param'. # param [0] holds n, the number of parameters. # param [1] .. param [n] are the parameters found. # param ["rest"] is the rest of the parsed string, minus white space. # param ["name"] is whatever came before the parameter list. # function parse_object (objstring, just_names, allow_special) \ { match (objstring, "[ \t]*([([{;]|$)") param ["name"] = substr (objstring, 1, RSTART-1) rest = substr (objstring, RSTART) sub ("^[ \t]*", "", rest) param [0] = 0 if (substr (rest, 1, 1) != "(") { param ["rest"] = rest return 0 } success = 1 rest = substr (rest, 2) quote = allow_special ? "'\"" : "'" while (1) { sub ("^[ \t]*", "", rest) if (just_names) { if (match (rest, "^[$]?" namepat "[" parend "]")) { pname = substr (rest, 1, RLENGTH - 1) if (substr (pname, 1, 1) == "$") { pname = substr (pname, 2) } add1(param, pname) rest = substr (rest, RSTART + RLENGTH - 1) } else { success = 0 break } } else { if (match (rest, "^" parpat "[" parend "]")) { add1(param, substr (rest, 1, RLENGTH - 1)) rest = substr (rest, RSTART + RLENGTH - 1) } else if (index (quote, (q = substr (rest, 1, 1))) > 0) { string = "" while ((n = index (substr (rest, 2), q)) > 0) { n += 1 string = string substr (rest, 1, n) rest = substr (rest, n+1) if (substr (rest, 1, 1) != q) { break } } if (n > 0 && index (parend, substr (rest, 1, 1)) > 0) { add1(param, string) } else { success = 0 rest = string rest break } } else { success = 0 break } } sub ("^[ \t]*", "", rest) c = substr (rest, 1, 1) if (c == ")") { rest = substr (rest, 2) break } else if (c != ",") { success = 0 break } rest = substr (rest, 2) } sub ("^[ \t]*", "", rest) param ["rest"] = rest return success } # this prepares for a new applet # function init_applet () \ { # aparam/aparams is a string-indexed array of the tag's params # (case-insensitive lookup) clear(aparam) clear(aparams) aparams [0] = 0 # jparam/jparams is a string-indexed array of the params # (case-insensitive lookup) clear(jparam) clear(jparams) jparams [0] = 0 # cline holds the construction as an array of lines clear(cline) # named_object is an unordered, string-indexed array of named JSP objects clear(named_object) # jsp_object holds, for any object in the extended JSP language, # the number of the corresponding (original) JSP object clear(jsp_object) this_jsp_object = 0 # for these extended JSP objects that are pseudo objects, # we will create an entry in pseudo_objects (the entry is just "") clear(pseudo_objects) pseudo_objects ["-"] = "" # warnings generated in the conversion process clear(warning) # macro definitions clear(macro) # standards for aparam add2(aparam, "CODEBASE", "\"" \ (length (codebase) ? codebase : \ "http://www.keypress.com/sketchpad/java_gsp/JSP/") \ "\"", aparams) add2(aparam, "ARCHIVE", "\"" (length (archive) ? archive : "JSPDR3.jar") "\"", aparams) add2(aparam, "CODE", "\"GSP.class\"", aparams) add2(aparam, "WIDTH", "200", aparams) add2(aparam, "HEIGHT", "200", aparams) # standards for jparam add2(jparam, "Offscreen", "1", jparams) add2(jparam, "Frame", "1", jparams) # make other applet params known getkey("BackRed", jparams) getkey("BackGreen", jparams) getkey("BackBlue", jparams) getkey("LabelFont", jparams) getkey("LabelSize", jparams) getkey("LabelBold", jparams) getkey("LabelItalic", jparams) getkey("ActionFont", jparams) getkey("ActionSize", jparams) getkey("ActionBold", jparams) getkey("ActionItalic", jparams) getkey("MeasureFont", jparams) getkey("MeasureSize", jparams) getkey("MeasureBold", jparams) getkey("MeasureItalic", jparams) getkey("MeasureInDegrees", jparams) getkey("DirectedAngles", jparams) # reset apology and comment mechanisms apol_delim = "" comment_level = 0 } # clean up after processing an applet, # continue scanning with what followed the closing tag # on the same line # function clear_applet () \ { status = 0 # most actual cleaning work is left to the next init_applet() call buffer_line(after_applet) } # do whatever checks are necessary before printing a complete applet # function close_applet () \ { if (comment_level > 0) { add_warning_noobj(sprintf \ ("%d comment%s still open at ", \ comment_level, comment_level == 1 ? "" : "s")) } } # this will print the applet after it has been read in # function print_applet () \ { printf "" print "" for (i = 1; i <= jparams [0]; ++i) { key = jparams [i] printf ("\n", key, jparam [key]) } print "" print " 0) { if (last_length) print "" print "{ Warnings generated by jsp.awk: }" for (i = 1; i <= warnings; ++i) { split (warning [i], part, "\n"); if (part [2] in pseudo_objects) { jsp_reference = "[none]" } else { jsp_reference = jsp_object [part [2]] } printf "{ %-72s }\n", \ sprintf ("input line %3d: output object %-7s %s", \ part [1], sprintf ("%3s:", jsp_reference), part [3]) } last_length = 1 } if (last_length) print "" printf "{ %-72s }\n", "JavaSketchpad code " \ "generated by Sebastian Lisken's jsp.awk script --" printf "{ %-72s }\n", \ "see http://www.Mathematik.Uni-Bielefeld.DE/~lisken/jsp/" print "\">" print "" print length (apol_delim) ? apology : std_apology printf "" } # finish_line # # does things before abandoning the rest of the script # and starting the next line. # In other words, finish_line will be called just before AWK's 'next' command. # function finish_line () \ { # must we print the applet? if (status < 0) { close_applet() print_applet() # there might be another one later in the HTML section clear_applet() } } # buffer_macro # # inserts the body of a macro found in the current line into the stream # # This procedure has been separated for the sake of code readability. # It is only called once and the expects the following: # - The line calling the macro has been parsed using parse_object # - The macro parameter names have been split into the 'mpname' array, # with a separate variable 'mpnames' holding their number # - The macro lines have been spit into the 'mline' array, # with a separate variable 'mlines' holding their number # - The string 'name' signals if a name has been given to the macro call # function buffer_macro () \ { # assign parameters clear(macro_param) references = "" d = "" for (i = 1; i <= mpnames; ++i) { if (0) { # # Previous versions gave special treatment to referential # macro parameters. That behaviour has been found to be # non-intuitive. Advantage: There is now a rule that names # used in macro parameter lists are local, while any other # names defined in macros are global. Slight disadvantage: # Earlier, object references passed to macros could be referred # to by the name used in the macro parameter list after the # macro call. Thus, there was a notion of "the object passed # as x in the previous call to macro m" which could be # expressed simply as $x. Now you have to actually look at # that previous macro call and copy the reference used there. # # if (match (param [i], "^(" refpat1 "|" numpat ")$")) { ## implement jsp.awk and standard references with 'Reference's # references = references d \ # "$!" mpname [i] "\t" refname " (" param [i] ");" # d = "\n" } else { mparam = param [i] if (match (mparam, "^" refpat1 "$")) { # without special treatment to referential parameters, # we have to evaluate them before passing mparam = convert_refs(mparam, 0, 1) } else if (substr (mparam, 1, 1) == "\"") { # implement other macro parameters by macro substitution # treat special macro parameters (those in double quotes) mparam = substr (mparam, 2, length (mparam) - 2) } # transfer the value into the macro_param array macro_param [mpname [i]] = mparam } } # put any formats or comments into the last macro line if (match (param ["rest"], "^" eolpat3)) { last_line = mline [mlines] if (! match (last_line, eolpat2)) { RSTART = length (last_line) + 1 } last_line = substr (last_line, 1, RSTART - 1) # append a blank, if required connection = substr (last_line, length (last_line), 1) \ substr (param ["rest"], 1, 1) if (! match (connection, "[ \t]")) { last_line = last_line " " } # append the format / comment last_line = last_line param ["rest"] mline [mlines] = last_line } # give a name to the macro result, if desired if (name != "") { mline [++mlines] = $1 "\t" refname " ($1);" } # put the macro body into the stream, substituting params for (i = mlines; i > 1; --i) { buffer_jsp_line(convert_refs(mline [i], 1, 0)) } if (references != "") buffer_jsp_line(references) } BEGIN { namepat = "[A-Za-z_][A-Za-z0-9_]*" numpat = "[1-9][0-9]*" rnumpat = "[+-]?([0]|" numpat ")(.[0-9]+)?" # This matches a jsp.awk object reference. refpat1 = "[$]([!]?" namepat "|" numpat ")" # This matches an object reference after $ processing by jsp.awk. # refpat2 = "(" numpat "|([?](" numpat "|" namepat ")[?]))" # This matches any (macro) parameter other than a string. parpat = "(" refpat1 "|" rnumpat ")" # These characters occur after a parameter (see parse_object) parend = " \t,)" # Two patterns for the part of a construction line after the ')'. # One lenient, but without formats, one strict with formats. eolpat1 = ";?[ \t]*([{][^}]*[}][ \t]*)?$" eolpat2 = "([[][^]]*[]])?;[ \t]*([{][^}]*[}][ \t]*)?$" # The last pattern, specifically asking for a "[...]" format eolpat3 = "[[][^]]*[]];[ \t]*([{][^}]*[}][ \t]*)?$" # what to say to Java non-drinkers std_apology = "This page requires a Java capable browser." # The name of jsp.awk's 'Reference' pseudo-object # (maybe it has been changed in the language file) refname = "Reference" # Load the language file, if any if (length (jsplang)) { while ((getline langint < jsplang) > 0) { # Parse an entry sub ("^[ \t]+", "", langint) if (length (langint) < 1 || substr (langint, 1, 1) == "#") { continue } if ((n = index (langint, "\t")) > 0) { langext = substr (langint, n+1) gsub ("((^[ \t]+)|([ \t]+$))", "", langext) langint = substr (langint, 1, n-1) } else { langext = "" } sub ("[ \t]+$", "", langint) # Add it to the language table if (length (langext)) { translation = normalize(langext) } else { translation = normalize(langint) } langentry [translation] = langint # Remember any translation of the "Reference" pseudo-object if (langint == "Reference") { refname = translation } } close (jsplang) } # status: 0 for HTML section, other for JSP section # negative if the closing tag has been found in this line # status's absolute value is: # 1 if no construction line has yet been found in the JSP section # 2 otherwise (all that changes is we record empty lines) status = 0 # # We're replacing AWK's line-reading mechanism # buffer = "" while (get_line()) { # # following patterns converted to if conditions -- # can be recognized by the fact that they're _not_ indented by a tab # # 'next' statements converted to 'continue', comment added # # for debugging purposes, needs UNIX-style cat and stderr # { print "*" NR "* " $0 | "cat >&2" } # before the JSP section - copies line and looks for if (! status) \ { # is there a tag? if ((n = index (toupper ($0), "")) > 0) { # copy text preceding the tag printf "%s", substr ($0, 1, n-1) # restrict to text after and proceed with JSP analysis $0 = substr ($0, n + 5) init_applet() status = 1 } else { print # no finish_line required outside JSP section continue # converted 'next' statement } } # within JSP section - checks if this is the last line of the applet { check_jsp_line() } # empty line - record it if part of the actual construction if (NF == 0) \ { if (status > 1) { add1(cline, $0) } finish_line() continue # converted 'next' statement } # our own comments mechanism (as JSP's {} comments aren't handled reliably) # 1. See if a comment level is opened or closed if (NF == 1 && match ($1, "^[$][{}]$")) { comment_level -= index ("{*}", substr ($1, 2, 1)) - 2 if (comment_level < 0) { add_warning_noobj("Too many comment closures") comment_level = 0 } continue # converted 'next' statement } # 2. Don't transfer parts inside comments to the applet if (comment_level > 0) { continue # converted 'next' statement } # get first character c and rest of line (minus white space) { c = substr ($1, 1, 1) rest = substr ($0, index ($0, c) + 1) sub ("^[ \t]*", "", rest) } # this line sets a parameter if (c == "#" || c == "*") \ { get_directive(rest) finish_line() continue # converted 'next' statement } # Here's how parameters were handled earlier. # It used to be the leading character (#/*) that determines what type # of parameter we have, now we actually look at the parameter name. # # # this is a line modifying the tag # if (c == "#") \ # { # get_directive(rest, aparam, aparams) # finish_line() # continue # converted 'next' statement # } # # no, it's a line containing an applet # if (c == "*") \ # { # get_directive(rest, jparam, jparams) # finish_line() # continue # converted 'next' statement # } # no, sorry: it's one (more) line of apologetic text if (c == ":") \ { apology = apology apol_delim rest apol_delim = "\n" finish_line() continue # converted 'next' statement } # ah, it's part of the actual construction { status = status > 0 ? 2 : -2 complete_jsp_line() } # don't parse lines that don't actually say anything # skip one leading numerical comment (as produced by the GSP->HTML converter) if (c == "{") \ { if (match ($0, "}[ \t]*$")) { add1(cline, $0) finish_line() continue # converted 'next' statement } if (match (rest, "^[0-9]+}[ \t]*")) { $0 = substr (rest, RSTART + RLENGTH) } } # Before the real action, handle macro definitions if ($1 == "$(") \ { # Read name and parameters into the same line, then delete $( tag complete_jsp_line() complete_fields(2) if (NF < 2) { finish_line() continue # converted 'next' statement } tag = $1 $0 = substr ($0, index ($0, substr ($1, 1, 1)) + length ($1)) # record macro name and parameters ... macro_def = $0 sub ("^[ \t]*", "", macro_def) # .. by ensuring we have a parsable line without rest success = parse_object(macro_def, 1, 0) if (success) success = match (param ["rest"] " ", "^;?[ \t]*$") if (! success) { # not a correct definition -- # treat the tag like any other object and buffer the rest this_jsp_object += 1 # store the tag's number record_object(this_jsp_object, 0) # record its construction line add1(cline, make_jsp_line(this_jsp_object, tag)) add_warning("invalid macro declaration") buffer_jsp_line($0) finish_line() continue # converted 'next' statement } # put name into a variable macro_name = param ["name"] # put parameters into first macro line, separated by tabs macro_def = "" d = "" params = param [0] for (i = 1; i <= params; ++i) { pname = param [i] # a leading $ might have been given by mistake sub ("^[$]", "", pname) macro_def = macro_def d pname d = "\t" } # add macro body, stop at line consisting just of "$)" # we won't record empty lines while (1) { if (no_further_jsp_line()) { break } complete_jsp_line() if (NF == 1 && $1 == "$)") { break } if (NF == 0) { continue } # we'll record this line first_line = 0 # enforce names defined here to be temporary if (match ($1, "^[$]" namepat "$")) { sub ("[$]", "$!", $0) } macro_def = macro_def "\n" $0 } # put all the lines into the macro array macro [macro_name] = macro_def finish_line() continue # converted 'next' statement } # The real action! { # is a name given to the object? if (match ($1, "^[$][!]?" namepat "$")) { # ensure that there's actually an object following the name # (Don't put a comment and no object after a name.) complete_fields(2) if (NF < 2) { finish_line() continue # converted 'next' statement } # get the name and the rest of the line name = substr ($1, 2) check_reuse = (substr (name, 1, 1) != "!") name = substr (name, check_reuse ? 1 : 2) construction = \ substr ($0, index ($0, substr ($1, 1, 1)) + length ($1)) } else { # no name given to this one name = "" construction = $0 } # clear initial white space sub ("^[ \t]*", "", construction) # extract and translate the construction name match (construction, "[ \t]*([([{;]|$)") cname = substr (construction, 1, RSTART-1) ncname = normalize(cname) unknown = 0 if (ncname in langentry) { cname = langentry [ncname] construction = cname substr (construction, RSTART) } else if (length (jsplang)) { unknown = 1 } # recognize and treat macros; recognize 'Reference' lines is_reference = 0 if (cname in macro) { if (parse_object(construction, 0, 1)) { mlines = split (macro [cname], mline, "\n") mpnames = split (mline [1], mpname, "\t") if (param [0] == mpnames) { # Handle macros here: if (mlines > 1) { buffer_macro() } # nothing more to do for this line finish_line() continue # converted 'next' statement } } } else if (cname == "Reference") { if (parse_object(construction, 0, 0)) { # Leave references until later. is_reference = \ (param [0] == 1 \ && match (param [1], "^(" refpat1 "|" numpat ")$") \ && match (param ["rest"], "^" eolpat1)) } } # We have an object! (Possibly pseudo.) this_object += 1 # Did we expect it in the language file and not find it? if (unknown) { add_warning("Construction '" cname "' not in JSP language") } # Have we got a reference? if (is_reference) { # get the number of the object being referred to jsp_reference = convert_refs(param [1], 0, 0) # store that number record_object(jsp_reference, 1) # don't record this construction line } else { # we have found a new (original) JSP object this_jsp_object += 1 # process references construction = convert_refs(construction, 0, 0) # store its number record_object(this_jsp_object, 0) # record its construction line add1(cline, make_jsp_line(this_jsp_object, construction)) } # if there was a name, remember it if (length (name)) { # record the number of the (original) JSP object thus named if (check_reuse && name in named_object) { add_warning("Name '" name "' reused") } named_object [name] = jsp_object [this_object] } # for debugging purposes, needs UNIX-style cat and stderr # { print "* constructed" | "cat >&2" } finish_line() continue # converted 'next' statement } # # end of the while loop that replaced AWK's line-reading mechanism # } # end of BEGIN }