home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Guide / c-cplusplus-interactive-guide.iso / c_ref / csource3 / 137_01 / sep84col.ddj < prev    next >
Encoding:
Text File  |  1979-12-31  |  20.8 KB  |  676 lines

  1. .pl 61
  2. .po 4
  3. ..
  4. .. this article was prepared for the C/Unix Programmer's Notebook
  5. .. Copyright 1984 Pyramid Systems, Inc.
  6. ..
  7. .. by A. Skjellum
  8. ..
  9. .he "C/Unix Programmer's Notebook"   for September, 1984 DDJ. 
  10.  
  11.  
  12.                                Introduction
  13.  
  14. .. okay
  15.     Past  columns have been devoted to topics about C and Unix as  they 
  16.  
  17.  
  18.  
  19. exist in the real world.   In this column,  I depart from this precedent by 
  20.  
  21.  
  22.  
  23. discussing  some  proposals for increasing the power and flexibility of  C.  
  24.  
  25.  
  26.  
  27. We  work in an evolving field where change  is  inevitable.   Consequently, 
  28.  
  29.  
  30.  
  31. many languages go through regular upgrades and improvements (eg Fortran IV, 
  32.  
  33.  
  34.  
  35. Fortran  66,  and Fortran 77).   C has been more immune than other  popular 
  36.  
  37.  
  38.  
  39. languages.   The  lack  of upgrades is probably due mainly to C's  lack  of 
  40.  
  41.  
  42.  
  43. intrinsic functions,  but clearly points to the basic elegance and power of 
  44.  
  45.  
  46.  
  47. C.  
  48.  
  49. .. okay 
  50.     I  am  aware of only a few upgrades beyond the language  definition 
  51.  
  52.  
  53.  
  54. specified in The C Programming Language (K&R).  These are enumerated  types 
  55.  
  56.  
  57.  
  58. and structure assignment.  Both of these features were introduced with Unix 
  59.  
  60.  
  61.  
  62. version 7 and are detailed in Unix v7 documentation.   Structure assignment 
  63.  
  64.  
  65.  
  66. is  useful  in what follows,  but type enumeration will not  be  mentioned.  
  67.  
  68.  
  69.  
  70. Therefore, while we base our definition on the Unix 7 version of C, this is 
  71.  
  72.  
  73.  
  74. not  likely  to  confuse those who have access only to  The  C  Programming 
  75.  
  76.  
  77.  
  78. Language.
  79.  
  80. .. okay
  81.     Purists  may argue that I have no business recommending changes  or 
  82.  
  83.  
  84.  
  85. upgrades  to  C.   Others  may argue that many of the  suggestions  can  be 
  86.  
  87.  
  88.  
  89. implemented via compiler preprocessors or by function calls and need not be 
  90.  
  91.  
  92.  
  93. part  of  the  language.   (This  second point is discussed  later  in  the 
  94.  
  95.  
  96.  
  97. column.)  In order to head off the critisism that I am 'tampering' with the 
  98.  
  99.  
  100.  
  101. C language, I offer my recommendations as a new language grammar based on C 
  102.  
  103.  
  104.  
  105. but  called 'X.' The letter X was chosen to denote language  extensibility, 
  106.  
  107.  
  108.  
  109. which is the main point of the following proposals.
  110.  
  111.                           Language Extensibility
  112.  
  113. .. okay
  114.     Most  languages allow user defined functions,  subroutines and many 
  115.  
  116.  
  117.  
  118. newer languages allow user defined data types.   Extensible languages  like 
  119.  
  120.  
  121.  
  122. Forth  and APL allow functions,  operators,  and data types  to be added to 
  123.  
  124.  
  125.  
  126. the programming environment in a way that makes them equivalent in  stature 
  127.  
  128.  
  129.  
  130. to  predefined  operations.   While  C retains  tremendous  flexibility  by 
  131.  
  132.  
  133.  
  134. excluding  intrinsic functions,  it does not allow user defined types to be 
  135.  
  136.  
  137.  
  138. treated  as easily as ints,  longs or  floats.   Specifically,  one  cannot 
  139.  
  140.  
  141.  
  142. extend  the definitions of operators such as addition or multiplication  to 
  143.  
  144.  
  145.  
  146. new  data types created with typedef.   This means that function calls must 
  147.  
  148.  
  149.  
  150. be used,  which while a completely viable approach,  lacks elegance.   This 
  151.  
  152.  
  153.  
  154. concept is illustrated in the following example.
  155.  
  156.  
  157.  
  158. .. okay
  159.     As part of a program,  I need to define a data type called  COMPLEX 
  160.  
  161.  
  162.  
  163. which  will  function like Fortran's complex data type.  This data type  is 
  164.  
  165.  
  166.  
  167. used  for  handling  complex  numbers of the form A + iB  where  i  is  the 
  168.  
  169.  
  170.  
  171. imaginary  unit and A and B are real numbers.  This might be done with  the 
  172.  
  173.  
  174.  
  175. following definition:
  176.  
  177. .cp 6
  178.         typedef struct  /* complex number type definition */
  179.         {
  180.             double _creal;    /* real part */
  181.             double _cimag;  /* imaginary part */
  182.  
  183.         } COMPLEX;
  184.  
  185. .. okay
  186.     I  will work with several variables of type COMPLEX (eg  alpha  and 
  187.  
  188.  
  189.  
  190. beta) which are defined as follows:
  191.  
  192.  
  193.         COMPLEX alpha, beta;   /* alpha and beta are complex #'s */
  194.  
  195. Up  to this point,  we have treated the complex data type  equivalently  to 
  196.  
  197.  
  198.  
  199. built-in types.  We can also work with pointers to or arrays of COMPLEX, so 
  200.  
  201.  
  202.  
  203. there  is  no  deficiency along these  lines.   However,  to  assign,  add, 
  204.  
  205.  
  206.  
  207. multiply, or subtract these COMPLEX variables, subroutines would have to be 
  208.  
  209.  
  210.  
  211. invented.  Subroutines for two representative operations are illustrated in 
  212.  
  213.  
  214.  
  215. Figure I.
  216.  
  217. .pa
  218.  
  219.                       ---------- FIGURE I. ----------
  220.  
  221.     Assignment:  alpha = A + iB; /* pseudo code */
  222.  
  223.     Function:    
  224.         calling sequence    (K&R C):     cassign(&alpha,A,B);
  225.         calling sequence    (Unix 7 C):  alpha = cassign(A,B);
  226.  
  227.         function definition (K&R C):
  228.  
  229.             cassign(comp,a,b)
  230.             COMPLEX *comp;
  231.             double a,b;
  232.             {
  233.                 comp->_creal = a;
  234.                 comp->_cimag = b;
  235.             }
  236.  
  237.         function definition (Unix 7 C):
  238.  
  239.             COMPLEX cassign(a,b)
  240.             double a,b;
  241.             {
  242.                 COMPLEX temp;   /* temporary variable */
  243.  
  244.                 temp._creal = a;
  245.                 temp._cimag = b;
  246.                 return(temp);   /* return structure */
  247.             }
  248.  
  249.  
  250.  
  251.     Addition:  gamma = alpha + beta; /* pseudo code */
  252.  
  253.     Function:    
  254.         calling sequence    (K&R C):     cadd(&gamma,&alpha,&beta);
  255.         calling sequence    (Unix 7 C):  gamma = cadd(alpha,beta);
  256.  
  257.         function definition (K&R C):
  258.  
  259.             cadd(gamma,alpha,beta)
  260.             COMPLEX *gamma;    /* destination */
  261.             COMPLEX *alpha; /* addend */
  262.             COMPLEX *beta;  /* augend */
  263.             {
  264.                 gamma->_creal = alpha->_creal + beta->_creal;
  265.                 gamma->_cimag = alpha->_cimag + beta->_cimag; 
  266.             }
  267.  
  268. .cp 10
  269.  
  270.         function definition (Unix 7 C):
  271.  
  272.             COMPLEX cadd(alpha,beta)
  273.             COMPLEX alpha,beta; /* addend, augend */
  274.             {
  275.                 COMPLEX temp; /* temporary */
  276.  
  277.                 temp._creal = alpha._creal + beta._creal;
  278.                 temp._cimag = alpha._cimag + beta._cimag;
  279.  
  280.                 return(temp);
  281.             }
  282.  
  283.  
  284.                     ---------- END FIGURE I. ----------
  285.  
  286.  
  287. .. okay
  288.     The pseudo code presented with the subroutines in Figure I.  is the 
  289.  
  290.  
  291.  
  292. most convenient way to specify the operations desired.   If the data  types 
  293.  
  294.  
  295.  
  296. had been intrinsic, we could have used similar real C statements in lieu of 
  297.  
  298.  
  299.  
  300. subroutines.   To utilize '+', '*' or other operators with the COMPLEX data 
  301.  
  302.  
  303.  
  304. type  we must introduce a mechanism for defining these operations.
  305.  
  306.                                  Operators
  307.  
  308. .. okay
  309.     How  could  we  specify new operations?  For example how  would  we 
  310.  
  311.  
  312.  
  313. define  addition  for  the  complex  data  type?   The  following  type  of 
  314.  
  315.  
  316.  
  317. definition could be used to extend addition to the COMPLEX type: 
  318.  
  319.         COMPLEX oper `+`(alpha,beta)    /* X grammar */
  320.         COMPLEX alpha,beta;
  321.         {
  322.             COMPLEX __temp; /* temporary */
  323.             __temp._creal = alpha._creal + beta._creal;
  324.             __temp._cimag = alpha._cimag + beta._cimag;
  325.  
  326.             return(__temp); /* return result */ 
  327.         }
  328.  
  329. The  keyword oper is new:  oper indicates that the following definition  is 
  330.  
  331.  
  332.  
  333. for  an operator.   The return keyword used in function calls also  appears 
  334.  
  335.  
  336.  
  337. with a similar meaning.      Since COMPLEX preceeds oper,  this defines  an 
  338.  
  339.  
  340.  
  341. operation  over  the  COMPLEX data type.   Since there  are  two  arguments 
  342.  
  343.  
  344.  
  345. (alpha,  beta), the operator is binary.  Finally, note that the '+' sign is 
  346.  
  347.  
  348.  
  349. enclosed  in graven accents.   Quoting by graven accents is chosen as a way 
  350.  
  351.  
  352.  
  353. to distinguish operator names.   We will see that quotation will not always 
  354.  
  355.  
  356.  
  357. be needed.
  358.  
  359. .. okay
  360.     To  use  this  new operator (and assuming that '='  had  also  been 
  361.  
  362.  
  363.  
  364. defined,) the following statement could be used:
  365.  
  366.         gamma = alpha + beta;    /* add complex numbers */
  367.  
  368. Note  that  we  have  omitted the graven accents.   Since the  '+'  can  be 
  369.  
  370.  
  371.  
  372. distinguished from keywords or identifiers in this context, quoting  is not 
  373.  
  374.  
  375.  
  376. required.  The  operator definition specified above gives the X compiler  a 
  377.  
  378.  
  379.  
  380. means to evaluate the addition request specified in the example  statement.  
  381.  
  382.  
  383.  
  384. The  parser would break this statement down until it could pass an argument 
  385.  
  386.  
  387.  
  388. garnered from the left and right of the addition operator,  much as it does 
  389.  
  390.  
  391.  
  392. with  intrinsic  operators  and data types.   Whether  this  results  in  a 
  393.  
  394.  
  395.  
  396. subroutine   call   or  in-line  code  would  depend  on   the   compiler's 
  397.  
  398.  
  399.  
  400. implementation.
  401.  
  402.                              More on Operators
  403.  
  404. .. okay
  405.     Operators  turn out to be a very powerful and useful  concept.   We 
  406.  
  407.  
  408.  
  409. needn't  limit  ourselves to defining standard operations  for  new  types.  
  410.  
  411.  
  412.  
  413. There  is nothing to stop the definition of arbitrary operators.   A  crude 
  414.  
  415.  
  416.  
  417. facility  already  exists  for  this in C  via  the  parameterized  #define    
  418.  
  419.  
  420.  
  421. statement.  However, the above facility is more general and more consistent 
  422.  
  423.  
  424.  
  425. with the syntax of C than the preprocessor #define approach.   To encompass 
  426.  
  427.  
  428.  
  429. the  generation of inline code as provided by #define,  we would also offer 
  430.  
  431.  
  432.  
  433. the inline adjective, which could be used as follows:
  434.  
  435.         COMPLEX inline oper `-`(alpha,beta) /* subtraction inline */
  436.         ...
  437.  
  438. This  keyword  would  instruct the compiler to  generate  inline  code  (as 
  439.  
  440.  
  441.  
  442. opposed to a subroutine call) whenever possible.   It's use is analogous to 
  443.  
  444.  
  445.  
  446. the use of the register adjective:  the compiler complies when feasible and 
  447.  
  448.  
  449.  
  450. silently ignores the request when it cannot comply.
  451.  
  452. .. okay
  453.     In  some  cases,  C definitions can be shortened when no  ambiguity 
  454.  
  455.  
  456.  
  457. exists  (eg 'unsigned' instead of  'unsigned  int').   Therefore,  'inline' 
  458.  
  459.  
  460.  
  461. would  replace 'inline oper' in actual  practice.   Furthermore,  operators 
  462.  
  463.  
  464.  
  465. would by default work on and return integers, as functions do by default.
  466.  
  467.                          Other Users for Operators
  468.  
  469. .. okay
  470.     In  my view,  operators would be used not only to  define  existing 
  471.  
  472.  
  473.  
  474. operations  over new data types,  but also for specifying other  operations 
  475.  
  476.  
  477.  
  478. over  new  as  well  as existing data types.   These  new  operators  would 
  479.  
  480.  
  481.  
  482. normally  have alphanumeric names and would thus require quoting in  graven 
  483.  
  484.  
  485.  
  486. accents  when  they  appear in expressions.   For example,  we  define  the 
  487.  
  488.  
  489.  
  490. operation of NAND (negated and) for integers as follows (no graven  accents 
  491.  
  492.  
  493.  
  494. are required in the definition but are required in the below invocation): 
  495.  
  496.         int oper nand(a,b)
  497.         int a,b;
  498.         {
  499.             return(~(a & b));
  500.         }
  501.  
  502. To use this in an actual expression we would have to quote the nand:
  503.  
  504.         c = a `nand` b;
  505.  
  506.                             Operator Hierarchy
  507.  
  508.  
  509. .. okay
  510.     C already has a built-in hierarchy for known operations.   The most 
  511.  
  512.  
  513.  
  514. reasonable  approach is to give user-defined operators the lowest priority.  
  515.  
  516.  
  517.  
  518. This might require more parentheses, but seems logical.  
  519.  
  520.                            Pointers to Operators
  521.  
  522. .. okay
  523.     C  provides  the facility to use pointers to functions.   It  could 
  524.  
  525.  
  526.  
  527. potentially  prove  useful  to  have pointers  to  operators  as  well.   A 
  528.  
  529.  
  530.  
  531. function's  address is specified by its name without trailing  parentheses.  
  532.  
  533.  
  534.  
  535. Unfortunately,  operator  names  are  used  in this  way  to  indicate  the 
  536.  
  537.  
  538.  
  539. operation they represent.   In order to remove the ambiguity in  requesting 
  540.  
  541.  
  542.  
  543. the pointer, the operator name could be parenthesized (eg (+) or (`nand`)).  
  544.  
  545. .. okay
  546.     Using  pointers  to operators implies that defined operations  must 
  547.  
  548.  
  549.  
  550. have  subroutines associated with them.   Thus truly inline operators could 
  551.  
  552.  
  553.  
  554. have no pointers associated with them.
  555.  
  556.                    Dichotomy of Operators and Functions
  557.  
  558. .. okay
  559.     Functions and operators are almost the same  thing.   However,  the 
  560.  
  561.  
  562.  
  563. compiler  must  know if an operator is binary  or  unary.   Therefore,  its 
  564.  
  565.  
  566.  
  567. definition must be available before use.  On the other hand, arguments to C 
  568.  
  569.  
  570.  
  571. functions  are not checked for number or type.    Therefore,  we choose  to 
  572.  
  573.  
  574.  
  575. keep operators and functions separate, although there is nothing to prevent 
  576.  
  577.  
  578.  
  579. operators using function calls.
  580.  
  581. .. okay
  582.     In  order to avoid lexical conflicts,  operator and function  names 
  583.  
  584.  
  585.  
  586. would  have  to be different.   This is also desirable from  a  programming 
  587.  
  588.  
  589.  
  590. viewpoint, in order to avoid confusion and errors.
  591.  
  592.                               Other Proposals
  593.  
  594. .. okay
  595.     With the addition of operators,  the X grammar provides a much more 
  596.  
  597.  
  598.  
  599. consistent  programming environment than standard C.   However,  there  are 
  600.  
  601.  
  602.  
  603. some  other  points  with deserve consideration.   The first  of  these  is 
  604.  
  605.  
  606.  
  607. providing  a  means  to  handle  subroutines  with  a  variable  number  of 
  608.  
  609.  
  610.  
  611. arguments.  This is considered first.
  612.  
  613.     Since  C makes no assumptions about its function library,  the user 
  614.  
  615.  
  616.  
  617. is free to write his own,  should the standard functions prove  inadequate.  
  618.  
  619.  
  620.  
  621. However,  the user cannot properly handle functions with variable number of 
  622.  
  623.  
  624.  
  625. arguments,  as must be done by printf(),  scanf() and their relatives.   We 
  626.  
  627.  
  628.  
  629. solve  this  problem by introducing a typing adjective called vec which  is 
  630.  
  631.  
  632.  
  633. short  for vector.   This adjective is used to indicate the the  number  of 
  634.  
  635.  
  636.  
  637. arguments  to  the  function is  variable.   For  example,  the  ficticious 
  638.  
  639.  
  640.  
  641. function  my_printf()  which  allows  variable arguments  (and  returns  an 
  642.  
  643.  
  644.  
  645. integer) would be defined as follows:
  646.  
  647.         vec int my_printf(argcnt,argvec)
  648.         int argcnt;
  649.         char *argvec[];
  650.         {
  651.             /* code goes here */
  652.         }
  653.  
  654. .. okay
  655. A  function declared with vec always has  two  arguments:  argcnt,  argvec.  
  656.  
  657.  
  658.  
  659. These variables are analogous to main()'s (argc,argv) pair.   Before use, a 
  660.  
  661.  
  662.  
  663. definition of the form:
  664.  
  665.         vec my_printf();
  666.  
  667. would  be  included  in each file where  my_printf()  is  referenced.  This 
  668.  
  669.  
  670.  
  671. definition  causes  command line arguments to be  processed  normally:  the 
  672.  
  673.  
  674.  
  675. rightmost argument is pushed (placed on the stack) first,  and the leftmost 
  676.  
  677.  
  678.  
  679. last when code is generated.   However, the two additional arguments argcnt 
  680.  
  681.  
  682.  
  683. and  argvec  are  also stacked.   The argvec variable points to  the  stack 
  684.  
  685.  
  686.  
  687. location where the first real argument is located.  Since normal stacks are 
  688.  
  689.  
  690.  
  691. push-down,  this should provide the arguments in the correct order.  argcnt 
  692.  
  693.  
  694.  
  695. contains  the  number of arguments plus one to account  for  argvec.   This 
  696.  
  697.  
  698.  
  699. makes it completely analogous to argc.   argvec always contains an address, 
  700.  
  701.  
  702.  
  703. but this is not very useful if no arguments were specified in the  function 
  704.  
  705.  
  706.  
  707. call.
  708.  
  709. .. okay
  710.     To  illustrate  the  stacking mechanism,  imagine  that  we  invoke 
  711.  
  712.  
  713.  
  714. my_printf() as follows:
  715.  
  716.         my_printf(arg1,arg2,arg3,arg4,arg5);
  717.  
  718. For  this  specific call,  the stacking arrangment (excluding  any  special 
  719.  
  720.  
  721.  
  722. register saves) would look as specified in Figure II.   Note that argcnt is 
  723.  
  724.  
  725.  
  726. six (five arguments) for this case, as described above.
  727.  
  728. .pa
  729.  
  730.                      ---------- Figure II. ----------
  731.  
  732.             Memory Layout for a variable argument function call 
  733.  
  734.                             -------------------
  735.                             |   Low memory    |
  736.                             -------------------
  737.                             |       ...       |
  738.                             -------------------
  739.                             |   argcnt = 6    |
  740.                             -------------------
  741.                             |   argvec = ADDR |
  742.                             -------------------
  743.                       ADDR: |      arg1       |
  744.                             -------------------
  745.                             |      arg2       |
  746.                             -------------------
  747.                             |      arg3       |
  748.                             -------------------
  749.                             |      arg4       |
  750.                             -------------------
  751.                             |      arg5       |
  752.                             -------------------
  753.                             |       ...       |
  754.                             -------------------
  755.                             |   High memory   |
  756.                             -------------------
  757.       
  758.                    ---------- End Figure II. ----------
  759.  
  760.  
  761. .. okay
  762.     It might be worthwhile to have variable argument calls, even if the 
  763.  
  764.  
  765.  
  766. function were not declared as using this calling convention. To allow this, 
  767.  
  768.  
  769.  
  770. we  introduce  the  ellispis (...) concept into the  argument  string.   If 
  771.  
  772.  
  773.  
  774. my_printf()  were  not declared as vec,  we could force  variable  argument 
  775.  
  776.  
  777.  
  778. format as follows:
  779.  
  780.         my_printf(arg1,arg2,arg3,arg4,arg5...);
  781.  
  782. Always  including  the  ellipsis mark for this variety  of  call  seems  to 
  783.  
  784.  
  785.  
  786. improve  readability,  but  is not required in order that compatibility  is 
  787.  
  788.  
  789.  
  790. kept with current C usage.
  791.  
  792.                               Fixed Arguments
  793.  
  794.  
  795. .. okay 
  796.     The  argvec variable always points to the first variable  specified 
  797.  
  798.  
  799.  
  800. on  the  command  line.   However,  the  function  definition  could  still 
  801.  
  802.  
  803.  
  804. explicitly  declare  a  finite number of arguments which  it  may  wish  to 
  805.  
  806.  
  807.  
  808. examine more directly.   For example,  if the first argument of my_printf() 
  809.  
  810.  
  811.  
  812. were a control string, we could declare my_printf() as follows:
  813.  
  814.         vec int my_printf(argcnt,argvec,control_string);
  815.         int argcnt;
  816.         char **argvec;
  817.         char *control_string;
  818.  
  819. Notice that contents of control_string would be meaningless if argcnt  were 
  820.  
  821.  
  822.  
  823. less than two.  
  824.  
  825. .. okay
  826.      One final note about variable argument control is that it enhances 
  827.  
  828.  
  829.  
  830. a  function's  ability  to  detect  incorrect  input.   With  reference  to 
  831.  
  832.  
  833.  
  834. printf(),  Kernighan and Ritchie state:  "A warning:  printf uses its first 
  835.  
  836.  
  837.  
  838. argument to decide how many arguments follow and what their types are.   It 
  839.  
  840.  
  841.  
  842. will  get confused,  if there are not enough arguments or if they  are  the 
  843.  
  844.  
  845.  
  846. wrong  type."  If implemented with the vec arrangement,  printf() could  at 
  847.  
  848.  
  849.  
  850. least  know if it has been given the right number of arguments.   It  still 
  851.  
  852.  
  853.  
  854. would not know if they were of the correct types.
  855.  
  856.                      Variable length automatic arrays
  857.  
  858. .. okay
  859.     Another  element  of  the  X  grammar is  the  ability  to  declare 
  860.  
  861.  
  862.  
  863. automatic arrays which possess variable length.   Since stack displacements 
  864.  
  865.  
  866.  
  867. are  computed at each entry to a block,  this only forces a  computed  size 
  868.  
  869.  
  870.  
  871. allocation.   At worst, a memory allocation mechanism must be tied into the 
  872.  
  873.  
  874.  
  875. compiler.   This  latter restriction can be serious if C is used in a  very 
  876.  
  877.  
  878.  
  879. low  level environment,  such as in operating system development.   So that 
  880.  
  881.  
  882.  
  883. the use of this feature can be seen readily,  we require the use of the var 
  884.  
  885.  
  886.  
  887. adjective  in conjunction with such definitions.   For  most  purposes,  it 
  888.  
  889.  
  890.  
  891. offers  a  welcome enhancement.   Where it is inappropriate,  this  feature 
  892.  
  893.  
  894.  
  895. should be disabled via a compiler switch. 
  896.  
  897.     As  a general example,  we declare a variable length array  in  the 
  898.  
  899.  
  900.  
  901. following routine:
  902.  
  903.                 /* declare an array of integers one larger than argument */
  904.         array_test(length)
  905.         int length;
  906.         {
  907.             var int test[length+1]; /* declare array */
  908.             ...
  909.         }
  910.  
  911.                           A new looping structure
  912.  
  913. .. okay
  914.     Many  loops  are  unconditional  with breaks  generated  only  from 
  915.  
  916.  
  917.  
  918. within.   Therefore  it  is often useful to have an  unconditional  looping 
  919.  
  920.  
  921.  
  922. command.   This  avoids  a  lot of  'while(1)'  sequences.  This  could  be 
  923.  
  924.  
  925.  
  926. implemented as follows:
  927.  
  928. .cp 7
  929.         loop
  930.         {
  931.             ... code ...
  932.         }
  933.  
  934. replaces
  935.  
  936.         while(1)
  937.         {
  938.             ... code ...
  939.         }
  940.  
  941.  
  942.                     Preprocessors and related comments
  943.  
  944. .. okay
  945.     Preprocessors  could be used to implement several of the X features 
  946.  
  947.  
  948.  
  949. mentioned above.   The statements and expressions would be expanded by  the 
  950.  
  951.  
  952.  
  953. preprocessor  into  standard function calls.   The preprocessor would  also 
  954.  
  955.  
  956.  
  957. provide  subroutines from definitions,  as needed.   New data  types  could 
  958.  
  959.  
  960.  
  961. certainly be handled in this way.  However, changes to the C parser must be 
  962.  
  963.  
  964.  
  965. made  in order to handle the vec and var features.   Trivial additions such 
  966.  
  967.  
  968.  
  969. as loop can be handled with the existing C preprocessor.
  970.  
  971. .. okay
  972.     Some programmers may argue that no additions are needed since  most 
  973.  
  974.  
  975.  
  976. of  the features outlined above can all be achieved through function calls.  
  977.  
  978.  
  979.  
  980. In my view, the X grammar makes C more (and not less) consistent because it 
  981.  
  982.  
  983.  
  984. allows both intrinsic and user-defined types to be handled in similarly. It 
  985.  
  986.  
  987.  
  988. also allows greater portability by defining a means through which  variable 
  989.  
  990.  
  991.  
  992. argument functions can be handled uniformly.  In summation, it turns C into 
  993.  
  994.  
  995.  
  996. an extensible language while adding only a few new keywords.
  997.  
  998.                                 Conclusion
  999.  
  1000. .. okay
  1001.     In  this column,  I have suggested an enhanced C grammar which  was 
  1002.  
  1003.  
  1004.  
  1005. denoted  X to indicate extensibility.   It is the (Unix 7) C language  with 
  1006.  
  1007.  
  1008.  
  1009. enhancements   designed  to  allow  the  incorporation  of   user-specified 
  1010.  
  1011.  
  1012.  
  1013. operators into programs.  This should provide  more flexible and consistent 
  1014.  
  1015.  
  1016.  
  1017. reference to user-defined data types.   Also mentioned were variable length 
  1018.  
  1019.  
  1020.  
  1021. automatic  arrays  (var)  and a mechanism for  allowing  variable  argument 
  1022.  
  1023.  
  1024.  
  1025. functions (vec).   Finally, the use of preprocessors for implementing these 
  1026.  
  1027.  
  1028.  
  1029. ideas was mentioned.
  1030.  
  1031.     I  look forward to any other ideas about X or enhanced C which  may 
  1032.  
  1033.  
  1034.  
  1035. be forthcoming from our readers.
  1036.  
  1037.