home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / pgmutil / val_link.zip / ORDER.C < prev    next >
Text File  |  1989-02-18  |  26KB  |  649 lines

  1. /*                                 ORDER.C                                 */
  2.  
  3. /*
  4.   order_expression::  term   {('or'  | '+' | '|') term}
  5.               term::  factor {('and' | '*' | '&') factor}
  6.             factor::  ('!' | '~' | '-' | 'not')* primary
  7.            primary::  'true'                       |
  8.                       'false'                      |
  9.                       ('segment' '[' name ']' )    |
  10.                       ('class'   '[' name ']' )    |
  11.                       ('group'   '[' name ']' )    |
  12.                       ( '(' order_expression ')' )
  13. */
  14.  
  15. /*+-------------------------------------------------------------------------+
  16.   |                                                                         |
  17.   |                          align_active_segment                           |
  18.   |                                                                         |
  19.   +-------------------------------------------------------------------------+*/
  20. void align_active_segment()
  21. BeginDeclarations
  22. bit_32                                 gap;
  23. lseg_ptr                               lseg;
  24. #define Lseg                           (*lseg)
  25. bit_32                                 mask;
  26. public_entry_ptr                       pub;
  27. #define Pub                            (*pub)
  28. EndDeclarations
  29. BeginCode
  30.  If (*Active_segment.lsegs.first).align IsZero
  31.   Then /* Don't align absolute segments */
  32.    return;
  33.   EndIf;
  34.  Active_segment.highest_uninitialized_byte = 0L;
  35.  TraverseList(Active_segment.lsegs, lseg)
  36.   BeginTraverse
  37.    LoopIf(Lseg.align Is absolute_segment);
  38.    If Active_segment.combine Is common_combine
  39.     Then  /* Finally, we know how big the common area is, so allocate
  40.              memory for it.  */
  41.      Lseg.data = allocate_memory(Addr(static_pool), Lseg.length);
  42.     EndIf;
  43.    mask                    = align_mask[Lseg.align];
  44.    gap                     = AlignmentGap(next_available_address, mask);
  45.    next_available_address += gap;
  46.    Lseg.address            = next_available_address;
  47.    next_available_address += Lseg.length;
  48.    If Lseg.highest_uninitialized_byte IsNotZero
  49.     Then
  50.      highest_uninitialized_byte                =
  51.      Active_segment.highest_uninitialized_byte =
  52.       Lseg.address + Lseg.highest_uninitialized_byte;
  53.     EndIf;
  54.   EndTraverse;
  55.  Active_segment.address = (*Active_segment.lsegs.first).address;
  56.  Active_segment.length  = (*Active_segment.lsegs.last).address +
  57.                           (*Active_segment.lsegs.last).length -
  58.                           Active_segment.address;
  59.  If Active_segment.highest_uninitialized_byte IsZero
  60.   Then
  61.    Active_segment.highest_uninitialized_byte =
  62.     (*Active_segment.lsegs.first).address;
  63.   EndIf;
  64.  If (Active_segment.owning_group IsNotNull) AndIf
  65.     ((*Active_segment.owning_group).first_segment IsNull)
  66.   Then
  67.    (*Active_segment.owning_group).first_segment = active_segment;
  68.   EndIf;
  69.  If (DOSSEG.val IsTrue) AndIf 
  70.     (Active_segment.owning_group IsNotNull) AndIf
  71.     ((*Active_segment.owning_group).group_name Is DGROUP_lname)
  72.   Then
  73.    If (edata_segment IsNull) AndIf
  74.       (Active_segment.class_name Is BSS_lname)
  75.     Then
  76.      edata_segment = active_segment;
  77.      pub = lookup_public(6, (byte *) "_edata", 0);
  78.      If (Pub.type_entry Is external) OrIf (Pub.type_entry Is unused)
  79.       Then
  80.        Pub.type_entry      = internal;
  81.        Pub.Internal.group  = Active_segment.owning_group;
  82.        Pub.Internal.lseg   = Active_segment.lsegs.first;
  83.        Pub.Internal.frame  = 0;
  84.        Pub.Internal.offset = 0;
  85.       Else
  86.        linker_error(4, "Could not generate symbol \"_edata\" "
  87.                        "when \"/DOSSEG\" set\n"
  88.                        "because it was explicitly defined.\n");
  89.       EndIf;
  90.     EndIf;
  91.    If (end_segment IsNull) AndIf
  92.       (Active_segment.class_name Is STACK_lname)
  93.     Then
  94.      end_segment = active_segment;
  95.      pub = lookup_public(4, (byte *) "_end", 0);
  96.      If (Pub.type_entry Is external) OrIf (Pub.type_entry Is unused)
  97.       Then
  98.        Pub.type_entry      = internal;
  99.        Pub.Internal.group  = Active_segment.owning_group;
  100.        Pub.Internal.lseg   = Active_segment.lsegs.first;
  101.        Pub.Internal.frame  = 0;
  102.        Pub.Internal.offset = 0;
  103.       Else
  104.        linker_error(4, "Could not generate symbol \"_end\" "
  105.                        "when \"/DOSSEG\" set\n"
  106.                        "because it was explicitly defined.\n");
  107.       EndIf;
  108.     EndIf;
  109.   EndIf;
  110.  return;
  111. EndCode
  112. #undef Lseg
  113. #undef Pub
  114.  
  115. /*+-------------------------------------------------------------------------+
  116.   |                                                                         |
  117.   |                            get_order_token                              |
  118.   |                                                                         |
  119.   +-------------------------------------------------------------------------+*/
  120. void get_order_token()
  121. BeginDeclarations
  122. EndDeclarations
  123. BeginCode
  124.  While token_break_char Is ' '
  125.   BeginWhile
  126.    order_token_get_char();
  127.   EndWhile;
  128.  copy_string(token, null_string);
  129.  If IsIdentifier(token_break_char)
  130.   Then
  131.    While IsIdentifier(token_break_char)
  132.     BeginWhile
  133.      concat_char_to_string(token, token_break_char);
  134.      order_token_get_char();
  135.     EndWhile;
  136.    lowercase_string(token);
  137.   Else
  138.    If token_break_char Is '['
  139.     Then
  140.      While token_break_char IsNot ']'
  141.       BeginWhile
  142.        concat_char_to_string(token, token_break_char);
  143.        order_token_get_char();
  144.       EndWhile;
  145.      order_token_get_char();
  146.      If case_ignore.val
  147.       Then
  148.        lowercase_string(token);
  149.       EndIf;
  150.     Else
  151.      concat_char_to_string(token, token_break_char);
  152.      order_token_get_char();
  153.     EndIf;
  154.   EndIf;
  155.  return;
  156. EndCode
  157.  
  158. /*+-------------------------------------------------------------------------+
  159.   |                                                                         |
  160.   |                         order_and_align_segments                        |
  161.   |                                                                         |
  162.   +-------------------------------------------------------------------------+*/
  163. void order_and_align_segments()
  164. BeginDeclarations
  165. byte_ptr                               start_of_expression;
  166. EndDeclarations
  167. BeginCode
  168.  order_start_time = Now;
  169. /*+-------------------------------------------------------------------------+
  170.   |                                                                         |
  171.   | Before we can start ordering segments, we have to get some housekeeping |
  172.   | done first.                                                             |
  173.   |                                                                         |
  174.   +-------------------------------------------------------------------------+*/
  175.  
  176.  order_prologue();
  177.  
  178. /*+-------------------------------------------------------------------------+
  179.   |                                                                         |
  180.   |              OK, lets get to ordering and aligning segments.            |
  181.   |                                                                         |
  182.   +-------------------------------------------------------------------------+*/
  183.  First(segments_ordered_list) =
  184.  Last(segments_ordered_list)  = Null;
  185.  highest_uninitialized_byte = 0L;
  186.  start_of_expression = String(ordering.val);
  187.  start_of_expression++;
  188.  While *start_of_expression IsNot '\000'
  189.   BeginWhile
  190.    ExitIf(segment_list.first IsNull);
  191.    First(segments_unordered_list) =
  192.    Last(segments_unordered_list)  = Null;
  193.    While segment_list.first IsNotNull
  194.     BeginWhile
  195.      Pop segment_list InTo active_segment EndPop;
  196.      order_expression_char_ptr = start_of_expression;
  197.      token_break_char = ' ';
  198.      If codeview_information_present AndIf
  199.         (((Active_segment.segment_name Is codeview_segment_TYPES) AndIf
  200.           (Active_segment.class_name Is codeview_class_DEBTYP)) OrIf
  201.          ((Active_segment.segment_name Is codeview_segment_SYMBOLS) AndIf
  202.           (Active_segment.class_name Is codeview_class_DEBSYM)))
  203.       Then /* Eat the codeview segment */
  204.       Else /* Process all non-codeview segments */
  205.        If order_expression()
  206.         Then
  207.          Insert active_segment AtEnd InList segments_ordered_list EndInsert;
  208.          align_active_segment();
  209.         Else
  210.          Insert active_segment AtEnd InList segments_unordered_list EndInsert;
  211.         EndIf;
  212.       EndIf;
  213.     EndWhile;
  214.    start_of_expression = order_expression_char_ptr;
  215.    start_of_expression--;
  216.    segment_list = segments_unordered_list;
  217.   EndWhile;
  218.  
  219. /*+-------------------------------------------------------------------------+
  220.   |                                                                         |
  221.   |          Make one final pass accepting all remaining segments.          |
  222.   |                                                                         |
  223.   +-------------------------------------------------------------------------+*/
  224.  
  225.  While segment_list.first IsNotNull
  226.   BeginWhile
  227.    Pop segment_list InTo active_segment EndPop;
  228.    Insert active_segment AtEnd InList segments_ordered_list EndInsert;
  229.    align_active_segment();
  230.   EndWhile;
  231.  segment_list = segments_ordered_list;
  232.  return;
  233. EndCode
  234.  
  235. /*+-------------------------------------------------------------------------+
  236.   |                                                                         |
  237.   |                            order_expression                             |
  238.   |                                                                         |
  239.   +-------------------------------------------------------------------------+*/
  240. bit_16 order_expression()
  241. BeginDeclarations
  242. bit_16                                 left_operand;
  243. EndDeclarations
  244. BeginCode
  245.  get_order_token();
  246.  left_operand = order_term();
  247.  While TokenIs(or_string)   OrIf
  248.        TokenIs(plus_string) OrIf
  249.        TokenIs(bar_string)
  250.   BeginWhile
  251.    get_order_token();
  252.    left_operand |= order_term();
  253.   EndWhile;
  254.  return(left_operand);
  255. EndCode
  256.  
  257. /*+-------------------------------------------------------------------------+
  258.   |                                                                         |
  259.   |                             order_factor                                |
  260.   |                                                                         |
  261.   +-------------------------------------------------------------------------+*/
  262. bit_16 order_factor()
  263. BeginDeclarations
  264. bit_16                                 unary_not;
  265. EndDeclarations
  266. BeginCode
  267.  unary_not = False;
  268.  While TokenIs(exclamation_string) OrIf
  269.        TokenIs(tilde_string)       OrIf
  270.        TokenIs(minus_string)       OrIf
  271.        TokenIs(not_string)
  272.   BeginWhile
  273.    get_order_token();
  274.    unary_not ^= True;
  275.   EndWhile;
  276.  return(unary_not ^ order_primary());
  277. EndCode
  278.  
  279. /*+-------------------------------------------------------------------------+
  280.   |                                                                         |
  281.   |                             order_primary                               |
  282.   |                                                                         |
  283.   +-------------------------------------------------------------------------+*/
  284. bit_16 order_primary()
  285. BeginDeclarations
  286. group_entry_ptr                        group;
  287. #define Group                          (*group)
  288. bit_16                                 operand;
  289. EndDeclarations
  290. BeginCode
  291.  If TokenIs(true_string)
  292.   Then
  293.    get_order_token();
  294.    return(True);
  295.   EndIf;
  296.  If TokenIs(false_string)
  297.   Then
  298.    get_order_token();
  299.    return(False);
  300.   EndIf;
  301.  If TokenIs(open_paren_string)
  302.   Then
  303.    operand = order_expression();
  304.    If TokenIs(close_paren_string)
  305.     Then
  306.      get_order_token();
  307.      return(operand);
  308.     Else
  309.      linker_error(8, "Expression syntax error:\n"
  310.                      "\t\"%Fs\"\n",
  311.                      String(ordering.val));
  312.     EndIf;
  313.   EndIf;
  314.  If TokenStartsWith(segment_string)
  315.   Then
  316.    get_order_token();
  317.    If *String(token) IsNot '['
  318.     Then
  319.      linker_error(8, "Expression syntax error:\n"
  320.                      "\t\"%Fs\"\n",
  321.                      String(ordering.val));
  322.     EndIf;
  323.    cut_string(token, 0, 1);
  324.    operand = match_pattern(token,
  325.                            string((*Active_segment.segment_name).symbol));
  326.    get_order_token();
  327.    return(operand);
  328.   EndIf;
  329.  If TokenStartsWith(group_string)
  330.   Then
  331.    get_order_token();
  332.    If *String(token) IsNot '['
  333.     Then
  334.      linker_error(8, "Expression syntax error:\n"
  335.                      "\t\"%Fs\"\n",
  336.                      String(ordering.val));
  337.     EndIf;
  338.    cut_string(token, 0, 1);
  339.    group = Active_segment.owning_group;
  340.    If group IsNull
  341.     Then
  342.      operand = False;
  343.     Else
  344.      operand =
  345.       match_pattern(token, string((*Group.group_name).symbol));
  346.     EndIf;
  347.    get_order_token();
  348.    return(operand);
  349.   EndIf;
  350.  If TokenStartsWith(class_string)
  351.   Then
  352.    get_order_token();
  353.    If *String(token) IsNot '['
  354.     Then
  355.      linker_error(8, "Expression syntax error:\n"
  356.                      "\t\"%Fs\"\n",
  357.                      String(ordering.val));
  358.     EndIf;
  359.    cut_string(token, 0, 1);
  360.    operand = match_pattern(token,
  361.                            string((*Active_segment.class_name).symbol));
  362.    get_order_token();
  363.    return(operand);
  364.   EndIf;
  365.  linker_error(8, "Expression syntax error:\n"
  366.                  "\t\"%Fs\"\n",
  367.                  String(ordering.val));
  368.  return(False);
  369. EndCode
  370. #undef Group
  371.  
  372. /*+-------------------------------------------------------------------------+
  373.   |                                                                         |
  374.   |                              order_prologue                             |
  375.   |                                                                         |
  376.   +-------------------------------------------------------------------------+*/
  377. void order_prologue()
  378. BeginDeclarations
  379. group_entry_ptr                        group;
  380. bit_32                                 length;
  381. lseg_ptr                               lseg;
  382. #define Lseg                           (*lseg)
  383. public_entry_ptr                       next_communal;
  384. bit_16                                 offset;
  385. public_entry_ptr                       pub;
  386. #define Pub                            (*pub)
  387. bit_16                                 size;
  388. EndDeclarations
  389. BeginCode
  390.  
  391. /*+-------------------------------------------------------------------------+
  392.   |                                                                         |
  393.   | Compute the address base for the executable image.  For .COM files this |
  394.   | will be 100H, for .EXE and .SYS files, this will be 0.                  |
  395.   |                                                                         |
  396.   +-------------------------------------------------------------------------+*/
  397.  
  398.  If comfile.val IsTrue
  399.   Then
  400.    address_base = 0x100L;
  401.   Else
  402.    address_base = 0L;
  403.   EndIf;
  404.  next_available_address = 0L;
  405.  
  406. /*+-------------------------------------------------------------------------+
  407.   |                                                                         |
  408.   |   The ordering expression for the segments must be known.  If the       |
  409.   |   "/ORDER:(expression)" switch was specified, then it will be used      |
  410.   |   regardless or whether or not "/DOSSEG" is set.  If the "/ORDER"       |
  411.   |   switch was not specified and "/DOSSEG" was, then the appropriate      |
  412.   |   expression is used.  Otherwise, the linker will just take the         |
  413.   |   segments in the order they were encountered.                          |
  414.   |                                                                         |
  415.   +-------------------------------------------------------------------------+*/
  416.  
  417.  If ordering.val IsNull
  418.   Then
  419.    If DOSSEG.val IsTrue
  420.     Then
  421.      ordering.val = duplicate_string(Addr(static_pool),
  422.       string((byte *) ("(seg[*code]|seg[*CODE], "
  423.                        "!(gr[dgroup]|gr[DGROUP]), "
  424.                        "cl[begdata]|cl[BEGDATA], "
  425.                        "cl[*data]|cl[*DATA], "
  426.                        "!(cl[*bss]|cl[*BSS]|cl[*stack]|cl[*STACK]), "
  427.                        "cl[*bss]|cl[*BSS], "
  428.                        "cl[*stack]|cl[*STACK])")));
  429.     Else
  430.      ordering.val = duplicate_string(Addr(static_pool),
  431.                                      string((byte *) ("(true)")));
  432.     EndIf;
  433.   EndIf;
  434.  
  435. /*+-------------------------------------------------------------------------+
  436.   |                                                                         |
  437.   |  Check to insure a stack segment of appropriate size is present.  For   |
  438.   |  .COM and .SYS files, no stack segment is required.  For .EXE files,    |
  439.   |  one is required.  For .EXE files, we must insure that a stack of at    |
  440.   |  least the size specified in the "/STACK" switch is available.          |
  441.   |                                                                         |
  442.   +-------------------------------------------------------------------------+*/
  443.  
  444.  If exefile IsFalse
  445.   Then
  446.    If stack_segment_found IsTrue
  447.     Then
  448.      linker_error(4, "Stack segment found for a non .EXE file.\n");
  449.     EndIf;
  450.   Else
  451.    If (stack_segment_found IsFalse) AndIf
  452.       (stack.set           IsFalse)
  453.     Then
  454.      linker_error(4, "No stack segment for .EXE file.\n");
  455.     Else
  456.      If (stack.set IsTrue) AndIf 
  457.         (Bit_16(Largest_stack_seg.length) LessThan stack.val)
  458.       Then
  459.        obj_generate_segment(generated_lname,
  460.                             none_lname,
  461.                             stack_combine,
  462.                             2,              /* word aligned */
  463.                             none_lname,
  464.                             exe_file_list.first,
  465.                             0L,             /* not absolute segment */
  466.                             Bit_32(stack.val));
  467.       EndIf;
  468.     EndIf;
  469.   EndIf;
  470.  
  471. /*+-------------------------------------------------------------------------+
  472.   |                                                                         |
  473.   |  Handle near communals as follows:  If there is at least one near       |
  474.   |  communal, create the segment "c_common" in group "DGROUP" with         |
  475.   |  class "BSS".  Then, for each communal place it in "c_common" in        |
  476.   |  ascending order while changing the dictionary entry from               |
  477.   |  "near_communal" to "internal".                                         |
  478.   |                                                                         |
  479.   +-------------------------------------------------------------------------+*/
  480.  
  481.  If near_communals IsNotNull
  482.   Then
  483.    length = 0L;
  484.    For pub=near_communals; pub IsNotNull; pub=Pub.Communal.next_communal
  485.     BeginFor
  486.      LoopIf(Pub.type_entry IsNot near_communal);
  487.      length += Pub.Communal.element_size;
  488.     EndFor;
  489.    If length Exceeds 65536L
  490.     Then
  491.      linker_error(8, "Near communal size exceeds 64K by %lu bytes.\n",
  492.                      length-65536L);
  493.     EndIf;
  494.    lseg = obj_generate_segment(c_common_lname,
  495.                                BSS_lname,
  496.                                blank_common_combine,
  497.                                3,              /* paragraph aligned */
  498.                                none_lname,
  499.                                exe_file_list.first,
  500.                                0L,             /* not absolute segment */
  501.                                length);
  502.    Lseg.highest_uninitialized_byte = length;
  503.    group                        = lookup_group(DGROUP_lname);
  504.    (*Lseg.segment).owning_group = group;
  505.    offset = 0;
  506.    For pub=near_communals; pub IsNotNull; pub=next_communal
  507.     BeginFor
  508.      next_communal        = Pub.Communal.next_communal;
  509.      LoopIf(Pub.type_entry IsNot near_communal);
  510.      size                 = Bit_16(Pub.Communal.element_size);
  511.      Pub.type_entry       = internal;
  512.      Pub.Internal.group   = group;
  513.      Pub.Internal.lseg    = lseg;
  514.      Pub.Internal.offset  = offset;
  515.      Pub.Internal.frame   = 0;
  516.      offset              += size;
  517.     EndFor;
  518.   EndIf;
  519.  
  520. /*+-------------------------------------------------------------------------+
  521.   |                                                                         |
  522.   |  Handle far communals as follows:  Packing as many far communals as     |
  523.   |  will fit (64K), create private segments with the segment and class     |
  524.   |  name of "FAR_BSS".  They do NOT go in DRGROUP.                         |
  525.   |                                                                         |
  526.   +-------------------------------------------------------------------------+*/
  527.  
  528.  lseg   = Null;
  529.  offset = 0;
  530.  For pub=far_communals; pub IsNotNull; pub=next_communal
  531.   BeginFor
  532.    next_communal = Pub.Communal.next_communal;
  533.    LoopIf(Pub.type_entry IsNot far_communal); 
  534.    length = Pub.Communal.element_size * Pub.Communal.element_count;
  535.    If length Exceeds 65536L
  536.     Then
  537.      Pub.Communal.next_communal = huge_communals;
  538.      huge_communals             = pub;
  539.      ContinueLoop;
  540.     EndIf;
  541.    If (lseg IsNull) OrIf ((length + Bit_32(offset)) Exceeds 65536L)
  542.     Then
  543.      lseg = obj_generate_segment(FAR_BSS_lname,
  544.                                  FAR_BSS_lname,
  545.                                  blank_common_combine,
  546.                                  3,              /* paragraph aligned */
  547.                                  none_lname,
  548.                                  exe_file_list.first,
  549.                                  0L,             /* not absolute segment */
  550.                                  0L);
  551.      offset = 0;
  552.     EndIf;
  553.    Pub.type_entry                   = internal;
  554.    Pub.Internal.group               = Null;
  555.    Pub.Internal.lseg                = lseg;
  556.    Pub.Internal.offset              = offset;
  557.    Pub.Internal.frame               = 0;
  558.    offset                          += Bit_16(length);
  559.    Lseg.highest_uninitialized_byte += length;
  560.    Lseg.length                     += length;
  561.    (*Lseg.segment).length          += length;
  562.   EndFor;
  563.  
  564. /*+-------------------------------------------------------------------------+
  565.   |                                                                         |
  566.   |  Handle huge communals as follows:  Taking as many segments as          |
  567.   |  required, create private segments with the segment and class           |
  568.   |  name of "HUGE_BSS".  They do NOT go in DRGROUP.                        |
  569.   |                                                                         |
  570.   +-------------------------------------------------------------------------+*/
  571.  
  572.  For pub=huge_communals; pub IsNotNull; pub=next_communal
  573.   BeginFor
  574.    next_communal = Pub.Communal.next_communal;
  575.    length = Pub.Communal.element_size * Pub.Communal.element_count;
  576.    lseg = obj_generate_segment(HUGE_BSS_lname,
  577.                                HUGE_BSS_lname,
  578.                                blank_common_combine,
  579.                                3,              /* paragraph aligned */
  580.                                none_lname,
  581.                                exe_file_list.first,
  582.                                0L,             /* not absolute segment */
  583.                                0L);
  584.    If Pub.Communal.element_size Exceeds 65536L
  585.     Then
  586.      linker_error(4, "Communal \"%Fs\" has element size exceeding 64K.\n",
  587.                      Pub.symbol);
  588.      offset = 0;
  589.     Else
  590.      offset = Bit_16(65536L Mod Pub.Communal.element_size);
  591.     EndIf;
  592.    length                          += Bit_32(offset);
  593.    Pub.type_entry                   = internal;
  594.    Pub.Internal.group               = Null;
  595.    Pub.Internal.lseg                = lseg;
  596.    Pub.Internal.offset              = offset;
  597.    Pub.Internal.frame               = 0;
  598.    Lseg.highest_uninitialized_byte += length;
  599.    Lseg.length                     += length;
  600.    (*Lseg.segment).length          += length;
  601.   EndFor;
  602.  
  603.  return;
  604. EndCode
  605. #undef Pub
  606. #undef Lseg
  607.  
  608. /*+-------------------------------------------------------------------------+
  609.   |                                                                         |
  610.   |                               order_term                                |
  611.   |                                                                         |
  612.   +-------------------------------------------------------------------------+*/
  613. bit_16 order_term()
  614. BeginDeclarations
  615. bit_16                                 left_operand;
  616. EndDeclarations
  617. BeginCode
  618.  left_operand = order_factor();
  619.  While TokenIs(and_string)       OrIf
  620.        TokenIs(star_string)      OrIf
  621.        TokenIs(ampersand_string)
  622.   BeginWhile
  623.    get_order_token();
  624.    left_operand &= order_factor();
  625.   EndWhile;
  626.  return(left_operand);
  627. EndCode
  628.  
  629. /*+-------------------------------------------------------------------------+
  630.   |                                                                         |
  631.   |                           order_token_get_char                          |
  632.   |                                                                         |
  633.   +-------------------------------------------------------------------------+*/
  634. void order_token_get_char()
  635. BeginDeclarations
  636. EndDeclarations
  637. BeginCode
  638.  If token_break_char Is '\000'
  639.   Then
  640.    linker_error(8, "Expression syntax error:\n"
  641.                    "\t\"%Fs\"\n",
  642.                    String(ordering.val));
  643.   EndIf;
  644.  token_break_char = *order_expression_char_ptr++;
  645.  return;
  646. EndCode
  647.  
  648.  
  649.