]> thyrsus.com Git - mad.git/commitdiff
All variables are global.
authorEric S. Raymond <esr@thyrsus,com>
Sat, 14 Feb 2004 06:41:44 +0000 (06:41 +0000)
committerEric S. Raymond <esr@thyrsus,com>
Sat, 14 Feb 2004 06:41:44 +0000 (06:41 +0000)
doc/mad-manual.xml
mad

index 147c5fc4bb7280194d8ff9afbeb1d93ef9d8df3a..a5413f7ee3d3f0db5d811976453c3a5d04053974 100644 (file)
@@ -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.</para>
+emulated in most later designs.  And, as the Simpson's Rule example
+illustrates, MAD even supported what was later called
+<quote>higher-order programming</quote> &mdash; passing around
+function-valued variables!</para>
 
 <para>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.</para>
 
-<para>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.</para>
+<para>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.</para>
 
 <para>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.</para>
+computed-transfer statement are not implemented.  Neither are multiple
+ENTRY points, or TAPE and DRUM statements.</para>
+
+<para>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.</para>
 
 <para>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.</para>
 
 <para>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.</para>
+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.</para>
 
 <para>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.</para>
 
+<para>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.</para>
+
 <para>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 dcd869148985aafb93dd3a37c3f566c1a7c66be5..7302d99ad209fcdcb807b27b591e14bbcc19d847 100755 (executable)
--- 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