home *** CD-ROM | disk | FTP | other *** search
/ The Education Master 1994 (4th Edition) / EDUCATIONS_MASTER_4TH_EDITION.bin / files / progmisc / qparser2 / qparser.doc
Encoding:
Text File  |  1993-11-07  |  112.2 KB  |  2,564 lines

  1.  
  2.  
  3.     QPARSER+ User Manual                                        page 1
  4.  
  5.  
  6.                       QPARSER+ SHAREWARE User Manual
  7.                                QCAD Systems
  8.                   1164 Hyde Avenue, San Jose, CA  95129
  9.     
  10.                             Table of Contents
  11.     
  12.     Chapter 1.  Introduction                                  page 2
  13.     Chapter 1.  QPARSER+ in Brief                             page 3
  14.     Chapter 2.  Parser Generator Overview                     page 7
  15.     Chapter 3.  Grammars                                      page 11
  16.     Chapter 4.  Building a Parser                             page 15
  17.     Chapter 5.  The Semantics Stack                           page 17
  18.     Chapter 6.  Accessing Semantic Elements                   page 23
  19.     Chapter 7.  More about the Lexical Analyzer               page 27
  20.     Chapter 8.  Embedded Semantics                            page 29
  21.     Chapter 9.  Using the DEBUG Option                        page 33
  22.     Chapter 10. Syntax Trees                                  page 34
  23.     Chapter 11. Miscellaneous Topics                          page 44
  24.     Chapter 12. Debugging a Translator                        page 46
  25.     Chapter 13. Registration and Support                      page 49
  26.     Chapter 14. For Additional Reading                        page 50
  27.  
  28.  
  29.     QPARSER+ User Manual                                        page 2
  30.  
  31.  
  32.     Chapter 1.  Introduction
  33.     
  34.     Almost  every  computer  program depends upon some type of textual
  35.     input.  The language associated with that input may be as  complex
  36.     as  a  programming  language  like  Pascal and C or as simple as a
  37.     series of yes and no responses.  QPARSER+ provides  the  utilities
  38.     for  the  development  of a translator capable of understanding an
  39.     input form and then converting that form into an alternate  output
  40.     form. 
  41.     
  42.     This definition of computer translation may sound quite foreign to
  43.     you,  but if you work with computers it is quite possible that you
  44.     use a  `translator'  every  day  since  one  of  the  most  common
  45.     translator applications  is the language compiler.  A product like
  46.     Turbo Pascal, for example, is a translator used to convert  Pascal
  47.     statements into machine code. 
  48.     
  49.     QPARSER+ may be used to define new programming languages, but that
  50.     is by no means the only use for the QPARSER+ system.  For example,
  51.     many of our customers have used the product to write user-friendly
  52.     interfaces  for  their  programs  since  QPARSER+  facilitates the
  53.     development  of  interfaces  capable  of   understanding   complex
  54.     user-input forms.    The  applications  for  QPARSER+  are  almost
  55.     limitless since almost every computer  program  uses  language  in
  56.     some form.    With  a  little imagination, QPARSER+ can be used to
  57.     develop products ranging from Artificial Intelligence  systems  to
  58.     word  processor  translators  capable of converting documents from
  59.     one word processor `language' into another. 
  60.     
  61.     Qparser+ is a professional, world-class product with an  installed
  62.     base of  over 400 users.  It has been preferred to Lex and Yacc by
  63.     many of its users for its ease of use, its  advanced  syntax  tree
  64.     features,  its  generation  of full source code, and the ease with
  65.     which the source code can be modified for the sake of  performance
  66.     or unusual conditions that often arise in language implementation. 
  67.     It was  designed and written by Dr.  William Barrett, an author of
  68.     a college text on compiler construction, and a  languages/compiler
  69.     specialist and consultant for more than 15 years. 
  70.     
  71.  
  72.  
  73.     QPARSER+ User Manual                                        page 3
  74.  
  75.  
  76.     Chapter 1.  QPARSER+ in Brief
  77.     
  78.     The  QPARSER+  user  must supply the structural rules of the input
  79.     language as  well  as  the  actions  to  be  taken  when  language
  80.     structures are  recognized.    QPARSER+  will  then take these two
  81.     inputs and produce a complete translator capable  of  interpreting
  82.     languages  as complex as Pascal or as simple as the set of control
  83.     codes in a word processor `language'. 
  84.     
  85.     The QPARSER+ translator system  is  designed  to  operate  in  two
  86.     phases --  a  syntactic  phase  and  a  semantics  phase.  We will
  87.     sometimes refer to these phases as the front end and back end of a
  88.     translator. 
  89.     
  90.     The syntactic  phase  is  almost  completely  automatic,  given  a
  91.     suitable grammar  for  the language.  In this phase, QPARSER+ will
  92.     take the user-defined grammar and automatically generate a  parser
  93.     capable of  understanding any input language form.  No programming
  94.     is required in this phase, although Qparser requires a compiler to
  95.     generate executable code from the generated source programs. 
  96.     
  97.     The semantics phase requires some programming  skills.    In  this
  98.     phase,  guided  by the grammar's language rules, actions are added
  99.     to the translator through additions to  the  C  or  Pascal  source
  100.     code.  Qparser provides a number of useful functions to manage the
  101.     symbol  table,  and access the parsing stack, but the central task
  102.     of providing the actions (semantics) is up to you.   This  is  the
  103.     back-end,  or  operational  side, of the translator and it must be
  104.     specially written for each application.  There  are  a  number  of
  105.     worked-out examples and tutorials to help you through this task. 
  106.     
  107.     QPARSER+ provides the tools to develop a complete translator using
  108.     either C or Pascal as the host language. 
  109.     
  110.     Features
  111.     
  112.     Many  of the QPARSER+ features listed here can only be appreciated
  113.     by those who have had some experience with translator writing, but
  114.     we feel that everyone will come to appreciate  them  after  having
  115.     used the product for a while. 
  116.     
  117.     The principal features of QPARSER+ are:
  118.     
  119.       * Correctness.   The language accepted by the translator will be
  120.     exactly that specified by the grammar. 
  121.     
  122.       * Detection of Ambiguity.  If your grammar is ambiguous,  or  if
  123.     the parser is unable to assure a correct translation, a diagnostic
  124.     message will be provided. 
  125.     
  126.       * Automatic  generation  of  two  key  components.   The lexical
  127.     scanner and the LR parser are automatically generated. 
  128.     
  129.       * Automatic generation of Syntax  Trees.    The  QTREES  package
  130.     included  in  QPARSER+  will  automatically build the syntax trees
  131.  
  132.  
  133.     QPARSER+ User Manual                                        page 4
  134.  
  135.  
  136.     which must be painfully constructed branch by branch with a system
  137.     like YACC.  The use of a syntax tree, generated  automatically  by
  138.     Qparser functions, makes code generation a snap. 
  139.     
  140.       * Flexibility.    A  translator  can  be generated in the source
  141.     language of your choice.    The  files  which  define  the  source
  142.     language  syntax  are provided only for Pascal and C, but they can
  143.     be used as models for other languages.  A program  like  YACC  can
  144.     only produce translators in C. 
  145.     
  146.       * Efficient   table-driven  parser  operations.    For  a  large
  147.     grammar, most compiler experts agree  that  a  table-driven  LR(1)
  148.     approach is superior in both performance and space requirements to
  149.     any other.  Several optimizations are made to minimize table size. 
  150.     
  151.       * Modularity  of  semantic operations.  A translator programming
  152.     task will become neatly partitioned into a  number  of  relatively
  153.     water-tight compartments. 
  154.     
  155.       * Efficient  lexical scanner.  The lexical scanner operates by a
  156.     CASE or SWITCH statement transfer on  the  next  character  to  be
  157.     scanned from  the  source.    No  backtracking, table searching or
  158.     waste motion is involved. 
  159.     
  160.       * Efficient  and  powerful  symbol  table  mechanism.     Symbol
  161.     searching   is   built   into   the   lexical  scanner,  which  is
  162.     automatically generated. 
  163.     
  164.       * Error recovery.  A reasonable error recovery system  is  built
  165.     into each translator.  No extra parser table space is required. 
  166.     
  167.       * Ease of extending or changing the language.  Once a translator
  168.     is  written following the guidelines in this manual, it'll be easy
  169.     to extend or modify.  QPARSER+ was written to be very flexible and
  170.     you'll find that once you've  mastered  the  basics,  you  can  do
  171.     translations of any type. 
  172.     
  173.       * Ease  of  extending  or  changing  the  lexical  scanner.  The
  174.     scanner is  provided  in  well-documented  source   form.      Its
  175.     underlying assumptions are described in the user manual. 
  176.     
  177.     Required Hardware and Software
  178.     
  179.     QPARSER+  runs  on  a  PC  AT  or compatible under version 5.0 (or
  180.     later) of MSDOS.  We recommend 640 Kbytes RAM for a large grammar,
  181.     but you can manage with 250 Kbytes.  A floating-point  coprocessor
  182.     is not   required.    There  are  no  special  screen  or  console
  183.     requirements -- your PC will be used in a 'dumb terminal' mode for
  184.     all of the Qparser utilities, debugging, etc.    So  you  can  run
  185.     under DOS  or Windows -- this guide will refer to a DOS operation. 
  186.     Qparser does not support extended memory, but that shouldn't be  a
  187.     handicap unless  you  have  an  exceptionally large grammar.  It's
  188.     optimized to make efficient use of your RAM.  A memory  crunch  --
  189.     if  any  --  will  appear  in LR1, which requires an unpredictable
  190.     amount of RAM, depending on the structure of your grammar. 
  191.  
  192.  
  193.     QPARSER+ User Manual                                        page 5
  194.  
  195.  
  196.     
  197.     A hard disk with at  least  2  Mbytes  of  available  capacity  is
  198.     recommended.   You  can try out the programs on a floppy-based PC,
  199.     but you'll quickly tire of juggling program and source files among
  200.     your floppy drives. 
  201.     
  202.     You'll need an editor.  It must be a technical editor, not a  word
  203.     processing editor, unless your word processor can export technical
  204.     text files.    The  required  file must consist of printable ASCII
  205.     characters (0x20 - 0x7E) separated by CR-LF pairs.  The  DOS  EDIT
  206.     utility is acceptable, as is the builtin Turbo C editor. 
  207.     
  208.     We recommend  the  Microsoft  C or Turbo C compiler.  Almost any C
  209.     compiler will do, but we've tested the product most intensively on
  210.     Turbo C and Microsoft C, and "make" files are provided  for  these
  211.     compilers. 
  212.     
  213.     DOS Installation
  214.     
  215.       You should have been able to install the product if you can read
  216.     this.   It's  simple -- 1) create a directory, like C:\QPARSER, 2)
  217.     choose an executables directory, say C:\BIN, 3) copy all the *.EXE
  218.     files to C:\BIN, 4) "xcopy" all  the  remaining  files  and  their
  219.     subdirectories  to  C:\QPARSER,  and 5) modify the C:\AUTOEXEC.BAT
  220.     file as shown below. 
  221.     
  222.       When the installation is complete, you should find the following
  223.     directories and  files  in  place.    We've   assumed   that   the
  224.     executables are installed in C:\BIN, and the Qparser work files in
  225.     C:\QPARSER. 
  226.     
  227.       C:\BIN LR1.EXE
  228.       C:\BIN OPT.EXE
  229.       C:\BIN LR1P.EXE
  230.       C:\BIN GRAMCHK.EXE
  231.       C:\QPARSER README.DOC
  232.       C:\QPARSER ORDRFORM.TXT
  233.       C:\QPARSER QPARSER.DOC
  234.       C:\QPARSER QPHELP.HLP
  235.       C:\QPARSER\CSKELS MAIN.C
  236.       C:\QPARSER\CSKELS CALC.GRM
  237.       C:\QPARSER\CSKELS CALCC.GRM
  238.       C:\QPARSER\CSKELS CALCT.GRM
  239.       C:\QPARSER\CSKELS M
  240.       C:\QPARSER\CSKELS MAKEFILE.MAK
  241.       C:\QPARSER\CSKELS SKELEVAL.C
  242.       C:\QPARSER\CSKELS SKELTAB.C
  243.       C:\QPARSER\CSKELS SKELDECL.H
  244.       C:\QPARSER\CSKELS SKELLEX.C
  245.       C:\QPARSER\CSKELS LEX.C
  246.       C:\QPARSER\CSKELS DECL.H
  247.       C:\QPARSER\CSKELS EVAL.C
  248.       C:\QPARSER\CSKELS TABLE.C
  249.       C:\QPARSER\CSKELS SYMS.C
  250.       C:\QPARSER\CSKELS PARS.C
  251.  
  252.  
  253.     QPARSER+ User Manual                                        page 6
  254.  
  255.  
  256.       C:\QPARSER\CSKELS DEBUG.C
  257.       C:\QPARSER\LIB M
  258.       C:\QPARSER\LIB MAKEFILE.MAK
  259.       C:\QPARSER\LIB SETS.H
  260.       C:\QPARSER\LIB ALLOC.H
  261.       C:\QPARSER\LIB HELP.H
  262.       C:\QPARSER\LIB PROTOS.H
  263.       C:\QPARSER\LIB RESPF.H
  264.       C:\QPARSER\LIB HELP.C
  265.       C:\QPARSER\LIB RESPF.C
  266.       C:\QPARSER\LIB ALLOC.C
  267.       C:\QPARSER\LIB SETS.C
  268.     
  269.       Your AUTOEXEC.BAT file should contain the following entries:
  270.     
  271.       PATH=....  C:\BIN; ... 
  272.       SET QPHELP=C:\QPARSER\QPHELP.HLP
  273.     
  274.     You can install the executables under some other directory, if you
  275.     adjust the PATH variable in the AUTOEXEC.BAT file to suit. 
  276.     
  277.     Regarding Pascal
  278.     
  279.     If  you're  a  Pascal  fan,  you'll  want to register with QCAD to
  280.     obtain the full  Qparser+  system.    It  contains  skeletons  and
  281.     several worked-out  examples  compatable with Turbo Pascal.  (This
  282.     version contains only C versions.) It probably won't be compatible
  283.     with the Pascal provided in other computers and operating systems,
  284.     but you can modify the source files provided to make it compatible
  285.     with your favorite Pascal. 
  286.     
  287.  
  288.  
  289.     QPARSER+ User Manual                                        page 7
  290.  
  291.  
  292.     Chapter 2.  Parser Generator Overview
  293.     
  294.     In order to generate a parser, you need a grammar  file  (XXX.GRM)
  295.     and one or more skeleton files (SKELzzzz.C). 
  296.     
  297.     The  grammar  defines the rules for parsing your language, and the
  298.     fragments of C code that are executed when a rule  is  satisfied.  
  299.     It  has  to have a particular form that will described in the next
  300.     chapter.  Program LR1.EXE is designed to operate on a grammar file
  301.     and produce several table files that are  used  by  the  remaining
  302.     utilities.  An important table file is XXX.TBL.  This contains all
  303.     the parsing tables that LR1 has worked out from the grammar. 
  304.     
  305.     LR1  and  the other utilities OPT, LR1P and GRAMCHK, will accept a
  306.     grammar file name with or without  the  suffix  .GRM.    LR1  will
  307.     create several table files based on the grammer prefix XXX. 
  308.     
  309.     Program OPT.EXE  will compress the tables in an XXX.TBL file.  The
  310.     tables will usually take less than half the space when the  parser
  311.     is finished  after OPT has done its job.  We recommend running it,
  312.     although it strictly isn't necessary. 
  313.     
  314.     A skeleton file looks like a C file, and mostly contains C  source
  315.     code.   However,  there  are  sections  that  cause  C  code to be
  316.     generated, based on table file information.  Program  LR1P.EXE  is
  317.     designed to work on a skeleton file, given a set of grammar files. 
  318.     It  generates  a  valid  C  file,  one that now is specific to the
  319.     grammar's rules.  LR1P will usually be used  several  times,  once
  320.     for each  skeleton  file.    For  example, LR1P will be applied to
  321.     SKELLEX.C to yield LEX.C, in order to specialize the code for  the
  322.     particular token forms found in the grammar. 
  323.     
  324.     You can write your own skeleton file, or revise the ones provided,
  325.     in order  to  set  up a new parsing regime.  By trying out LR1P on
  326.     various skeleton segments with a known grammar, you can figure out
  327.     how it transforms a skeleton file into a compilable C file.   (The
  328.     registered  version  includes a manual that explains the system in
  329.     detail). 
  330.     
  331.     Once you've run LR1P on the appropriate files, they are  ready  to
  332.     be compiled  and  linked.    The  result  should be an operational
  333.     parser, given only a grammar with no code fragments.    If  you've
  334.     attached  C  code  fragments  to the rules, the resulting EXE file
  335.     will not only parse input statement forms, but will  also  execute
  336.     the code fragments at the right places during the parse. 
  337.     
  338.     Operations Summary
  339.     
  340.     Here's a summary of the Qparser operations:
  341.     
  342.        Grammar  File  -->  LR1  --> Grammar tables --> OPT --> Grammar
  343.     tables
  344.     
  345.        (Skeleton File + Grammar tables) --> LR1P --> Compilable C file
  346.     
  347.  
  348.  
  349.     QPARSER+ User Manual                                        page 8
  350.  
  351.  
  352.        Compilable C files --> C COMPILER --> LINKER --> EXECUTABLES
  353.     
  354.     MAKE Files
  355.     
  356.     In order to make these operations easy, we've included two working
  357.     examples (CALC.GRM, CALCC.GRM) and make files that  carry  through
  358.     all the operations. 
  359.     
  360.     Look in  directory  CSKELS.   File "m" is a make file designed for
  361.     the Microsoft C compiler.  It  runs  under  the  Microsoft  "make"
  362.     utility.   Please examine it -- it assumes that LR1, OPT, LR1P and
  363.     QPHELP.HLP are in the  C:\QPARSER  directory,  and  the  Microsoft
  364.     compiler executables are in the C:\MCC directory.  is named in the
  365.     PATH= statement  in  your  autoexec.bat  file.  Also the Microsoft
  366.     compiler is assumed to be called by "cl".  You call the  Microsoft
  367.     make utility like this:
  368.     
  369.        make m
  370.     
  371.     File  "makefile.mak"  is a make file designed for Turbo C; it runs
  372.     under the Turbo "make" utility.  This one contains a specific path
  373.     directive for the Turbo C compiler directory -- "C:\TC".  You will
  374.     have to change this if it  isn't  in  that  directory.    It  also
  375.     contains  a  specific reference to the library files which you may
  376.     have to change.  You can call the Turbo make utility like this:
  377.     
  378.        make -fmakefile.mak
  379.     
  380.     The default grammar in both of these is CALC.GRM.  You can  change
  381.     that with the option
  382.     
  383.        GRM=YourGrammar
  384.     
  385.     in the make call, e.g. 
  386.     
  387.        make GRM=YourGrammar m ...  using Microsoft make
  388.        make GRM=YourGrammar -fmakefile.mak ...  using Turbo make
  389.     
  390.     What's  happening is that any appearance of $(GRM) in the makefile
  391.     is replaced by "YourGrammar".  Also, any definition of GRM in  the
  392.     make file  is  overridden by the command-line definition.  Look at
  393.     the makefile and you'll see where GRM is defined by  default,  and
  394.     how $(GRM) is used. 
  395.     
  396.     +***********************+
  397.     NOTE:  in  older  versions  of Turbo's make, the -D option doesn't
  398.     work.  You will have to change the GRM=CALC line in makefile.mak
  399.     +***********************+
  400.     
  401.     The makefiles will yield the  executable  file  MAIN.EXE.    We'll
  402.     assume that you won't change the name in what follows. 
  403.     
  404.     Memory Model
  405.     
  406.     We recommend  memory  model "large" for both compilers.  Check the
  407.  
  408.  
  409.     QPARSER+ User Manual                                        page 9
  410.  
  411.  
  412.     size of the executable after linking --  if  it  exceeds  64K  you
  413.     can't use  the  "compact"  or "small" model.  Turbo C will compile
  414.     and link the executable without complaint for a compact model, but
  415.     you'll run into mysterious execution problems.  Microsoft C  seems
  416.     to work with their compact model, although the executable file may
  417.     exceed 64K. 
  418.     
  419.     When  you change the model, be sure to recompile everything in the
  420.     LIB and CSKELS directory, and relink everything.  Having a mixture
  421.     of compact and large model object files is a recipe for disaster. 
  422.     
  423.     Qparser Executable Options
  424.     
  425.     You can read about the options for LR1, OPT, LR1P and  GRAMCHK  by
  426.     calling them with no parameters.  For example, LR1 reports this:
  427.     
  428.       QPARSER vs.  4.8.3
  429.       Copyright (c) QCAD Systems, Inc.  1990.  All rights reserved. 
  430.       Usage: lr1 [options] GrammarFile [RptFile]
  431.       options: -t dump tokens
  432.                 -p dump productions
  433.                 -n dump nullable nonterminals
  434.                 -i dump itemsets
  435.                 -mL language mode -- L==C, P, N
  436.                 -MN dump state machine
  437.                        N= 1, 2: dump level
  438.                 -r dump reads/includes/lookbacks
  439.                 -T tablefile table file name
  440.                 -s suppress single-production bypassing
  441.                 -?  help: more description of the options
  442.                 -C generate table file compatible with 3.0
  443.         (default tablefile name is grammar-filename.tbl)
  444.     
  445.     Most  of  these  give  additional  reports  of one kind or another
  446.     without affecting the resulting grammar table files.  (-MN, -s and
  447.     -C will affect the table file form).  You can ignore most of them. 
  448.     If you want to know more about any one option, run  LR1  with  the
  449.     option -?.   (The help file QPHELP.HLP must be installed properly,
  450.     or you'll get a complaint about it).  You can also read QPHELP.HLP
  451.     in your text editor; it's just an ordinary text file. 
  452.     
  453.     You should use the option
  454.     
  455.       -mC
  456.     
  457.     with LR1.  This states that you intend  to  use  C  as  your  host
  458.     language  --  this  is  the  compiler you will use to compile your
  459.     translator, not necessarily the language  you  are  creating  with
  460.     your grammar. 
  461.     
  462.     Executable  OPT  requires  no options, just the grammar file name,
  463.     without a suffix:
  464.     
  465.       OPT gram
  466.     
  467.  
  468.  
  469.     QPARSER+ User Manual                                       page 10
  470.  
  471.  
  472.     You should use the option
  473.     
  474.       -mC
  475.     
  476.     with LR1P.  LR1P is called with two or three files, like this:
  477.     
  478.       lr1p -mC gram skeleton [ target ]
  479.     
  480.     We recommend leaving the suffix OFF the grammar file,  i.e.    use
  481.     CALC rather  than  CALC.GRM.    The  skeleton  file name should be
  482.     complete, as well as the target file name.  Look at the make  file
  483.     for more  details.   If you don't specify a target file name, lr1p
  484.     will send the expanded source file to  stdout;  you  can  redirect
  485.     this in DOS with the ">" operator, i.e. 
  486.     
  487.       lr1p -mC gram skeleton > target
  488.     
  489.     Executable GRAMCHK is used for grammar reporting and checking; you
  490.     don't need  to  call  it to form your compilable target files.  It
  491.     has many parameters, which are explained  through  the  QPHELP.HLP
  492.     file.  Again, use option -mC to inform it that the target language
  493.     is C. 
  494.     
  495.  
  496.  
  497.     QPARSER+ User Manual                                       page 11
  498.  
  499.  
  500.     Chapter 3.  Grammars
  501.     
  502.     A grammar  is  a set of production rules.  The rules can be in any
  503.     order, except that the so-called "goal  production"  must  be  the
  504.     first in the set.  Here's what a rule looks like:
  505.     
  506.     Stmt -> Expr <eol> #PRTVAL
  507.     
  508.     This   says  that  there  is  some  class  of  input  strings,  or
  509.     "sentences" that we'll call "Stmt", that  consists  of  the  class
  510.     "Expr" followed  by  an  end-of-line  token,  "<eol>".  The symbol
  511.     "#PRTVAL" is a "tag" that will used as a handle, or reference,  to
  512.     this particular  rule.    Symbol  PRTVAL  will appear later as a C
  513.     define in the generated code, and will stand for some small  index
  514.     number. 
  515.     
  516.     The symbols  "Stmt"  and  "Expr"  are  called "nonterminals".  The
  517.     symbol "<eol>" is called a "terminal".    A  terminal  stands  for
  518.     itself,  while  a  nonterminal  can  stand for any one of a set of
  519.     strings. 
  520.     
  521.     The arrow "->" is just a place-holder in the production rule. 
  522.     
  523.     You can have several production rules  with  "Stmt"  on  the  left
  524.     side.   For  example,  in  the grammar CALC.GRM, there are several
  525.     like that:
  526.     
  527.     Stmt -> Expr <eol> #PRTVAL Stmt  ->  <identifier>  :=  Expr  <eol>
  528.     #ASSIGN1 Stmt -> <eol> \ allow an empty line
  529.     
  530.     Whenever several such rules exist, it means that "Stmt" can expand
  531.     into any  one of the given right parts.  You have a choice between
  532.     expanding Stmt into "Expr <eol>", or "<identifier> := Expr <eol>",
  533.     or just "<eol>". 
  534.     
  535.     The second rule represents an assignment  statement  as  it  might
  536.     appear in   Pascal.    The  intention  is  to  replace  the  value
  537.     associated with the user  name  "<identifier>"  with  the  current
  538.     value calculated  from  "Expr".   We'll see that "Expr" can expand
  539.     into a large number of algebraic expressions, so there's  lots  of
  540.     possibilities in that second rule. 
  541.     
  542.     The symbol  ":=" is an example of a "token".  A "token" is a short
  543.     string that appears in the input language.  It stands for  itself,
  544.     and  is  recognized  as  a  whole by a special front-end character
  545.     scanner called the "lexical analyzer". 
  546.     
  547.     An <identifier> is a  builtin  nonterminal  that  stands  for  any
  548.     string  starting with a letter and continuing with letters, digits
  549.     and underscores.  (You can change this convention to accept  other
  550.     kinds of  identifiers).    It's  used  in programming languages to
  551.     represent types, constants, variables, functions, etc.    You  can
  552.     introduce  a  name like "X" or "Sensor2", and the parser will scan
  553.     it and associate with the <identifier>  nonterminal.    Each  such
  554.     <identifier> is also a "token". 
  555.  
  556.  
  557.     QPARSER+ User Manual                                       page 12
  558.  
  559.  
  560.     
  561.     Every  <identifier>  is  also plugged into a symbol table, so that
  562.     when the same name appears more than  once,  you'll  automatically
  563.     have a handle on the same thing through the symbol table. 
  564.     
  565.     The third rule,
  566.     
  567.     Stmt -> <eol> \ allow an empty line
  568.     
  569.     makes use  of  Qparser's comment convention.  The backslash causes
  570.     it and the rest of the line to be ignored. 
  571.     
  572.     A production rule has to be written on a single text line.  If you
  573.     insert a line ending in the middle,  LR1  will  probably  complain
  574.     about a  syntax error.  Use a backslash as the LAST character in a
  575.     line in order to cause a line continuation.  Or, use long lines --
  576.     LR1 will accept a line as long as 255 characters. 
  577.     
  578.     Recursive Rules
  579.     
  580.     An important principle to use in writing a grammar  is  to  use  a
  581.     "recursive  rule"  when  you need to express the idea of repeating
  582.     something any number of times.  Look at the CALC.GRM file  again.  
  583.     You'll find a pair of rules like this:
  584.     
  585.       Stmts -> Stmts Stmt
  586.          -> <empty>
  587.     
  588.     There  are  two  more  ideas here -- the "<empty>" means literally
  589.     "empty", or nothing, not even a single character.  That's OK -- it
  590.     means that the Stmts nonterminal can expand into nothing at  all.  
  591.     You  may  think  that a rule like would be impossible to recognize
  592.     during parsing.  Well, it may cause some problems, but it  can  be
  593.     recognized on the basis of what characters are ahead of and behind
  594.     the "empty". 
  595.     
  596.     The  second  idea  is  that that when there's no left member, just
  597.     "->", Qparser assumes the same left  member  is  used  as  in  the
  598.     previous rule.  So we really have the rules
  599.     
  600.       Stmts -> Stmts Stmt
  601.       Stmts -> <empty>
  602.     
  603.     Now recursion -- the first rule says that a Stmts is another Stmts
  604.     followed by a  "Stmt".    It  would  appear that nothing's gained. 
  605.     What can this possibly mean?  Well, assume that we understand what
  606.     a Stmt stands for -- we've already looked its rule, in fact.  Then
  607.     a Stmts can be "nothing", thanks to rule. 
  608.     
  609.     Ah ha!  ...then the first rule says that a Stmts can be  "nothing"
  610.     followed by  a  Stmt, or just Stmt.  Given that, it's clear that a
  611.     Stmts can also be a Stmt followed by a Stmt.  (The  two  forms  of
  612.     Stmt don't  have  to stand for the same thing, of course).  And so
  613.     forth, ad infinitum. 
  614.     
  615.  
  616.  
  617.     QPARSER+ User Manual                                       page 13
  618.  
  619.  
  620.     This is an example of a "left recursive rule".   You'll  see  this
  621.     repeated over and over in the grammars.  The pattern is this:
  622.     
  623.        Something -> Something X
  624.     
  625.     where X can be anything at all, except <empty>.  There also has to
  626.     be a non-recursive rule for Something, such as
  627.     
  628.        Something -> Y
  629.     
  630.     Then  it's  easy to see that SomeThing will stand for any of these
  631.     sentences:
  632.     
  633.        Y
  634.        Y X
  635.        Y X X
  636.        Y X X X
  637.        (etc.)
  638.     
  639.     Recursion is how we get "any number" of something -- in this  case
  640.     X -- from a small number of rules. 
  641.     
  642.     GOAL Production
  643.     
  644.     The  first  production  rule in a Qparser grammar is assumed to be
  645.     the "goal production".  This  is  where  you  start  an  expansion
  646.     process.   There  must  also  be  only ONE such rule with its left
  647.     member.  You are allowed to use the special token  "<eof>",  which
  648.     stands  for the end-of-file in this rule, as the last token in the
  649.     rule.  You can't use <eof> anywhere else, however. 
  650.     
  651.     Grammar CALC.GRM has this goal production:
  652.     
  653.       Goal -> Stmts QUIT <eol> #QUIT
  654.     
  655.     It clearly says that the expanded forms all have to be  a  "Stmts"
  656.     followed by  the  keyword  QUIT  followed  by  an end-of-line.  An
  657.     end-of-file is implied at the end.   Since  "Stmts"  expands  into
  658.     nothing or more "Stmt" forms, the CALC grammar is a list of "Stmt"
  659.     forms followed by QUIT as the last one. 
  660.     
  661.     Since  QUIT doesn't appear anywhere else, it's only allowed at the
  662.     end of an input file. 
  663.     
  664.     Grammar Syntax
  665.     
  666.     Qparser grammar rules must  be  expressed  through  the  following
  667.     rules:
  668.     
  669.       *  Use  a backslash to start a comment, which extends to the end
  670.     of the current line. 
  671.     
  672.       * Use one or more spaces to separate the rule tokens. 
  673.     
  674.       * A rule must occupy a single line.  However, a backslash at the
  675.  
  676.  
  677.     QPARSER+ User Manual                                       page 14
  678.  
  679.  
  680.     end of a line, followed  only  by  a  line  return  character,  is
  681.     interpreted as a continuation to the next line. 
  682.     
  683.       *  Most  terminal  tokens  (like  QUIT  or  :=) don't have to be
  684.     quoted.  They are distinguished from nonterminals by the fact that
  685.     they don't appear as a left member of any rule.  Names are assumed
  686.     to be case-insensitive in the default lexical analyzer.  You  need
  687.     to express them in upper case in the grammar. 
  688.        However,  you  can  override  case-insensitivity  with suitable
  689.     changes to file SKELLEX.C.  If your  language  is  case-sensitive,
  690.     make  sure  the tokens are expressed correctly in the grammar, and
  691.     remove the "to_upper" function found in function get_symbol,  file
  692.     SKELLEX.C. 
  693.     
  694.       *  If  you  need  a  token  that  starts  with  a  quote mark, a
  695.     backslash, or a hash mark (#), use a quote (') to open  and  close
  696.     the token, for example,
  697.     
  698.        '#'
  699.     
  700.     However,  be  careful  about  quote  marks and spaces as tokens --
  701.     their use will probably confuse the lexical analyzer.  If a  quote
  702.     mark is   part   of   a  token,  you  need  to  double  it,  i.e.  
  703.     'one''token'. 
  704.     
  705.       * A rule doesn't have to  be  tagged,  i.e.    (#GOAL),  but  we
  706.     recommend tagging  every rule except single productions.  A single
  707.     production has a single nonterminal as  its  right  member.    Tag
  708.     everything else.   The tags should be unique, and will reappear as
  709.     C define names, so they need to look like C identifiers.  You  can
  710.     use the same tag on different rules, but you'll be warned, and you
  711.     need to make sure that having two rules with the same target won't
  712.     cause problems. 
  713.     
  714.     Expressions
  715.     
  716.     CALC  describes  a  complete set of algebraic expressions with the
  717.     simple operations + - * / and parenthesizing.    You  can  explore
  718.     these  and their properties by running the CALC grammar on various
  719.     input forms if you want  to.    The  registered  version's  manual
  720.     discusses  these and other commonly-found expressions, statements,
  721.     declarations, etc.  It also has a C grammar and an  Ada  grammar.  
  722.     You  can  also find grammars in various reference books -- see the
  723.     reference list at the end of this document. 
  724.     
  725.  
  726.  
  727.     QPARSER+ User Manual                                       page 15
  728.  
  729.  
  730.     Chapter 4.  Building a Parser
  731.     
  732.     A grammar describes the structure of  a  language.    You  usually
  733.     aren't interested in using a grammar to generate language strings,
  734.     as we've  described  above.   You usually have some string, in the
  735.     form of a file or user input, and you want to break it  down  into
  736.     the structure  defined by the grammar.  The program that does that
  737.     operation is called a "parser". 
  738.     
  739.     Qparser will build a parser for you from your grammar.   Let's  do
  740.     that by walking you through a parser construction for the CALC.GRM
  741.     grammar. 
  742.     
  743.     Start  by  building  the  library  files  found in the QPARSER\LIB
  744.     directory.  These are used over and over  and  will  almost  never
  745.     change, so  they've  been pulled out.  The make file there -- when
  746.     run -- should create a  library  file  called  QP.LIB,  containing
  747.     these oft-used  functions.  They'll be used in linking your parser
  748.     from here on. 
  749.     
  750.     Now build the parser by running the make file found in the  CSKELS
  751.     directory.  Make should run LR1 on the grammar file CALC.GRM, then
  752.     OPT,  then  LR1P  on each of the skeleton files listed in the make
  753.     file.  Finally, it will compile  each  of  the  files  in  CSKELS,
  754.     including some  that  LR1P  didn't  touch.   These, along with the
  755.     QPARSER\LIB library, should link together to  form  an  executable
  756.     file.  It'll be called MAIN.EXE -- the make file renames it. 
  757.     
  758.     Try executing MAIN as follows:
  759.     
  760.       main -d1
  761.     
  762.     This  invokes some Qparser debugging tools, so that you can follow
  763.     what the parser is doing.  You'll get a prompt, like this:
  764.     
  765.       >
  766.     
  767.     Enter a statement, like this, followed by an Enter:
  768.     
  769.       X + 16 * (5-6)
  770.     
  771.     You can enter spaces  freely,  or  leave  them  out,  between  the
  772.     tokens. 
  773.     
  774.     The response should be this:
  775.     
  776.       Primary -> <identifier> #VARIABLE
  777.     
  778.       [idebug] ?(help, N(ame, A(ll, D(bug level,
  779.         P(rodTrap, T(race, L(ex, S(tack, ENTER? 
  780.     
  781.     What  you've done is cause the rule Stmt -> <empty> to be applied,
  782.     then go  on.    The  next  rule  to  be   applied   is   Primary->
  783.     <identifier>.   (The  <identifier> stands for the X you entered as
  784.     the first variable name). 
  785.  
  786.  
  787.     QPARSER+ User Manual                                       page 16
  788.  
  789.  
  790.     
  791.     Keep typing the Enter key, and you'll get a complete  sequence  of
  792.     production rules that corresponds to the sentence you've entered. 
  793.     
  794.     This sequence  comprises the "parsing" of your sentence.  It won't
  795.     end until you enter "quit" followed by  an  end-of-line,  then  an
  796.     end-of-file mark.    Under  MSDOS,  this pair is entered by typing
  797.     "control-Z Enter" with input from the console.  Even  after  that,
  798.     you  may  get  one  or  two  more  rules  before the thing decides
  799.     everything is done. 
  800.     
  801.     The last rule applied will be the goal rule,
  802.     
  803.       Goal -> Stmts <eol>
  804.     
  805.     Bottom-Up Parsing
  806.     
  807.     This is a "bottom-up" parser.  It collects all  the  pieces  of  a
  808.     rule's right members, like the "Stmts" and the "<eol>" in the Goal
  809.     rule, then  collapses  them  into  the  Goal.   However, the input
  810.     sentence is scanned in a left-to-right fashion. 
  811.     
  812.  
  813.  
  814.     QPARSER+ User Manual                                       page 17
  815.  
  816.  
  817.     Chapter 5.  The Semantics Stack
  818.     
  819.     The semantics stack  is  a  first-in-last-out  (LIFO)  stack  that
  820.     carries information about the individual tokens used in the parse. 
  821.     It  grows  and  shrinks  as  the input sentence is scanned and the
  822.     rules are applied.  Growth occurs when a token  is  accepted  from
  823.     the  input  sentence  -- each token is pushed into the stack as it
  824.     appears in a left-to-right fashion in the input list.   The  stack
  825.     then shrinks when a production rule is applied.  The growth action
  826.     is  called  a  "read" or "shift" move, from the fact that an input
  827.     token is shifted into the stack. 
  828.     
  829.     Each rule application is called an "apply" or "reduce" action.  An
  830.     application causes one or more things on the top of the  stack  to
  831.     be  peeled  off  and  replaced  by  a  single state representing a
  832.     nonterminal symbol.  The apply action is the one that  you'll  use
  833.     to  obtain  a translation -- the others are just behind-the-scenes
  834.     window dressing that you  can  essentially  ignore.    Qparser  is
  835.     structured to ignore them for you, in fact. 
  836.     
  837.     A third  action,  called  a  "lookahead"  is sometimes seen.  This
  838.     action results in a state change, but nothing happens to the stack
  839.     and no token is shifted from the input  list.    It's  a  kind  of
  840.     gear-change required by the parser. 
  841.     
  842.     Let's watch  all this happen next.  Run MAIN with the option '-d3'
  843.     instead of '-d1'.  That will cause the semantics stack to  appear,
  844.     and also follow each shift action.  When we ran with option '-d1',
  845.     the shift  and  lookahead actions were skipped over.  Option '-d2'
  846.     shows the stack with each reduce action, and  option  '-d3'  shows
  847.     the stack  on every action.  Option '-d4' will show error recovery
  848.     action on a syntax error -- that one's really verbose  and  boring
  849.     unless you are interested in how Qparser handles syntax errors. 
  850.     
  851.     So type this expression at the prompt '>':
  852.     
  853.       X + 16 * (5 - 6)
  854.     
  855.     You'll  see  this on the screen after entering that expression and
  856.     touching the ENTER key several times.    (We've  omitted  the  two
  857.     prompt lines for the sake of clarity, and the states are separated
  858.     by a blank line.)
  859.     
  860.       Reduce, state 1
  861.       Stmts ->
  862.     
  863.       Read, state 18   on id X
  864.          1:   1 Stmts      NULL
  865.     
  866.       Look, state 34, on token +
  867.          1:   1 Stmts      NULL
  868.          2:  18 <identifier>  IDENT: X
  869.     
  870.       Reduce, state 3
  871.          1:   1 Stmts      NULL
  872.  
  873.  
  874.     QPARSER+ User Manual                                       page 18
  875.  
  876.  
  877.          2:  18 <identifier> IDENT: X
  878.       Primary -> <identifier> #VARIABLE
  879.     
  880.       VARIABLE seen
  881.       Look, state 35, on token +
  882.          1:   1 Stmts      NULL
  883.          2:  18 Primary    NULL
  884.     
  885.     What does all this mean?  Well, the parser has worked through four
  886.     states,  counting  the  very  first  reduce  (state 1) on Stmts ->
  887.     <empty>.  The second state (18) is a read, which shifts the  first
  888.     token X  into  the  stack.    The  report  tells  us  that X is an
  889.     identifier. 
  890.     
  891.     The third state (34) is a lookahead.  Token  +  is  used  in  this
  892.     state transition,  but  it  stays in the input list, as we'll see. 
  893.     The token X has been shifted into the stack and occupies the stack
  894.     top. 
  895.     
  896.     That leads to the fourth  state  (3),  a  reduce.    This  is  the
  897.     interesting one.    Note that the previous lookahead didn't change
  898.     the stack -- the identifier X is still on top.    Under  it  is  a
  899.     Stmts, which  it obtained from the first reduce action.  The stack
  900.     'top' will always be listed on the bottom. 
  901.     
  902.     This reduce state (3) is associated with a production about to  be
  903.     applied --
  904.     
  905.       Primary -> <identifier> #VARIABLE
  906.     
  907.     You   will  notice  that  the  production's  right  member  is  an
  908.     identifier, and it happens that an identifier is on the stack top. 
  909.     Coincidence?  Not at all.  It will always be that way --
  910.     
  911.        *****************************************
  912.        The top elements of the stack will always 
  913.           match the applied rule right member.
  914.        *****************************************
  915.     
  916.     For example, if the grammar rule at the moment of a reduce  action
  917.     is
  918.     
  919.       Expr -> Expr + Term #PLUS
  920.     
  921.     then the semantics stack will look like this:
  922.     
  923.        TOS-3:  (stuff below the stack top)
  924.        TOS-2:  Expr
  925.        TOS-1:  +
  926.        TOS:    Term
  927.     
  928.     This is  an  important  idea,  and we'll keep reminding you of it. 
  929.     Note that we'll show the top of the stack (TOS) as the last  thing
  930.     printed, so  the stack appears to be upside down.  This is because
  931.     we are usually only interested in a few elements near the top, not
  932.  
  933.  
  934.     QPARSER+ User Manual                                       page 19
  935.  
  936.  
  937.     the stuff underneath, which may scroll off the screen. 
  938.     
  939.     Each stack element is associated with a  terminal  or  nonterminal
  940.     token.   We'll  see that nonterminal stack elements can be used to
  941.     carry a data structure, which can be as simple as  an  int,  or  a
  942.     complex as a pointer to a large data structure. 
  943.     
  944.     Terminal  elements (other than <identifier>, <string>, <real>, and
  945.     <integer>) won't carry information as a rule, although there's  no
  946.     reason they couldn't. 
  947.     
  948.     When  you  type  ENTER,  you'll get a brief message that announces
  949.     that this production -- tagged VARIABLE -- has been applied:
  950.     
  951.       VARIABLE seen
  952.     
  953.     You'll get a similar message every time  a  tagged  production  is
  954.     applied.  Untagged productions aren't so announced.  This is built
  955.     in as  the  default  action  on  every tagged production.  It's to
  956.     remind you to fill in an "apply" action clause as explained later. 
  957.     
  958.     Getting the Whole Parse
  959.     
  960.     You can view the whole parse trail without the  debugging  prompts
  961.     by  using option -D3 (note the capital D rather than lowercase d),
  962.     for example:
  963.     
  964.       main -s -D3 input > output
  965.     
  966.     The "-s" option causes input lines to be  printed  on  the  screen
  967.     along with the debugging information. 
  968.     
  969.     "input" is  a  file  containing the sentences to parse.  It should
  970.     contain:
  971.     
  972.       X + 16*(5 - 6)
  973.       quit
  974.     
  975.     "output" is a file that  receives  the  textual  output  from  the
  976.     running MAIN   program.      The  -D  option  causes  stack  debug
  977.     information to be sent to the output file,  but  with  no  prompts
  978.     requiring attention.    You'll  get  a  whole trail of stack dumps
  979.     which you can examine with your editor. 
  980.     
  981.     If you try this without an "input" file, MAIN  will  expect  input
  982.     from the console, but you won't get a prompt.  You need to type in
  983.     the   recommended  statements,  followed  by  control-Z  ENTER  to
  984.     simulate the file input. 
  985.     
  986.     The trail (in file "output") is rather long.  We'll just look at a
  987.     few sections of it to bring out a few more points. 
  988.     
  989.     Find this piece of the trail -- it's been preceded by a couple  of
  990.     dozen states:
  991.     
  992.  
  993.  
  994.     QPARSER+ User Manual                                       page 20
  995.  
  996.  
  997.       Reduce, state 14
  998.          1:   1 Stmts      NULL
  999.          2:  18 Expr       NULL
  1000.          3:  23 +          NULL
  1001.          4:  27 Term       NULL
  1002.          5:  32 *          NULL
  1003.          6:  29 (          NULL
  1004.          7:  19 Expr       NULL
  1005.          8:  25 -          NULL
  1006.          9:  28 Term       NULL
  1007.       Expr -> Expr - Term #MINUS
  1008.       MINUS seen
  1009.     
  1010.     This  is  the  first  example  of  a  reduce  state  in  which the
  1011.     production's right member has more than one token in it.    Notice
  1012.     how  the  top  three states (7, 8, 9) match the production's right
  1013.     part. 
  1014.     
  1015.     The very next state is this one:
  1016.     
  1017.       Read, state 25, on token )
  1018.          1:   1 Stmts      NULL
  1019.          2:  18 Expr       NULL
  1020.          3:  23 +          NULL
  1021.          4:  27 Term       NULL
  1022.          5:  32 *          NULL
  1023.          6:  29 (          NULL
  1024.          7:  19 Expr       NULL
  1025.     
  1026.     If you compare this stack with the previous one, you'll  see  that
  1027.     elements 1 through 6 are the same, while elements 7 through 9 have
  1028.     been replaced by a single element corresponding to an Expr. 
  1029.     
  1030.     You can think of a reduce action as
  1031.     
  1032.       *********************************************************
  1033.       replacing the stack top elements matching the right member of its
  1034.       production with a single element equivalent to the production's left
  1035.       member.
  1036.       *********************************************************
  1037.     
  1038.     We said  earlier that the stack grows and shrinks.  We've now seen
  1039.     what makes it grow (tokens shifted into  the  stack  through  read
  1040.     actions) and what makes it shrink (apply actions). 
  1041.     
  1042.     Stack Represents Past History
  1043.     
  1044.     What  can be said about the rest of the stack, the portion that is
  1045.     underneath the tokens that match a production right member?  Well,
  1046.     the whole stack corresponds to a  kind  of  past  history  of  the
  1047.     parse.   It  carries  condensed  representations  of  what's  been
  1048.     scanned in the input string.  Look at the stack  again  for  state
  1049.     14.  The  deepest member, Stmts, came from an empty reduce action. 
  1050.     The next-deepest is an Expr -- this was the result of scanning the
  1051.     identifier X.  Then there's a + sign followed by Term -- the  Term
  1052.  
  1053.  
  1054.     QPARSER+ User Manual                                       page 21
  1055.  
  1056.  
  1057.     clearly corresponds to the number 16. 
  1058.     
  1059.     Here's  a  copy of the stack, and this time, we've added the parts
  1060.     of the original expression
  1061.     
  1062.       X + 16*(5 - 6)
  1063.     
  1064.     that go with it:
  1065.     
  1066.       Reduce, state 14
  1067.          1:   1 Stmts      NULL    <empty>
  1068.          2:  18 Expr       NULL    X
  1069.          3:  23 +          NULL    +
  1070.          4:  27 Term       NULL    16
  1071.          5:  32 *          NULL    *
  1072.          6:  29 (          NULL    (
  1073.          7:  19 Expr       NULL    5
  1074.          8:  25 -          NULL    -
  1075.          9:  28 Term       NULL    6
  1076.       Expr -> Expr - Term #MINUS
  1077.       MINUS seen
  1078.     
  1079.     There's a right parenthesis following the 6, but the parser hasn't
  1080.     scanned that yet.  If you continue the parse, it'll show up.    In
  1081.     fact,  here's  the  next  state,  and  -- sure enough -- the right
  1082.     parenthesis has been shifted into the stack:
  1083.     
  1084.       Reduce, state 12
  1085.          1:   1 Stmts      NULL    <empty>
  1086.          2:  18 Expr       NULL    X
  1087.          3:  23 +          NULL    +
  1088.          4:  27 Term       NULL    16
  1089.          5:  32 *          NULL    *
  1090.          6:  29 (          NULL    (
  1091.          7:  19 Expr       NULL    5 - 6
  1092.          8:  25 )          NULL    )
  1093.       Primary -> ( Expr ) #PARENS
  1094.       PARENS seen
  1095.     
  1096.     More reduce actions follow in rapid  succession.    The  next  one
  1097.     covers the multiplication:
  1098.     
  1099.       Reduce, state 15
  1100.          1:   1 Stmts      NULL    <empty>
  1101.          2:  18 Expr       NULL    X
  1102.          3:  23 +          NULL    +
  1103.          4:  27 Term       NULL    16
  1104.          5:  32 *          NULL    *
  1105.          6:  29 Primary    NULL    ( 5 - 6 )
  1106.       Term -> Term * Fact #MPY
  1107.       MPY seen
  1108.     
  1109.     After a lookahead, the addition is reduced:
  1110.     
  1111.       Reduce, state 13
  1112.  
  1113.  
  1114.     QPARSER+ User Manual                                       page 22
  1115.  
  1116.  
  1117.          1:   1 Stmts      NULL    <empty>
  1118.          2:  18 Expr       NULL    X
  1119.          3:  23 +          NULL    +
  1120.          4:  27 Term       NULL    16 * ( 5 - 6 )
  1121.       Expr -> Expr + Term #PLUS
  1122.       PLUS seen
  1123.     
  1124.     A  few more reduce actions, and the input sentences are completely
  1125.     swallowed up.  The  parsing  action  ends  on  reducing  the  Goal
  1126.     production, as follows:
  1127.     
  1128.       Reduce, state 10
  1129.          1:   1 Stmts      NULL
  1130.          2:  18 QUIT       NULL
  1131.          3:  22 <eol>      NULL
  1132.       Goal -> Stmts QUIT <eol> #QUIT
  1133.       QUIT seen
  1134.     
  1135.     This  reduction  obviously  leaves  just one element on the stack,
  1136.     associated with the Goal token.  Two  things  have  to  happen  in
  1137.     order  to  terminate  a  parse  with no errors -- the stack should
  1138.     contain just the Goal token, and no  more  tokens  appear  in  the
  1139.     input sentence.    If  only  one  of these is true, then the input
  1140.     sentence is considered to have a syntax error. 
  1141.     
  1142.  
  1143.  
  1144.     QPARSER+ User Manual                                       page 23
  1145.  
  1146.  
  1147.     Chapter 6.  Accessing Semantic Elements
  1148.     
  1149.     We now discuss how to do  something  when  a  production  rule  is
  1150.     applied.  In particular, there'll be stuff associated with each of
  1151.     the  nonterminals  on  the  stack, including "<identifier>", which
  1152.     stands for various names.  The rules for  handling  semantics  are
  1153.     fairly simple, as follows:
  1154.     
  1155.       1.   The  array  "semstack"  is  an  array  of  pointers to type
  1156.     "semrectype" (found in QPARSER\CSKELS\DECL.H) is created  in  each
  1157.     stack position.    Each  element  will  in  general  be  NULL or a
  1158.     pointer.  You should assume  that  a  non-NULL  element  has  been
  1159.     allocated  from  the  heap,  and  must  be released when no longer
  1160.     required.  The struct "semrectype"  carries  all  the  information
  1161.     associated with the stack element.  There are two type indicators,
  1162.     and a union type, corresponding to various kinds of thing that can
  1163.     be on  the stack.  You will probably want to expand this with your
  1164.     own special types. 
  1165.     
  1166.       2.  Variable "stackx" is the index of the  top-of-stack  element
  1167.     in "semstack".    You  can access the semrectype struct located at
  1168.     index N below the top of the stack by the expression
  1169.     
  1170.        semstack[stackx-N]->(whatever)
  1171.     
  1172.     N==0 refers to the top of the stack element, N==1 to the one  just
  1173.     under it, etc.  In relation to a production rule like this:
  1174.     
  1175.         P -> X4 X3 X2 X1 X0
  1176.     
  1177.     the  N==0  member  is  the RIGHT-MOST element, N==1 is next to it,
  1178.     etc.  ALL the right members need to be counted. 
  1179.       For example, a <real> in position X2 is accessed as
  1180.     
  1181.       semstack[stackx-2]->usem.rval
  1182.     
  1183.     Note that the "usem" is how the union type is referenced. 
  1184.     
  1185.       3.  As a shorthand, the define TOS(N) is equivalent to
  1186.     
  1187.       semstack[stackx-N]
  1188.     
  1189.     Thus the <real> given above can be referenced as
  1190.     
  1191.       TOS(2)->usem.rval
  1192.     
  1193.       4.  Function "apply" is called on each apply action for which  a
  1194.     production rule   tag   has  been  assigned.    (It  isn't  called
  1195.     otherwise).  This function is in file QPARSER\CSKELS\EVAL.C, which
  1196.     has been expanded from the skeleton file SKELEVAL.C.  You'll  find
  1197.     that this is essentially a switch statement on the production rule
  1198.     tags. 
  1199.       When  a  particular  production  rule  apply  action is about to
  1200.     occur, the apply is called, whence the switch  will  separate  out
  1201.     the rule  that's  being applied.  The stack is guaranteed to carry
  1202.  
  1203.  
  1204.     QPARSER+ User Manual                                       page 24
  1205.  
  1206.  
  1207.     the right member semrectype invocations.  It's up to  you  to  use
  1208.     these struct values and construct a semrectype to replace them. 
  1209.     
  1210.       5.  Qparser will allocate a "semrectype" for each of the special
  1211.     nonterminal  types  <identifier>, <integer>, <real>, <string>, and
  1212.     <character>.   All  other  tokens  shifted  into  the  stack   are
  1213.     associated with  NULL.    All other nonterminals will have NULL in
  1214.     the stack, unless you have allocated a semrectype for  certain  of
  1215.     them, as explained later. 
  1216.       You need  to free these before returning from apply.  The safest
  1217.     course of action is to call the function "disp_sem" on each of the
  1218.     right member nodes that might have something in it.  It will  deal
  1219.     with NULLs  and  the known semrectype types properly.  You need to
  1220.     expand this function if you have other kinds of nodes. 
  1221.     
  1222.       6.  For most parsers, you can just use the information given  in
  1223.     the stack and generate some code.  If the production has something
  1224.     to  do  with  a  declaration,  it'll have an <identifier> in it --
  1225.     you'll want to  do  something  with  the  symbol  table  attribute
  1226.     associated with the identifier.  If that's all you are doing, then
  1227.     make sure  that  the  apply  function  returns NULL.  When "apply"
  1228.     returns, the parsing system will  strip  the  stack  back  by  the
  1229.     number  of elements in the rule's right part, then push the return
  1230.     pointer from "apply"; this will be NULL in this case. 
  1231.     
  1232.       7.  If you want  to  save  something  on  the  stack  from  this
  1233.     production  rule  --  and  that gets us into some advanced parsing
  1234.     ideas -- you should allocate a semrectype struct  from  the  heap,
  1235.     and return its pointer from "apply".  That pointer will get pushed
  1236.     on the  stack.    Note that it is effectively associated with some
  1237.     rule left member token, for example "Expr".    This  pointer  will
  1238.     show up later in the parse.  You can identify it by the appearance
  1239.     of "Expr" in the right member of a production. 
  1240.       Use "new_sem"  to  allocate a "semrectype".  It also initializes
  1241.     various slots in the struct. 
  1242.       Say you've done this.  You now must make sure that:
  1243.     
  1244.         7a.  You've done the same thing for ALL the rules with  "Expr"
  1245.     as their  left  member.    It's OK to push NULL for some of these,
  1246.     provided you test for NULL when you find "Expr" appearing  later.  
  1247.     But  you  need  to  do something sensible for all the productions,
  1248.     since the reappearance of "Expr" may have come from any of them. 
  1249.     
  1250.         7b.  Since the "Expr"  reappearance  may  refer  to  a  struct
  1251.     allocated  from  the  heap,  you  should  free  that struct before
  1252.     returning from apply.  Otherwise, you'll get a memory leak,  since
  1253.     the  parser  doesn't pay attention to what's on the stack when the
  1254.     top part is scraped off.  Obviously, it doesn't need to  be  freed
  1255.     if it's NULL. 
  1256.     
  1257.     The <identifier> and the Symbol Table
  1258.     
  1259.     Every  appearance  of  <identifier>  in  your  grammar  causes the
  1260.     scanner to expect a user name object, for example,
  1261.     
  1262.  
  1263.  
  1264.     QPARSER+ User Manual                                       page 25
  1265.  
  1266.  
  1267.        credit_slip
  1268.        itemized_bill
  1269.        sender001
  1270.     
  1271.     or something like that.  Now there will also  be  tokens  in  your
  1272.     grammar  that  look  like  a  user name but are actually "reserved
  1273.     words" or "key words".  In C, for example, some of these are
  1274.     
  1275.        while
  1276.        for
  1277.        switch
  1278.        case
  1279.     
  1280.     and so on.  A reserved word is distinguished from an  <identifier>
  1281.     by the  fact  that  it will explicitly appear in the grammar.  For
  1282.     example, in C, there might be the rule
  1283.     
  1284.        Stmt -> while ( Expr ) Stmt
  1285.     
  1286.     in which the reserved word "while" has explicitly appeared. 
  1287.     
  1288.     The problem for the scanner is that both appear superficially like
  1289.     a user identifier.  There are  different  ways  of  distinguishing
  1290.     these, but in Qparser, the solution chosen is this:
  1291.     
  1292.       1.   The  lexical  analyzer  picks  up and scans everything that
  1293.     starts with a  letter  and  continues  with  a  letter,  digit  or
  1294.     underbar.   The  code  that  does this specifically is in function
  1295.     "get_symbol" found in skellex.c.  You can change this strategy  if
  1296.     you  want to, in order to accept different kinds of things as user
  1297.     names, but be careful -- the system  assumes  that  keywords  look
  1298.     like user names. 
  1299.     
  1300.       2.  Each  user  name is looked up in a symbol table.  If it's in
  1301.     the  table,  it  might  be  there  as  the  same  user  name  seen
  1302.     previously,  or  it  might  be  marked with an attribute that says
  1303.     "this is a keyword".  The keyword attribute is RESERVED.   If  the
  1304.     name has the RESERVED attribute, the symbol table also carries its
  1305.     token   code,   and  the  token  scanner  returns  that,  not  the
  1306.     <identifier> token code. 
  1307.     
  1308.       3.  The keywords are put in the symbol  table  as  part  of  the
  1309.     parser initialization.   This is also in SKELLEX.C -- see function
  1310.     "inittables", which opens the symbol table  system  for  operation
  1311.     and stuffs  it  with the known keywords.  You'll also find a bunch
  1312.     of other things in the symbol table as keywords  that  don't  look
  1313.     like user names -- ":=" for example, in CALC.  These are used by a
  1314.     different  section  of the lexical analyzer to figure out compound
  1315.     tokens. 
  1316.     
  1317.       The  symbol  table   functions   are   defined   in   the   file
  1318.     CSKELS\SYMS.C, and  the  structs  and prototypes in CSKELS\DECL.H. 
  1319.     The symbol table consists of an array of pointers to a linked list
  1320.     of "symtabtype" structs.  Each symtabtype carries a pointer to the
  1321.     next symtabtype in the list, if any,  and  NULL  if  none.    Each
  1322.  
  1323.  
  1324.     QPARSER+ User Manual                                       page 26
  1325.  
  1326.  
  1327.     symtabtype  also  carries  the  symbol  string as a heap-allocated
  1328.     string "sym".  A  scope  level  "level"  is  used  to  distinguish
  1329.     reserved tokens,  globals,  locals,  etc.  An attribute tag "symt"
  1330.     has a few predefined values, and will usually be expanded  by  you
  1331.     in  order  to  support  a  variety of symbol classes, for example,
  1332.     labels, typedefs, constants, etc. 
  1333.       A symbol's attribute may also include other values. 
  1334.     
  1335.       Several symbol table functions are provided through  the  SYMS.C
  1336.     module.  For example, "findsym" accepts a character string "fsym",
  1337.     and  returns  a pointer to the symtabtype in the symbol table with
  1338.     that symbol string, or NULL if it can't be found.  "makesym"  does
  1339.     the  same  thing,  except that it will always insert the symbol at
  1340.     the head of the list if it can't find one by that  name  already.  
  1341.     By  using  a  combination  of  these  functions, you can shape the
  1342.     symbol operations of your language in a variety of ways. 
  1343.     
  1344.  
  1345.  
  1346.     QPARSER+ User Manual                                       page 27
  1347.  
  1348.  
  1349.     Chapter 7.  More about the Lexical Analyzer
  1350.     
  1351.     The lexical analyzer has the task of scanning  the  input  string,
  1352.     which may  be  a user-entered string or a file.  It pays attention
  1353.     to line endings, surplus spaces, comments, etc.  and  attempts  to
  1354.     recognize  and  extract  a  sequence of tokens that the parser can
  1355.     operate on. 
  1356.     
  1357.     The Qparser lexical analyzer (which we'll call QLA)  is  partially
  1358.     constructed from  information obtained from the grammar.  When LR1
  1359.     scans the grammar, it also produces tables of  information  needed
  1360.     to build the analyzer, for example, what reserved words are found,
  1361.     and what special tokens are found. 
  1362.     
  1363.     How  all  this works is best explained in the registered version's
  1364.     manual.  You can of course study  the  code  in  SKELLEX.C  and  a
  1365.     generated file  such  as LEX.C.  In a nutshell, here is a guide to
  1366.     Qparser's lexical system:
  1367.     
  1368.       * QLA operates in a "skipblanks" mode or in a "recognize" mode.  
  1369.     It  starts  in  the skipblanks mode, in which it scans over -- and
  1370.     ignores -- spaces, tabs and comments.  It goes into the  recognize
  1371.     mode when  something  shows  up  that  is  neither  of these.  See
  1372.     function "get_token" in LEX.C which makes the  mode  decision  and
  1373.     later branches to one of the token functions. 
  1374.     
  1375.       *  All  the  QLA functions ultimately appear as explicit C code,
  1376.     not as cryptic  tables.    You  can  adjust  them  for  particular
  1377.     circumstances as required. 
  1378.     
  1379.       *  Identifiers and reserved words are considered to start with a
  1380.     letter, and  continue  with  letters,  digits  and  certain  other
  1381.     special characters.    Each  identifier  found  in  the  source is
  1382.     entered in the symbol table, but only if it's not already there. 
  1383.       Reserved words are separated from user names by their appearance
  1384.     in the symbol table as such.  Reserved words are marked by special
  1385.     initialization code found in function inittables.    See  function
  1386.     "get_symbol" in LEX.C. 
  1387.     
  1388.       *  Numbers  (integers and floats) are considered to start with a
  1389.     digit, and continue  until  a  C-like  floating  point  number  is
  1390.     recognized.   Note that a leading sign (+, -) is NOT recognized --
  1391.     you need to place these explicitly in the grammar.    The  default
  1392.     CSKELS  number  scanner  accepts  simple  integers, simple decimal
  1393.     numbers, or decimal numbers followed by an  exponent  part.    The
  1394.     exponent  part  must be "E" optionally followed by "+" or "-", and
  1395.     followed by an integer.  No spaces  or  line  returns  may  appear
  1396.     within the number.  Floating point forms are converted to a double
  1397.     internally.  Integers  are  converted to a long int.  See function
  1398.     "get_number" in LEX.C. 
  1399.       A  more  complete  C  number  recognizer  is  provided  in   the
  1400.     registered version. 
  1401.     
  1402.       *  Strings  are considered to start with a string quote ("), and
  1403.     continue through the closing quote.  A line return may not  divide
  1404.  
  1405.  
  1406.     QPARSER+ User Manual                                       page 28
  1407.  
  1408.  
  1409.     a string.    The  default  string recognizer also accepts \" as an
  1410.     embedded quote, and \\ as an embedded backslash, per the  usual  C
  1411.     string conventions.   No other backslash conventions are provided. 
  1412.     See function "get_string" in LEX.C. 
  1413.       A  more  complete  C  string  recognizer  is  provided  in   the
  1414.     registered version. 
  1415.     
  1416.       *  All other tokens, such as "+", ":=", ">>", that appear in the
  1417.     grammar, are recognized through a  "greedy"  algorithm.    Qparser
  1418.     provides  a  unique  way  of scanning these that is explained more
  1419.     fully in the registered version.  See  function  "get_special"  in
  1420.     LEX.C.  This function is modified by LR1P according to information
  1421.     derived from the grammar. 
  1422.     
  1423.     Your Very Own Lexical Analyzer
  1424.     
  1425.     You can  also  replace all of this with your own lexical analyzer. 
  1426.     It only needs to do the following:
  1427.     
  1428.       * Scan whatever source you have
  1429.     
  1430.       * Recognize the terminal tokens  that  appear  in  your  grammar
  1431.     somehow. 
  1432.     
  1433.       *  Assign  a  numeric  code  to  each  token  so recognized that
  1434.     corresponds to the numbers assigned by LR1.  You can get a list of
  1435.     the tokens and their code numbers through option -t when  you  run
  1436.     LR1.  Return this int code from "get_token". 
  1437.     
  1438.       *  If a token represents a class of strings rather than a single
  1439.     string, you need to have your lexical analyzer set up a semrectype
  1440.     for it.  This is assigned to the global variable "lsemp", which in
  1441.     turn will appear on the parser stack at  the  appropriate  place.  
  1442.     lsemp is  allocated  from  the  heap.    Follow  the  examples for
  1443.     <identifier>, <real> and <string> found in LEX.C. 
  1444.     
  1445.     You can also keep the lexical analyzer extremely  simple  and  get
  1446.     the grammar  parser  and  your semantics codes to do all the work. 
  1447.     However, note that a grammar is not a very good  way  to  describe
  1448.     the  idea  of a comment that can appear anywhere, so you will need
  1449.     to build this into your lexical analyzer.    You'll  find  similar
  1450.     problems with quoted strings. 
  1451.     
  1452.  
  1453.  
  1454.     QPARSER+ User Manual                                       page 29
  1455.  
  1456.  
  1457.     Chapter 8.  Embedded Semantics
  1458.     
  1459.     Qparser  supports  "embedded  semantics", which means that you can
  1460.     write the semantics for a  rule  directly  in  the  grammar.    We
  1461.     recommend doing this, rather than modify file EVAL.C after Qparser
  1462.     has produced it by expanding SKELEVAL.C. 
  1463.     
  1464.     An example  of this is given in the file QPARSER\CSKELS\CALCC.GRM. 
  1465.     Please refer to it in the discussion that follows. 
  1466.     
  1467.     An embedded semantic rule looks like this:
  1468.     
  1469.       Stmt -> Expr <eol> #PRTVAL
  1470.        {
  1471.        printf("%lg\n", STACK(1)->usem.rval);
  1472.        FREE(1);
  1473.        }
  1474.     
  1475.     The production rule is of course the first line.  The tag  #PRTVAL
  1476.     must appear  on the same line.  You can now write several lines of
  1477.     embedded C code in the following lines, provided that  you  follow
  1478.     these rules:
  1479.     
  1480.       1.   The  left  and  right  braces  {...}  must  be balanced and
  1481.     matched.  If they aren't, LR1 will get confused and complain about
  1482.     a syntax error.  LR1 understands the C string and comment  syntax,
  1483.     so you can have unbalanced braces in those. 
  1484.     
  1485.       2.   If  you're  fussy about indenting rules, the indentation of
  1486.     the left brace in the first line will be fit  to  the  indentation
  1487.     specified  in  the  target  file,  and the remaining lines will be
  1488.     indented relative to it.   Try  some  examples  through  LR1P  and
  1489.     you'll get the idea. 
  1490.     
  1491.       3.  The embedded semantics code fragment will appear in function
  1492.     "apply" found  in  EVAL.C,  after expansion of SKELEVAL.C by LR1P. 
  1493.     It will become the particular case branch code associated with the
  1494.     specified tag.  The "break" usually used at the end of a case code
  1495.     fragment is provided by the expansion code. 
  1496.     
  1497.     CALCC Semantics
  1498.     
  1499.     We're now in a position to review the CALCC semantics.  CALCC  has
  1500.     the same grammar as CALC, except that it has been embellished with
  1501.     embedded semantic     rules.          Please    refer    to    the
  1502.     QPARSER\CSKELS\CALCC.GRM file for details. 
  1503.     
  1504.     The idea here is that each of the  nonterminals  Expr,  Term,  and
  1505.     Primary will be associated with a semrectype struct, which in turn
  1506.     will carry  a  floating point number in its "rval" slot.  (Look at
  1507.     the struct in file DECLS.H and you'll find this). 
  1508.     
  1509.     The PRTVAL rule is a simple one to start with:
  1510.     
  1511.       Stmt -> Expr <eol> #PRTVAL
  1512.  
  1513.  
  1514.     QPARSER+ User Manual                                       page 30
  1515.  
  1516.  
  1517.        {
  1518.        printf("%lg\n", STACK(1)->usem.rval);
  1519.        FREE(1);
  1520.        }
  1521.     
  1522.     Here, we just print the  real  number  in  the  semrectype  struct
  1523.     associated with  "Expr".   Note that the <eol> is in position 0 on
  1524.     the stack, and "Expr" is in position 1, so STACK(1)  expands  into
  1525.     semstack[stackx-1].   (The  #PRTVAL  tag  doesn't  count  in  this
  1526.     indexing scheme). 
  1527.     
  1528.     The "Expr" element has been allocated from the heap, so it  should
  1529.     be freed  before  returning from the apply action.  That's done by
  1530.     the FREE(1) -- the "1" refers to index position 1, or  the  "Expr"
  1531.     position. 
  1532.     
  1533.     Getting a REAL on the Stack
  1534.     
  1535.       Here's how a real semrectype struct gets on the stack:
  1536.     
  1537.     Primary -> <real> #REALVAL
  1538.                {
  1539.                  tsemp= TOS(0);
  1540.                  }
  1541.     
  1542.     The  only  place  that  "<real>"  appears in a right member in the
  1543.     grammar is in this production rule.  If you follow the rule  chain
  1544.     backward,  you'll  see  that a Term can generate a Primary, and an
  1545.     Expr can generate a Term, so it's possible for the <real>  element
  1546.     to pop up as an Expr. 
  1547.     
  1548.     The action here is simple -- just return the top-of-stack element,
  1549.     TOS(0).  What's   going   on?    Well,  Qparser  will  allocate  a
  1550.     semrectype when it scans a real number, and will place  a  pointer
  1551.     to it on the stack.  So all we have to do is return it through the
  1552.     apply.   The returned pointer will be pushed onto the stack and be
  1553.     associated with  the  Primary.    That  in  turn  will  later  get
  1554.     associated with Term and ultimately with Expr. 
  1555.     
  1556.     Single Productions
  1557.     
  1558.     There's  a  bit  more magic that we haven't mentioned -- the apply
  1559.     function returns the current top-of-stack pointer TOS(0) when  the
  1560.     production has  no  flag.  That happens through this code fragment
  1561.     found in "apply":
  1562.     
  1563.         case 0:  /* no flag */
  1564.           if (popno[cstate]==1) tsemp= TOS(0);  /* pass this through */
  1565.           break;
  1566.     
  1567.     This works fine for each untagged single production, for example,
  1568.     
  1569.        A -> B
  1570.     
  1571.     where both A and B are nonterminals.  Whatever was associated with
  1572.  
  1573.  
  1574.     QPARSER+ User Manual                                       page 31
  1575.  
  1576.  
  1577.     B becomes associated with A.  However, a tagged single  production
  1578.     will  have  a  case  branch,  and you need to deal with the return
  1579.     value, which will be NULL by default.  You also need to make  sure
  1580.     that  a tag appears on every empty production and every non-single
  1581.     production, otherwise you'll get interesting things on  the  stack
  1582.     by default. 
  1583.     
  1584.     That's really  how  a  Primary turns into a Term, etc.  -- through
  1585.     the automatic semantics associated with single productions. 
  1586.     
  1587.     Binary Operations
  1588.     
  1589.     Now look at a typical binary operation production:
  1590.     
  1591.     Expr -> Expr + Term #PLUS
  1592.                {
  1593.                  CCBINOP(+);
  1594.                  }
  1595.     
  1596.     This just calls CCBINOP.  This is a macro, as follows:
  1597.     
  1598.     #define CCBINOP(op)  { NEW(FLOAT,REALVAR); \
  1599.               tsemp->usem.rval= TOS(2)->usem.rval op TOS(0)->usem.rval;\
  1600.               FREE(0); FREE(2); }
  1601.     
  1602.     (This is in the grammar file CALCC.GRM, too, but isolated from the
  1603.     production rules)
  1604.     
  1605.     What CCBINOP does is allocate a new  semrectype  struct  from  the
  1606.     heap, with  the types FLOAT and REALVAR.  This will be assigned to
  1607.     the local pointer variable tsemp. 
  1608.     
  1609.     In the second line, the  operation  "op"  expands  into  "+",  and
  1610.     therefore carries  out an addition followed by an assignment.  The
  1611.     addition is of the two right production  member  values,  "2"  and
  1612.     "0", corresponding to "Expr" and "Term", respectively.  The values
  1613.     are assigned to the "rval" slot of tsemp. 
  1614.     
  1615.     Finally, members  0  and  2  are freed.  We could have kept one of
  1616.     them for the result value, but that's dangerous in general. 
  1617.     
  1618.     Other Grammar Semantics
  1619.     
  1620.     The CCBINOP define, along with some other useful ones, are part of
  1621.     a section of the grammar that's kept  apart  from  the  production
  1622.     rules.   The section opens with "#begin" in the leftmost column of
  1623.     a new line.  In CALCC.GRM, the line is
  1624.     
  1625.       #begin apply_locals
  1626.     
  1627.     The section continues with whatever C code you want to put in, and
  1628.     ends with
  1629.     
  1630.       #end apply_locals
  1631.     
  1632.  
  1633.  
  1634.     QPARSER+ User Manual                                       page 32
  1635.  
  1636.  
  1637.     The names "apply_locals" must be matched. 
  1638.     
  1639.     You can have any number of such blocks of code in your grammar. 
  1640.     
  1641.     They reappear in a skeleton file, such as SKELEVAL.C, wherever you
  1642.     see the phrase
  1643.     
  1644.       {## begin
  1645.           write(apply_locals);
  1646.           end; ##}
  1647.     
  1648.     You'll find this in the skeleval.c file within the  function  body
  1649.     of "apply".   Compare with the eval.c file and you'll see that the
  1650.     code block has been properly inserted from the grammar. 
  1651.     
  1652.     In fact, the "write" phrase has been conditioned with
  1653.     
  1654.        if (defined(apply_locals)) then ...  ;
  1655.     
  1656.     just in case a block called "apply_locals" doesn't exist.   You'll
  1657.     discover some other blocks in skeleval.c that won't be expanded by
  1658.     CALCC.GRM, since they aren't defined. 
  1659.     
  1660.     Other  code  blocks  are provided in SKELEVAL.C, to support global
  1661.     functions  (eval_functions),  a  call  just   before   the   first
  1662.     production  is  called  (init_sem), and a call just after the last
  1663.     production is called (end_sem).  You can add more to  any  of  the
  1664.     skeleton files by following the pattern shown there.  In this way,
  1665.     ANY language-dependent feature can be embedded in your grammar, so
  1666.     that  you never need to modify the standard Qparser skeleton files
  1667.     again. 
  1668.     
  1669.     Running CALCC
  1670.     
  1671.     It's best to  delete  *.TBL.    This  will  force  a  rebuild  and
  1672.     recompile based on a new grammar. 
  1673.     
  1674.     You  can compile and link CALCC by changing the makefile to accept
  1675.     CALCC instead of CALC as the grammar, e.g.  run
  1676.     
  1677.        make GRM=CALCC (etc.)
  1678.     
  1679.     It should regenerate the grammar files and source files, and  link
  1680.     to produce  a working calculator as file MAIN.EXE.  (NOTE: Turbo's
  1681.     make utility seems to require changing the GRM=CALC  line  in  the
  1682.     makefile instead). 
  1683.     
  1684.     Try  out  the compiled/linked result (without the -d1 option) with
  1685.     some expressions, and you'll  see  how  it  evaluates  and  prints
  1686.     results. 
  1687.     
  1688.  
  1689.  
  1690.     QPARSER+ User Manual                                       page 33
  1691.  
  1692.  
  1693.     Chapter 9.  Using the DEBUG Option
  1694.     
  1695.     Qparser  provides  several  tools  for  checking  your grammar and
  1696.     semantics.  These are invoked by building your files with the make
  1697.     option -DDEBUG=1, i.e.   the  DEBUG  variable  must  be  defined.  
  1698.     You'll find  instructions  on  this  in  the  make  file.  (Again,
  1699.     Turbo's make utility doesn't accept this; you need to  modify  the
  1700.     make file directly). 
  1701.     
  1702.     You  can  invoke  the stack debugging when executing the parser by
  1703.     entering the special character "!"  with  the  input.    When  the
  1704.     parser encounters this, it will invoke the debugger on the current
  1705.     stack.  You can then set the debugging level to 1 or higher -- try
  1706.     2  to  see  the  whole  stack  -- and each Enter will show you the
  1707.     situation just before a particular apply action. 
  1708.     
  1709.     The options -dN and -DN cause the stack debugging to start  before
  1710.     anything is  entered,  which  is often useful.  The lower-case 'd'
  1711.     sets the debug level (N= 0, 1, 2, ...) and pauses  on  each  apply
  1712.     action for  your  response.    The  upper-case  'D' sets the debug
  1713.     level, but does not wait for a pause -- you  can  get  a  detailed
  1714.     trace of the actions with it. 
  1715.     
  1716.     When  you execute the CALCC program under debug level 2 or higher,
  1717.     you'll see the values entered appearing on  the  stack  associated
  1718.     with various  nonterminal  tokens.   You should be able to see how
  1719.     the system works using this. 
  1720.     
  1721.     The  character  "!"  is  caught  in  the  lexical  analyzer,  file
  1722.     SKELLEX.C, in  function  get_token.  You can remove this or change
  1723.     the character to something else by adjusting the source  code  and
  1724.     recompiling. 
  1725.     
  1726.     All the  debug  tools  are in file DEBUG.C.  You'll want to expand
  1727.     certain  of  its  functions  if  you  extend  the  symtabtype   or
  1728.     semrectype  structs with your own variant types, so that they will
  1729.     be reported correctly. 
  1730.     
  1731.  
  1732.  
  1733.     QPARSER+ User Manual                                       page 34
  1734.  
  1735.  
  1736.     Chapter 10.  Syntax Trees
  1737.     
  1738.     Qparser can automatically build an "abstract syntax tree" that can
  1739.     hold all the information  from  a  parser  in  a  form  especially
  1740.     convenient for generating code or handling a declaration. 
  1741.     
  1742.     An  abstract  syntax  tree,  or  AST  for short, is rooted in some
  1743.     nonterminal token of  the  grammar.    Each  internal  nodes  also
  1744.     corresponds  to  a  nonterminal  token,  and  the  leaf nodes will
  1745.     generally be an <identifier>, <number>, or <string>.  (Sometimes a
  1746.     leaf node  will  be  empty).    Each  node  is  represented  by  a
  1747.     "semrectype"  struct,  which  always  carries  a  node  designator
  1748.     "semt", a node type "symt", some attributes, and slots  for  child
  1749.     node pointers. 
  1750.     
  1751.     An  AST  will represent a clause in the input language stream that
  1752.     has been scanned and covered by the root nonterminal token. 
  1753.     
  1754.     Let's take an example.  Suppose the input clause is
  1755.     
  1756.       x - 15 * z
  1757.     
  1758.     This can be derived from an Expr, using our CALC  grammar.    Here
  1759.     are  some  of  the  key  derivations  from Expr that show how this
  1760.     clause expands out of Expr:
  1761.     
  1762.       Expr -> Expr - Term
  1763.            -> Expr - Term * Fact
  1764.            -> Expr - Term * <identifier>
  1765.            -> Expr - <integer> * <identifier>
  1766.            -> <identifier> - <integer> * <identifier>
  1767.            ->      x       -    15     *      z
  1768.     
  1769.     We've left out several  single-production  derivation  steps,  for
  1770.     example,
  1771.     
  1772.       Fact -> Primary -> <identifier>
  1773.     
  1774.     in order  to give you the general idea.  In fact, the parser hides
  1775.     steps like these, too. 
  1776.     
  1777.     What we'd like to do is describe this as a tree in which the nodes
  1778.     will be production tags.  Note that a production  tag  corresponds
  1779.     to  a production rule, which can also be loosely associated with a
  1780.     nonterminal.  The tree we want will look like this:
  1781.     
  1782.  
  1783.  
  1784.     QPARSER+ User Manual                                       page 35
  1785.  
  1786.  
  1787.                               MINUS (-)
  1788.                                 |
  1789.               +-----------------+-----------------+
  1790.               |                                   |
  1791.           <identifier> (x)                      MPY (*)
  1792.                                                   |
  1793.                                       +-----------+-----------+
  1794.                                       |                       |
  1795.                                   <integer> (15)         <identifier> (z)
  1796.     
  1797.     Notice how the "MPY: 15 * z"  part  comes  out  of  the  last  few
  1798.     derivation steps  in  a  natural  way.    It  will come out of our
  1799.     bottom-up system first, in fact, and later  get  attached  to  the
  1800.     MINUS part.  If you trace the productions and stack development by
  1801.     running CALC, you'll see the order in which these fall out. 
  1802.     
  1803.     AST Represents Evaluation Order
  1804.     
  1805.     Why is  this  form  useful?    Because it concisely represents the
  1806.     order in which the  operations  need  to  be  carried  out.    For
  1807.     example,  the  subtraction  can't be done until the subtrahend (x)
  1808.     and the subtractor (MPY) are both known, so that requires that the
  1809.     MPY be done first.  This  agrees  with  the  precedence  rules  of
  1810.     algebra,  which  are  in  fact  built  into  the  structure of the
  1811.     grammar.  (You can set up the precedence rules in any  manner  you
  1812.     like by suitably organizing the production rules.)
  1813.     
  1814.     How Qparser Builds an AST
  1815.     
  1816.     Qparser  generates  a pair of tables that help it construct an AST
  1817.     like this.  The function "make_tree", found in eval.c,  is  called
  1818.     as the  first operation in function apply.  Note that it returns a
  1819.     "tsemp" pointer.  Each call of make_tree does  the  following,  in
  1820.     general:
  1821.     
  1822.       1.   Allocates  a  semrectype  node  from  the  heap, by calling
  1823.     "new_sem".  Let's call the node N. 
  1824.       2.  Attaches certain  of  the  stack  semrectype  nodes  as  N's
  1825.     children.   The  child  nodes are also semrectype structs, and are
  1826.     linked to  N  through  the  array  "position0".    The   size   of
  1827.     "position0" is set by Qparser to hold just enough pointers for the
  1828.     longest possible production rule. 
  1829.       3.   Returns  the "tsemp" pointer which carries the current rule
  1830.     tag, and carries (as its children) the appropriate stuff from  the
  1831.     semantics  stack  that  corresponds to the current production rule
  1832.     right members. 
  1833.     
  1834.     The "tsemp" pointer is returned by apply in general.   If  you  do
  1835.     nothing  else  after  "make_tree"  is called, the apply calls will
  1836.     simply construct a big AST from your input stream.  Eventually the
  1837.     goal production is invoked and the tree will represent  everything
  1838.     in your input stream. 
  1839.     
  1840.     Embellishments
  1841.     
  1842.  
  1843.  
  1844.     QPARSER+ User Manual                                       page 36
  1845.  
  1846.  
  1847.     Function  "make_tree"  has several important embellishments, which
  1848.     you need to pay attention to:
  1849.     
  1850.       1.  It doesn't consider any terminal token  as  a  child.    For
  1851.     example,  stuff  like  "+",  ":="  are  ignored,  and  don't get a
  1852.     placeholder in the tree. 
  1853.       2.  A single production with no tag is just skipped  over;  that
  1854.     is,  any  tree  rooted  in  B  will  simply  be attached A, when a
  1855.     production like A->B is applied.   If  the  single  production  IS
  1856.     tagged, a new node with a single child is created. 
  1857.       3.    A   production   with   no   tag   will  be  assigned  the
  1858.     general-purpose tag GENL_KIND.  You can sometimes figure out  what
  1859.     the node  is  without  knowing  the tag.  In general, we recommend
  1860.     that you assign a unique tag to EACH production, except for  those
  1861.     single productions that can be bypassed in the tree. 
  1862.       4.  An empty production, i.e.  one like
  1863.     
  1864.       A -> <empty>
  1865.     
  1866.     will have a NULL child, i.e.  position0[0] will be NULL for this. 
  1867.       5.   The  position0  indices  go  from left to right through the
  1868.     right members of a production as tree children, rather  than  from
  1869.     right to  left  as  stack  elements.   Remember that terminals are
  1870.     skipped in the count.  So the right-members in the production rule
  1871.     X -> A + B will be accessed like this in the AST:
  1872.     
  1873.        A:  root->position0[0]
  1874.        B;  root->position0[1]
  1875.     
  1876.     assuming that "root" is the pointer corresponding to X. 
  1877.     
  1878.     In order to make it easier to refer to the children, you  can  use
  1879.     these macros, found in SKELDECL.H:
  1880.     
  1881.       #define POSITION(N) usem.position0[(N)-1]
  1882.       #define CHILD usem.position0[0]
  1883.       #define LEFT  usem.position0[0]
  1884.       #define RIGHT usem.position0[1]
  1885.       #define ONE   usem.position0[0]
  1886.       #define TWO   usem.position0[1]
  1887.       #define THREE usem.position0[2]
  1888.       #define FOUR  usem.position0[3]
  1889.       #define FIVE  usem.position0[4]
  1890.     
  1891.     Tree Walking
  1892.     
  1893.     We  said  that  an  AST  makes  it easier to generate code than by
  1894.     constructing special semantics code for each production rule.  The
  1895.     general idea is to write a tree-walking function  that  accepts  a
  1896.     semrectype pointer  as  its  argument.  It then branches through a
  1897.     switch statement on the possible tags that can be associated  with
  1898.     the root.  You then decide what to do on each node. 
  1899.     
  1900.     The biggest advantage of this approach is that the code generation
  1901.     actions can occur through your tree-walk in a different order than
  1902.  
  1903.  
  1904.     QPARSER+ User Manual                                       page 37
  1905.  
  1906.  
  1907.     that in  which  they happen to pop up in the bottom-up parse.  You
  1908.     can decide whether to inspect the right member ahead of  the  left
  1909.     member,  for example, even though the left member gets built first
  1910.     in the parser. 
  1911.     
  1912.     For example, if code generation requires  a  register  assignment,
  1913.     the  assignment algorithm can't be expected work in the same order
  1914.     as the production rule parsing. 
  1915.     
  1916.     Another advantage is that you can walk over a tree  several  times
  1917.     if you  need  to  in  order  to get certain actions to take place. 
  1918.     This amounts to using multiple passes, except that you can control
  1919.     the passes to take place in only a limited section of the grammar. 
  1920.     
  1921.     A third advantage is that this approach  eliminates  most  of  the
  1922.     worry  about  freeing  invalid pointers or having a memory leak --
  1923.     virtually all the allocation and free operations  are  done  in  a
  1924.     safe fashion. 
  1925.     
  1926.     Recursive Tree-Walking Functions
  1927.     
  1928.     Tree-walking is done with a recursive function that accepts a tree
  1929.     node (a  "semrectype" pointer) as one of its arguments.  It may or
  1930.     may not return a value.  The art of designing such functions comes
  1931.     with practice.  The general idea is that you need to  decide  just
  1932.     what  the function is supposed to do in an abstract way, then make
  1933.     sure it does just that in each case. 
  1934.     
  1935.     A common case is for it  to  just  call  itself  on  one  or  more
  1936.     children.  You'll probably want to do this (but not always) on the
  1937.     left member of a recursive rule.  For example, if the rule is
  1938.     
  1939.       Expr -> Expr + Term #PLUS
  1940.     
  1941.     then  when  the  node  is  PLUS,  you  will  probably start with a
  1942.     recursive call on the LEFT child, followed a  call  on  the  RIGHT
  1943.     child, followed  by  whatever  stuff you want to do with the PLUS. 
  1944.     It's possible for the left and right children to return some mixed
  1945.     types, e.g.  double for the  left  and  integer  for  the  right.  
  1946.     That's  a  signal that the Term value requires a cast to double in
  1947.     order to finish the addition.  In Pascal,  the  "+"  operation  is
  1948.     overloaded as a string operation, so if one of the two things is a
  1949.     string,  the other one must be, too, and a string concatenation is
  1950.     called for. 
  1951.     
  1952.     CALC by Syntax Tree Evaluation
  1953.     
  1954.     This all sounds very complicated, but in fact, it's easier  to  do
  1955.     than fussing with specific production rule semantics.  Look at the
  1956.     grammar  file  CALCT.GRM,  which  is a fully worked-out calculator
  1957.     that behaves just like CALCC.GRM, except that  it  is  implemented
  1958.     through a tree-builder and tree-walker. 
  1959.     
  1960.     The tree-walker  is  function  "eval",  found  in CALCT.GRM.  This
  1961.     accepts a tree node as a semrectype pointer, and returns a  double
  1962.  
  1963.  
  1964.     QPARSER+ User Manual                                       page 38
  1965.  
  1966.  
  1967.     value.   Since  we  are  implementing  a calculator, a double will
  1968.     always be the "value" of any tree node.  If we  were  implementing
  1969.     something  else, such as a compiler, the return value might be the
  1970.     type of the tree as inferred from its members.    Function  "eval"
  1971.     will  be  called  from  within  itself,  and  also from "apply" on
  1972.     completion of one the "Stmt" production rules.  You can  find  the
  1973.     "eval" call associated with "Stmt" later in the grammar file. 
  1974.     
  1975.     Function  eval  immediately  switches  on the "semt" member of the
  1976.     semrectype struct.  "semt" will be  one  of  the  production  rule
  1977.     tags,  or  one  of the predefined tags IDENT, FIXED, FLOAT, STRNG,
  1978.     etc.  that are reserved for an <identifier>,  <number>,  <string>,
  1979.     etc.  See  decl.h for a complete list.  The tag GENL_KIND may show
  1980.     up here if you  haven't  tagged  all  your  non-single  production
  1981.     rules. 
  1982.     
  1983.     Finding the Switch Tags to Consider
  1984.     
  1985.     The  way to decide what tags need to be among your walker's switch
  1986.     list is to look at the derivations from the "apply" calls.  In our
  1987.     case, the apply eval calls are from the nonterminal  "Expr".    So
  1988.     you  need to see what "Expr" can derive -- it can derive a PLUS or
  1989.     a MINUS directly.  Through "Term", it can derive MPY and  DIVIDE.  
  1990.     Then  through  "Fact"  it  can  derive  UMINUS,  PARENS, VARIABLE,
  1991.     REALVAL or INTVAL.  So all these tags need to  appear  within  the
  1992.     switch statement.   Note that this chain is the result of having a
  1993.     series of untagged single productions in the rule set, i.e. 
  1994.     
  1995.       Expr -> Term
  1996.       Term -> Primary
  1997.     
  1998.     You can break the chain by tagging one of these, and catching  the
  1999.     tag in your tree-walker.  Then direct those calls somewhere else. 
  2000.     
  2001.     CALC Eval Switch Cases
  2002.     
  2003.     Now let's look at the "eval" switch cases.  Case PLUS is easy.  We
  2004.     call  eval  on  the left node (root->LEFT), then on the right node
  2005.     (root->RIGHT).  These are supposed to each return a double  value,
  2006.     and  we  want the double result of the "+" operation, so we return
  2007.     the sum of these function calls. 
  2008.     
  2009.     Case MINUS, MPY and DIVIDE work the same way,  except  that  we've
  2010.     put in  a  trap  to  catch a division by zero for the DIVIDE case. 
  2011.     (It would be best to also trap on overflow  and  underflow,  which
  2012.     could  happen  in  any of the operations, rather than on just this
  2013.     obvious one). 
  2014.     
  2015.     Case UMINUS is also easy.  The single  child  is  referred  to  by
  2016.     root->CHILD. 
  2017.     
  2018.     Case  PARENS,  INTVAL,  REALVAL and VARIABLE are also easy -- just
  2019.     return the value of root->CHILD as evaluated by eval. 
  2020.     
  2021.     Case IDENT requires that we extract the  identifier's  value  from
  2022.  
  2023.  
  2024.     QPARSER+ User Manual                                       page 39
  2025.  
  2026.  
  2027.     its symbol table.  It happens that we can depend on the identifier
  2028.     having  been  previously  assigned-to through code in the VARIABLE
  2029.     production rule, as we'll see later. 
  2030.     
  2031.     Case FIXED picks up the long value in root->usem.numval and  casts
  2032.     it to a double. 
  2033.     
  2034.     Case  FLOAT simply returns the root->usem.rval, which is already a
  2035.     double. 
  2036.     
  2037.     Case QUIT is thrown in for good measure.  Note that it falls  into
  2038.     the  "default"  case,  which should always be included so that you
  2039.     catch any omissions in the tree-walker cases.  This refers to  the
  2040.     "flags"   array,   which   is   guaranteed  to  deliver  a  string
  2041.     representation of any of the legal root->semt values.   In  CALCT,
  2042.     there's  no  way  that  QUIT  can  be passed to eval, nor will the
  2043.     "default" case ever be called, so these could be "assert" calls or
  2044.     just left out completely. 
  2045.     
  2046.     The APPLY Actions
  2047.     
  2048.     Now let's look at the apply function actions, which of course  are
  2049.     associated with production  rules.    There isn't much.  Note that
  2050.     most of the rules are tagged, but have no semantics.   This  means
  2051.     that  the  rule  will  get  built  into a syntax tree with nothing
  2052.     further required from you. 
  2053.     
  2054.     However, we need to do something on the PRTVAL and ASSIGN1  rules,
  2055.     and in particular to stop the tree building on these rules. 
  2056.     
  2057.     In  the  PRTVAL  case, the required action is to evaluate the Expr
  2058.     tree, through the call
  2059.     
  2060.       evalue= eval(tsemp->CHILD);
  2061.     
  2062.     Assuming that  goes  well  (why  shouldn't  it?),  the  result  is
  2063.     printed.  The tree is then disposed by a call to "disp_sem", which
  2064.     incidentally returns NULL so that tsemp returns something sensible
  2065.     from the apply call. 
  2066.     
  2067.     In the  ASSIGN1  production rule, we again evaluate the Expr tree. 
  2068.     The value will then be associated with  the  <identifier>  by  the
  2069.     next two statements:
  2070.     
  2071.       tsemp->LEFT->usem.symp->usym.value= evalue;  /* the assignment */
  2072.       tsemp->LEFT->usem.symp->symt= REALVAR;  /* defined */
  2073.     
  2074.     What are  these doing?  The <identifier> is always associated with
  2075.     a symbol table entry, accessed through the pointer
  2076.     
  2077.       tsemp->LEFT->usem.symp
  2078.     
  2079.     where LEFT refers to the <identifier> position in the rule.    The
  2080.     value  associated with this identifier is then set to the recently
  2081.     evaluated tree value, evalue.  The type of the value "symt" in the
  2082.  
  2083.  
  2084.     QPARSER+ User Manual                                       page 40
  2085.  
  2086.  
  2087.     symbol table is set to REALVAR -- this states that the  identifier
  2088.     has appeared  on  the left side of an assignment.  We'll come back
  2089.     to this later. 
  2090.     
  2091.     The remaining two lines are the same as for the PRTVAL case, since
  2092.     we want our calculator to print  the  result  of  the  assigned-to
  2093.     variable. 
  2094.     
  2095.     Note that  the  AST is unaffected by the two assignments.  We have
  2096.     merely changed two entries in the symbol table attribute  for  the
  2097.     <identifier>.   The  tree  can be disposed as usual, having served
  2098.     its purpose. 
  2099.     
  2100.     The VARIABLE production rule  has  a  trap  for  an  unassigned-to
  2101.     identifier.  The symptom of such an identifier is that it does not
  2102.     have the  REALVAR  attribute  set in its symbol table entry.  Note
  2103.     that this attribute is set when the identifier is  assigned-to  in
  2104.     the ASSIGN1  production  semantics.    So  we  complain  about the
  2105.     problem, then set up the identifier with some reasonable values in
  2106.     case someone else notices the problem somewhere else in the code. 
  2107.     
  2108.     Running CALCT
  2109.     
  2110.     You can try out the tree-builder calc as follows:
  2111.     
  2112.       1.  Delete the *.TBL,  *.OBJ  and  *.EXE  files  in  the  CSKELS
  2113.     subdirectory.  This will force "make" to rebuild them. 
  2114.       2.  Run  make  with  the option GRM=CALCT.  (With the Turbo make
  2115.     file, you need to set GRM=CALCT in the makefile,  since  it  won't
  2116.     take this as an external option.)
  2117.       3.  The  result is MAIN.EXE.  It should execute and perform in a
  2118.     manner similar to CALCC.  You won't see any difference, unless you
  2119.     use the debugging tools to explore the stack and the trees  rooted
  2120.     in the stack. 
  2121.     
  2122.     Debugging Tree Programs
  2123.     
  2124.     We  recommend  using your compiler debugger if you have to debug a
  2125.     QTREES program.    Set  a  breakpoint  on  one  of  the  recursive
  2126.     evaluators,  or on one of the switch case statements to see what's
  2127.     happening.  If your debugger will allow you to  call  a  function,
  2128.     call "print_tree" with a pointer to some tree root.  It'll display
  2129.     what's in the tree. 
  2130.     
  2131.     You  can  also  get  the  "print_tree"  display  from  the Qparser
  2132.     debugging line.  You need to  trap  on  a  production  rule  apply
  2133.     operation with  a  debugging level or 2 or greater.  When the trap
  2134.     occurs, select the "S(tack" option, then move up or  down  in  the
  2135.     stack with the + or - key.  You'll see an asterisk (*) next to the
  2136.     selected stack  item.  Then press T(ree to display the tree rooted
  2137.     in that position in the stack.  You can move down into the tree by
  2138.     selecting one of the numbers shown at the end of the prompt -- "1"
  2139.     means choose the left-most nonterminal, "2", the one next  to  it,
  2140.     etc.  ("1"  corresponds to "position0[0]").  Move back up the tree
  2141.     by pressing "U".  With enough "U" presses, you'll be back  at  the
  2142.  
  2143.  
  2144.     QPARSER+ User Manual                                       page 41
  2145.  
  2146.  
  2147.     stack level. 
  2148.     
  2149.     For more information, consult the help facility, which you can get
  2150.     at any time by pressing "?". 
  2151.     
  2152.     Tree Walking Hints
  2153.     
  2154.     The  decisions  you  need  to  make  when designing a tree-walking
  2155.     evaluator are these:
  2156.     
  2157.       1.  On which production should the tree-building be  interrupted
  2158.     so that you can apply a tree-walker?  We recommend an interruption
  2159.     on  each  declaration  --  which  may  result in declaring several
  2160.     identifiers. 
  2161.       If you care about global optimizations within  a  function,  you
  2162.     may  want  to build a tree for an entire function, so that you can
  2163.     do  an  intelligent   register   assignment,   look   for   common
  2164.     subexpressions, etc.    That  also makes it easy to expand control
  2165.     statements  like  "while...do"  or  "for  (){...}"  by  calling  a
  2166.     statement tree-walker. 
  2167.       In  any  case,  we recommend building a tree for each assignment
  2168.     statement or expression.  Usually (in a compiler) you need to walk
  2169.     through such a tree in order to  establish  the  types,  eliminate
  2170.     constant expressions, etc., before generating code segments. 
  2171.     
  2172.       2.  What  should  the  tree-walkers  do?  Again, much depends on
  2173.     your imagination.  We've demonstrated a simple numeric evaluator.  
  2174.     The extension of that to a code generator is fairly easy -- and in
  2175.     fact  is  one of the worked-out examples in the registered version
  2176.     of Qparser+. 
  2177.       You  will  probably  want  a  walker  that  evaluates   constant
  2178.     subexpressions.   Another one might assign types to all the nodes,
  2179.     based on casts and the  implicit  type  rules  of  the  language.  
  2180.     Another  one  might  reorganize  parts of the tree around a set of
  2181.     rules that facilitates optimizations, for example, moving all  the
  2182.     constants to  a  canonical  location  for  commutative operations. 
  2183.     Another one might perform register assignments.  Yet  another  one
  2184.     will  generate code for the control statements -- it will no doubt
  2185.     call an expression evaluator when it runs into expressions. 
  2186.     
  2187.       3.  What cases must be tree-walker deal with?   We've  explained
  2188.     above that you need to trace a derivation through from one or more
  2189.     nonterminals.   Start  with  the  nonterminals  that appear in the
  2190.     "apply" or other root calls to your evaluator.  You need  all  the
  2191.     tags that can appear in a derivation chain stemming from those. 
  2192.       If  you  choose  to  stop  calling your tree-walker on some tag,
  2193.     that's OK -- it just won't penetrate that tree.  If  you  do,  you
  2194.     need  to  include  the  tags that come from that production rule's
  2195.     left member. 
  2196.     
  2197.     Hints on Identifier Attributes
  2198.     
  2199.     We've pointed out that  Qparser  assigns  every  identifier  to  a
  2200.     symbol table slot upon its first appearance.  The attribute tag is
  2201.     the "symt" field of the symtabtype struct, and is USER by default. 
  2202.  
  2203.  
  2204.     QPARSER+ User Manual                                       page 42
  2205.  
  2206.  
  2207.     
  2208.     The   identifier  assignment  is  performed  with  the  "make_sym"
  2209.     function -- you can see where it happens by  looking  at  function
  2210.     get_symbol in  skellex.c.    "make_sym"  will accept an identifier
  2211.     already in the symbol table, even though it may  be  at  a  deeper
  2212.     scope than  you  wish.    The theory is that the reference for the
  2213.     sake of consistency.  may be to the higher-scope  identifier,  for
  2214.     example, a reference within a function to a global variable. 
  2215.     
  2216.     If  you  discover  through your tree-walker that the identifier is
  2217.     being declared, you need to pay attention to this:
  2218.     
  2219.       1.  If the identifier's attribute is USER, it means it has  been
  2220.     seen for  the  first time in this context.  You can safely set its
  2221.     attributes to something else appropriate to your declaration,  for
  2222.     example, change its symt to REALVAR or something. 
  2223.       2.   Or,  if  the identifier has some other attribute, its scope
  2224.     level will probably also be smaller than the current  scope  level
  2225.     "clevel".   This  means  it  was previously declared in a covering
  2226.     scope.  If this is a new declaration,  you  MUST  call  "forcesym"
  2227.     with  the  symbol  in  order  to  push  a  second  instance of the
  2228.     identifier into the symbol  table,  then  set  up  its  attributes
  2229.     appropriately. 
  2230.       3.   At  the  end  of a scope, you need to decide what has to be
  2231.     saved and what can be discarded.  If the names are associated with
  2232.     function parameters, you usually need to save something  in  order
  2233.     to check  the  parameter  types against subsequent function calls. 
  2234.     If the names are truly local in scope, you can safely get  rid  of
  2235.     all of it. 
  2236.       4.  Function "clearsym" will unlink all the symbol table entries
  2237.     at and  below  a  given  scope  level  for  you.  It will free the
  2238.     symtabtype struct and the "sym" string as well.  However, it  will
  2239.     NOT free any other space associated with the attribute, unless you
  2240.     explicitly require    this.        See    file    "syms.c",    and
  2241.     "free_symtab_item" in particular.  So you need to  embellish  this
  2242.     if you've attached other information to the symtabtype struct, and
  2243.     you want to avoid memory leaks. 
  2244.     
  2245.     Releasing an AST
  2246.     
  2247.     Releasing  the  heap space occupied by an AST is easy -- just call
  2248.     "disp_sem" on the root node.   Usually  this  is  done  within  an
  2249.     "apply"  case, but you can do this within a tree-walker as well if
  2250.     you like.  Of course, you need to make sure that any  pointers  to
  2251.     the  tree  node, or any node within it, are neutralized by setting
  2252.     them to NULL.  In particular, you should NOT set up some auxiliary
  2253.     data structures with pointers into a tree somewhere, then  release
  2254.     the tree  space.   Learn to live with the AST and stack structures
  2255.     instead. 
  2256.     
  2257.     Note that if you embellish semrectype with additional  structures,
  2258.     you also  need  to  extend  "new_sem", "disp_sem", and "dump_sem". 
  2259.     "dump_sem" requires some thought -- you  probably  won't  want  to
  2260.     display  everything  in  a  complicated  node  when displaying the
  2261.     stack.  It's best to decide where to cut off the display,  or  how
  2262.  
  2263.  
  2264.     QPARSER+ User Manual                                       page 43
  2265.  
  2266.  
  2267.     to condense it. 
  2268.     
  2269.  
  2270.  
  2271.     QPARSER+ User Manual                                       page 44
  2272.  
  2273.  
  2274.     Chapter 11.  Miscellaneous Topics
  2275.     
  2276.     Skeleton File Semantics
  2277.     
  2278.     The skeleton files are divided into sections of code that are just
  2279.     copied to their target files, and other sections that get expanded
  2280.     by LR1P,  according  to information derived from the grammar.  The
  2281.     sections to be expanded are covered by the special bracket codes
  2282.     
  2283.       {## ...  ##}
  2284.     
  2285.     which are recognized by LR1P.  Everything outside  those  brackets
  2286.     are simply  copied  to the target file by LR1P.  Everything inside
  2287.     is interpreted as a Pascal-like source  code  generator  language,
  2288.     with a precisely defined syntax. 
  2289.     
  2290.     The  registered  version  of Qparser has a complete explanation of
  2291.     the code generation language syntax, so  that  you  can  construct
  2292.     your own  generator  forms.    It's  based on a special variant of
  2293.     Pascal, and is quite powerful.  You can figure out  what  most  of
  2294.     the  forms  do by comparing skeleval.c with eval.c, and trying out
  2295.     some cases on your own. 
  2296.     
  2297.     The more difficult forms are associated with the  parser  tables.  
  2298.     They will be hard to decipher without some help. 
  2299.     
  2300.     For most purposes, you won't need to touch these generator forms. 
  2301.     
  2302.     Help Functions
  2303.     
  2304.     We  pointed  out  that  when  your parser or translator is finally
  2305.     linked and ready for execution, you can invoke the debug functions
  2306.     by either using the option "-d1" or using character  "!"  in  your
  2307.     input.  When the debug line appears:
  2308.     
  2309.       [idebug] ?(help, N(ame, A(ll, D(bug level,
  2310.         P(rodTrap, T(race, L(ex, S(tack, ENTER? 
  2311.     
  2312.     typing  a  question  mark "?" should produce several screensful of
  2313.     help information about your environment.  In this case,  the  help
  2314.     will be  about  this  particular  set  of options.  The first help
  2315.     report will give you some topics that you can enter by  typing  ?  
  2316.     followed by the option name. 
  2317.     
  2318.     Gramchk
  2319.     
  2320.     Program GRAMCHK.EXE  hasn't  been  described  yet.    Like LR1, it
  2321.     accepts a grammar file name.  Unlike LR1, it's  just  designed  to
  2322.     give  you lots of information about the grammar that would be hard
  2323.     to figure out otherwise. 
  2324.     
  2325.     An accurate description of GRAMCHK's services can be  obtained  by
  2326.     running GRAMCHK  with  no  parameters.    You'll get a list of the
  2327.     options that it understands.  To get more information on  any  one
  2328.     of  the  options,  run  it  with the option tagged with a question
  2329.  
  2330.  
  2331.     QPARSER+ User Manual                                       page 45
  2332.  
  2333.  
  2334.     mark, for example,
  2335.     
  2336.        gramchk -t? 
  2337.     
  2338.     will generate a screen of information about the -t option. 
  2339.     
  2340.  
  2341.  
  2342.     QPARSER+ User Manual                                       page 46
  2343.  
  2344.  
  2345.     Chapter 12.  Debugging a Translator
  2346.     
  2347.     Qparser should always produce a valid parser if your  grammar  can
  2348.     get  through  gramchk  and lr1 without complaints, and if you have
  2349.     not changed any of the base CSKELS files. 
  2350.     
  2351.     However, a parser alone isn't usually what you  want.    When  you
  2352.     start  adding  semantics  operations, particularly operations that
  2353.     call for leaving material in the stack and later using it, you may
  2354.     introduce an error that could cripple the system. 
  2355.     
  2356.     Here are some tips on developing reliable Qparser programs:
  2357.     
  2358.       * You should compile with the DEBUG option defined.   This  will
  2359.     help  you  trace  the  stack  and parsing operations while setting
  2360.     breakpoints and viewing program variables. 
  2361.     
  2362.       * Keep DEBUG.C up to date so that the symtabtype and  semrectype
  2363.     are always reported according to your latest revision.  Similarly,
  2364.     check  new_sem,  new_sym,  disp_sem  and  disp_sym for the correct
  2365.     disposition of the structs. 
  2366.     
  2367.       * Check the terminal and nonterminal list by using  option  "-t"
  2368.     in LR1 or GRAMCHK.  A common error is omitting the definition of a
  2369.     nonterminal  as a production rule, causing it to appear to Qparser
  2370.     as a terminal.  GRAMCHK will also warn  you  of  production  rules
  2371.     that seem to go nowhere or are not connected to the chain of rules
  2372.     in any way -- this is also a symptom of some grammar error. 
  2373.     
  2374.       * Pay  attention  to  the reports from LR1 and GRAMCHK.  LR1 may
  2375.     complain about syntax errors -- which  you  should  repair  --  or
  2376.     state conflicts.  A state conflict can be difficult to repair, and
  2377.     is  beyond  the  scope of this little document to explain and help
  2378.     you to correct.  LR1 will nevertheless generate a parser,  but  it
  2379.     may not  behave  as  you  expect.    If  GRAMCHK complains about a
  2380.     variable name that has been overloaded or some other problem,  you
  2381.     should  look  into  it,  not just assume that everything will work
  2382.     anyway -- it won't. 
  2383.     
  2384.       * If your compiler supports  prototypes,  and  checks  parameter
  2385.     types,  you  should enable that option and change the skeleton and
  2386.     other files so that they make use of this important  type-checking
  2387.     feature.  Then make sure your C files are type-clean. 
  2388.     
  2389.       *  If  you've  changed  the internals of the lexical analyzer --
  2390.     file SKELLEX.C or LEX.C --  you  should  first  test  the  lexical
  2391.     analyzer separately.    That  file  can  be compiled separately by
  2392.     compiling it with the macro  TEST  defined.    It  will  become  a
  2393.     program in its own right, designed to accept a file of characters,
  2394.     then report  the tokens that it sees.  Only after you've made sure
  2395.     that the lexical analyzer is working correctly should you  attempt
  2396.     to move on to generating and testing a parser. 
  2397.     
  2398.       * Develop your translator in these stages, making sure that each
  2399.     stage  is  correct  before moving on to the next stage: a) lexical
  2400.  
  2401.  
  2402.     QPARSER+ User Manual                                       page 47
  2403.  
  2404.  
  2405.     analyzer, if you've changed SKELLEX.C in some way, b)  grammar  --
  2406.     it  should  be  complete,  free  of LR1 and GRAMCHK complaints and
  2407.     correctly represent your  intentions,  c)  symbol  attributes  and
  2408.     correct   scope   rules,   d)   symbol  declarations,  e)  control
  2409.     statements, f) function calls, g) executable statements. 
  2410.     
  2411.       * Qparser should correctly call  the  apply  function  with  the
  2412.     appropriate  state  number  and  that  in  turn will drop into the
  2413.     appropriate switch case number.  If you aren't convinced of  this,
  2414.     follow  the  stack  traces with some simple input streams and make
  2415.     sure that that mechanism is performing correctly before adding any
  2416.     semantics code to "apply".  It's all driven by tables in the  code
  2417.     segment, which can still get corrupted by bugs in most systems. 
  2418.     
  2419.       *  Use  the  compiler  debugger  to  plant  a  breakpoint  on  a
  2420.     particular case in "apply" to trap on one or another apply action. 
  2421.     If your debugger can execute a function call  from  a  breakpoint,
  2422.     execute  "idebug",  which  will allow you to interactively examine
  2423.     the stack, symbol table variables, etc.  It draws  on  the  global
  2424.     stack information, and requires no parameters. 
  2425.     
  2426.       *  Make  your  program  report what it's doing with extra printf
  2427.     statements covered by some test option, i.e.  TEST1, TEST2,  etc.  
  2428.     Prepare some input files with situations that you are testing that
  2429.     will set up the symbol table and other environment so that you can
  2430.     track down  the  problem  you are having.  Insert "idebug()" calls
  2431.     freely where you suspect trouble; they will help  you  track  them
  2432.     down. 
  2433.     
  2434.       * Pay  special attention to the use of the heap.  In particular,
  2435.     please remember that Qparser allocates each of the following,  and
  2436.     it's up to you to dispose of them at the appropriate time:
  2437.     
  2438.          a.   One  symtabtype  type  on  the  appearance  of  a  fresh
  2439.     identifier.  An identifier appearing more than once is  associated
  2440.     with the  same  symtabtype.    Call  "free_symtab_item"  to free a
  2441.     symtabtype.  Call "clearsym" to remove a set of  identifiers  from
  2442.     the symbol table when falling out of some inner scope. 
  2443.     
  2444.          b.  One  "char*  sym"  type  within each symtabtype.  This is
  2445.     freed with the symtabtype struct by free_symtab_type. 
  2446.     
  2447.          c.  One semrectype in the semantics stack "semstack" for each
  2448.     appearance of  <identifier>,  <real>,  <integer>,  <character>  or
  2449.     <string> in  the  input stream.  There is NO automatic disposal of
  2450.     these -- you must dispose of them at the appropriate time, usually
  2451.     after you have made use of their information in an  "apply"  call,
  2452.     just before  its  return.   Call "disp_sem" to free any semrectype
  2453.     struct.  It is also designed to free an entire tree  rooted  in  a
  2454.     semrectype. 
  2455.          d.   One  "char*  strx"  type on each appearance of a literal
  2456.     string in  the  input  stream.    This  is   associated   with   a
  2457.     "semrectype" and is normally freed through the "disp_sem" call. 
  2458.     
  2459.       *  Use functions "new_sem", "new_sym", "disp_sem" and "disp_sym"
  2460.  
  2461.  
  2462.     QPARSER+ User Manual                                       page 48
  2463.  
  2464.  
  2465.     to allocate and free a semrectype or symtabtype.  Keep them up  to
  2466.     date with  changes  you make in those structs.  This is much safer
  2467.     than "malloc" and "free", since  new_sem  and  new_sym  initialize
  2468.     critical  struct members, and the "disp" functions handle NULL and
  2469.     embedded allocated material safely. 
  2470.     
  2471.       If your compiler has a library function for  checking  the  heap
  2472.     status,  we  urge  that  you  enable  it  and pay attention to its
  2473.     complaints.  The nature of this parsing system is  such  that  you
  2474.     will  be  allocating heap space in one section of the program, but
  2475.     freeing it somewhere else, such that  it  won't  be  obvious  that
  2476.     every allocated  object  is freed exactly once.  We also recommend
  2477.     that you be fastidious  about  setting  pointers  to  NULL  before
  2478.     allocating space for them, and after freeing them, if they live on
  2479.     thereafter.   You  can then test a pointer for NULL before freeing
  2480.     it. 
  2481.     
  2482.  
  2483.  
  2484.     QPARSER+ User Manual                                       page 49
  2485.  
  2486.  
  2487.     Chapter 13.  Registration and Support
  2488.     
  2489.     We urge that you register your Qparser+ software with QCAD Systems
  2490.     by filling out the order blank file form (file  ORDRFORM.TXT)  and
  2491.     returning it  with  the requested payment.  Your registration will
  2492.     entitle you to the full version, which  contains  more  worked-out
  2493.     examples and  grammars,  and skeletons in Pascal as well as C.  It
  2494.     comes with a typeset 170-page user manual.  You'll be entitled  to
  2495.     limited  free phone support to answer any questions you have or to
  2496.     help you track down problems with your translator. 
  2497.     
  2498.     You'll also learn more about using abstract  syntax  trees,  which
  2499.     are  the  recommended  way  to develop a translator product of any
  2500.     reasonable complexity. 
  2501.     
  2502.     The Qparser binaries (LR1.EXE, OPT.EXE, LR1P.EXE, GRAMCHK.EXE) can
  2503.     be ported to any computer system with a K&R or ANSI C compiler  by
  2504.     purchasing a license for the source code.  A make file is provided
  2505.     for  Unix  system  V,  and  the  product has been tested on a wide
  2506.     variety of Unix platforms, as well as VMS and IBM platforms.   For
  2507.     more details, please contact us below. 
  2508.     
  2509.     If you need support, please write or FAX us at the following:
  2510.     
  2511.         QCAD Systems
  2512.         1164 Hyde Avenue
  2513.         San Jose, CA 95129
  2514.         FAX/voice message: 408 727 6884
  2515.     
  2516.     Even if you don't want to register, let us know with a postcard or
  2517.     a  one  page  FAX that you've acquired this shareware edition, and
  2518.     what you think of it.  We'll be working on improvements over time,
  2519.     and are always interested in customer response to the product. 
  2520.     
  2521.     Version Number
  2522.     
  2523.     By running one of the executables with no parameters, the  version
  2524.     number is reported.  Please give us the version number if you want
  2525.     to  report  a  problem  or  a  suggestion -- it's 4.8.3 as of this
  2526.     writing. 
  2527.     
  2528.  
  2529.  
  2530.     QPARSER+ User Manual                                       page 50
  2531.  
  2532.  
  2533.     Chapter 14.  For Additional Reading
  2534.     
  2535.     The registered version includes a  170-page  printed  manual  that
  2536.     describes  all  the  Qparser internals, and an introduction to the
  2537.     underlying theory.  There are more worked-out examples,  and  tips
  2538.     on compiler development. 
  2539.     
  2540.     Dr.   Barrett's  compiler construction book "Compiler Construction
  2541.     -- Theory and Practice" was out of print at the  time  of  writing
  2542.     this.  A new edition should be available from MacMillan publishers
  2543.     in early 1994. 
  2544.     
  2545.     Allen Holub,  "Compiler  Design  in  C", Prentice-Hall.  We highly
  2546.     recommend this  book  for  the  professional  language  designer.  
  2547.     Virtually  all the theoretical material is provided in the form of
  2548.     tested C programs, which can be purchased separately  in  diskette
  2549.     form.   It's  well  written  and  full  of  examples  and  example
  2550.     programs. 
  2551.     
  2552.     Aho  and  Ullman,  "The  Theory  of   Parsing,   Translation   and
  2553.     Compiling", two   volumes,  Prentice-Hall.    This  is  a  classic
  2554.     reference volume  on  the  theory.    Those  unfamiliar  with  the
  2555.     structure  of  proofs  and abstract language notation will find it
  2556.     difficult to read. 
  2557.     
  2558.     Kernighan  and  Ritchie,  "The  C  Programming  Language",  second
  2559.     edition, Prentice-Hall.  Contains a C grammar that is LR(1). 
  2560.     
  2561.     Bjarne  Stroustrup,  "The C++ Programming Manual", second edition,
  2562.     Addison-Wesley.  Contains a C++ grammar that is LR(1). 
  2563.  
  2564.