From b7b36f27b74378038d9f5f761dfdc59eede38d3a Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sat, 14 Feb 2004 06:41:44 +0000 Subject: [PATCH] All variables are global. --- doc/mad-manual.xml | 41 +++++++++++++++++------- mad | 79 ++++++++++++++++++++++------------------------ 2 files changed, 66 insertions(+), 54 deletions(-) diff --git a/doc/mad-manual.xml b/doc/mad-manual.xml index 147c5fc..a5413f7 100644 --- a/doc/mad-manual.xml +++ b/doc/mad-manual.xml @@ -100,7 +100,10 @@ rather advanced for its period. The multiline WHENEVER statement anticipated the free-format syntax of ALGOL 60's descendants, including Pascal and C. The DEFINE statement, which supports syntactic extension of the language, was an interesting experiment not -emulated in most later designs. +emulated in most later designs. And, as the Simpson's Rule example +illustrates, MAD even supported what was later called +higher-order programming — passing around +function-valued variables! In most of the respects we've described, however, MAD is very typical of its time. This will become apparent from a look at the @@ -135,16 +138,20 @@ be usual in C. Perfect fidelity to the MAD code would have required the data to be unpacked into int or double arrays, but that code would have been far more complicated to generate and hard to read. -In original MAD all variables were global, and arguments were -passed by reference. In the interests of generating reasonably clean -C, this implementation makes variables local to the functions in which -they occur and passes arguments by value. Exploiting the original MAD -behaviors was considered bad style even then, and none of the examples -given in the manual rely on them. +In original MAD arguments were passed by name. This is +difficult to simulate in C, so the generated code passes arguments by +value. None of the examples given in the manual rely on +pass-by-reference, and given that all variables are global it is +difficult to imagine code that would. Label arrays, array-mode variables, and the associated -computed-transfer statement are not implemented. Neither are ERROR -RETURN, multiple ENTRY points, or TAPE and DRUM statements. +computed-transfer statement are not implemented. Neither are multiple +ENTRY points, or TAPE and DRUM statements. + +Only the no-argument case of ERROR RETURN is supported. The +language describing the more general case of this feature is deeply +confusing, and I was unable to extract a coherent semantics for +it. None of the examples use the general case. On the other hand, some limits in original MAD are not present in this implementation, mainly because it would have required actual @@ -179,14 +186,24 @@ visible rendering you see, though hyphen characters in the original have been preserved as ISO soft-hyphen entities. The program listings in the original failed to distinguish -between 0 and O; this does not reflect incompetence but rather the -fact that typewriters of the period did not have a separate 0 key. -This distinction has been restored. +between 0 and O, and between 1 and l; this does not reflect +incompetence but rather the fact that although computer line printers +of the period had separate forms for these character pairs, +typewriters of the period did not have. This distinction has been +added in the present text. Some program listings in the original were missing continuation punches required by the language. These have been corrected, and the corrections noted in XML coments. +Most of the original listings were typed so there was whitespace +between remark or continuation punches and following statements or +comments. The language's card-based, column-sensitive format did not +require this, however, and a few listings had a remark or continuation +character stuck to the first statement token. These have been +whitespace-separated so that a modern token-oriented parser can read +them. + In the original, the description of flowcharting conventions in Chapter 1 include small hand-drawn geometrical shapes after each of the items A, B, and the words "diamonds" and "rectangles" in item diff --git a/mad b/mad index dcd8691..7302d99 100755 --- a/mad +++ b/mad @@ -500,13 +500,9 @@ def croak(t, msg): sys.stderr.write('"%s", line %d: %s\n' % (file, line, msg)) # All MAD variables are global, you have to simulate locals using SAVE DATA and -# RETURN DATA. Thus most of the variable lookup tables about are straight -# dictionaries not qualified by current function name. typedict is an -# exception; we use this to generate per-function variables as C auto -# variables and get nicer C code. This would break if two MAD external -# functions deliberately shared a global, but this doesn't happen in -# any of the examples. -typedict = {} # Dictionary of per-function dictionaries of locals +# RETURN DATA. Thus the variable lookup tables about are straight +# dictionaries not qualified by current function name. +typedict = {} # Types of globals normalmode = 'double' # Default type as set by NORMAL statement dummycount = 0 # Count of FOR VALUES statements @@ -531,17 +527,16 @@ def analyze(): for (file, line, label, statement) in deck: if statement[0] == 'boolean': for var in statement[1:]: - typedict[currentfunc][var] = 'bool' + typedict[var] = 'bool' elif statement[0] == 'dimension': for (var, dimlist) in statement[1]: dimensions[var] = dimlist elif statement[0] == 'entry': currentfunc = statement[1] funcline = line - typedict[currentfunc] = {} elif statement[0] == 'integer': for var in statement[1:]: - typedict[currentfunc][var] = 'int' + typedict[var] = 'int' elif statement[0] == 'normal': global normalmode normalmode = statement[1] @@ -593,9 +588,9 @@ def analyze(): (fmt, types) = fmt_convert(format[statement[1]]) for (vtype, var) in zip(types, statement[2:]): if type(var) == type(()) and var[0] == 'range': - typedict[currentfunc][var[1]] = vtype + typedict[var[1]] = vtype else: - typedict[currentfunc][var] = vtype + typedict[var] = vtype # Type inference loop. We initialized for this by deducing types # from READ FORMAT statements. INTEGER, and BOOLEAN declarations # above. Now propagate markers of non-normalmode type through @@ -620,13 +615,15 @@ def analyze(): deductions += 1 elif statement[0] == 'restore': for var in statement[1]: - typedict[currentfunc][var] = normalmode + if var not in typedict: + typedict[var] = normalmode + deductions += 1 elif statement[0] == 'substitution': # Propagate type markers through assignment var = statement[1] m = mode(statement[2]) - typedict[currentfunc][var] = m - if m != normalmode and typedict[currentfunc][statement[1]]!=m: + typedict[var] = m + if m != normalmode and typedict[statement[1]]!=m: deductions += 1 if deductions == 0: break @@ -653,8 +650,8 @@ def mode(t): "Compute the return type of an expression" #print "->mode", t if t[0] == 'variable': - #print "<-mode of ", t[1], typedict[currentfunc].get(t[1], normalmode) - return typedict[currentfunc].get(t[1], normalmode) + #print "<-mode of ", t[1], typedict.get(t[1], normalmode) + return typedict.get(t[1], normalmode) elif t[0] in ('p', 'abs') or t[0] in ('+', '-') and len(t) == 2: #print "<-mode of ", t[1] return mode(t[1]) @@ -865,36 +862,22 @@ def print_C_statement(statement): return "} else {" elif statement[0] == 'entry': currentfunc = statement[1] - types = typedict[currentfunc] rep = "\n" + returntypes.get(currentfunc, normalmode) + " " + currentfunc[:-1] + "(" defaulted = [] if len(statement) > 2: for (dummy, var) in statement[2]: declarator = ("*" * arraydepths.get(var, 0)) + var - if var in types: - rep += types[var] + " " + declarator + ", " - del types[var] + if var in typedict: + rep += typedict[var] + " " + declarator + ", " + del typedict[var] else: rep += normalmode + " " + declarator + ", " defaulted.append(var) - rep = rep[:-2] + if rep.endswith(", "): + rep = rep[:-2] rep = rep + ") {" #if defaulted: # sys.stderr.write("warning: defaulted base type for %s\n" % ", ".join(defaulted)) - declarations = "\n" - for vtype in ('double', 'int', 'bool', 'char'): - vars = filter(lambda x: types[x] == vtype, types.keys()) - if vars: - declarations += vtype - for var in vars: - declarations += " " + var - if var in dimensions: - for dim in dimensions[var]: - declarations += "[" + print_C_expr(dim) + "]" - declarations += "," - declarations = declarations[:-1] + "; " - if declarations: - rep += " " + declarations[:-1] return rep elif statement[0] == 'error_return': return 'puts("ERROR RETURN"); exit(1);' @@ -961,7 +944,7 @@ def print_C_statement(statement): popoff = list(statement[1]) popoff.reverse() for var in popoff: - rep += "%s = (%s)(*--recursor); " % (var, typedict[currentfunc].get(var, normalmode)) + rep += "%s = (%s)(*--recursor); " % (var, typedict.get(var, normalmode)) return rep; elif statement[0] == 'return': if len(statement) == 1: @@ -991,16 +974,28 @@ def generate_C_code(out): out.write('#include <%s.h>\n' % header) if headers: out.write("\n") - needbool = False - for funvars in typedict.values(): - if 'bool' in funvars.values(): - needbool = True - if needbool: + if 'bool' in typedict.values(): out.write("typedef int bool;\n") for (id, value) in static.items(): out.write('static const char *%s\t= "%s";\n' % (id,fmt_convert(value))) + declarations = "" + for vtype in ('double', 'int', 'bool', 'char'): + vars = filter(lambda x: typedict[x] == vtype, typedict.keys()) + if vars: + declarations += vtype + for var in vars: + declarations += " " + var + if var in dimensions: + for dim in dimensions[var]: + declarations += "[" + print_C_expr(dim) + "]" + declarations += "," + declarations = declarations[:-1] + "; " + if declarations: + out.write(declarations[:-1]) + out.write("\n") + indent = 0 global sourceline scopes = [] # THROUGH scopes that are pending -- 2.43.0