⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter 9 designing classes.htm

📁 英文版编译器设计:里面详细介绍啦C编译器的设计
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<H4>9.1.4.1 Parsing Lightweight Classes</H4><!-------------------------------------------------------------------------------->This 
is the rule that parses class declarations. As we can see by figure {RULE13} 
that there are three parts that we need to be concerned with. The first is a 
call to RuleExtends. This rule will be ignored for now. After parsing all of the 
base classes, we enter a loop to process a series of variable declarations and 
procedure/function declarations. 
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE13.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE13}</B> </FONT></P></MENU>We will not 
cover the implementation of SAL streams. For more information on this subject, 
the reader is referred to the SAL tutorial. At this stage this rule bears some 
resemblance to rule RecordType.
<P><TT>RuleDeclareType()</TT> needs to be modified slightly, in order to detect 
the start of a class declaration. The modifications are in grey in figure 
{RULE07}. 
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE07.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE07}</B> 
</FONT></P></MENU><TT>RuleMemberField()</TT> needs to be modified so that it can 
parse a procedure/function call. This is shown in grey in figure {RULE20}. This 
rule also covers some of the details that need to be performed for scope 
resolution. This is included for grammatical clarity, but you will not need to 
implement scope resolution as it is covered in one of the utility functions. 
This will also be discussed more later on in this chapter. 
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE20.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE20}</B> </FONT></P></MENU>
<H4>9.1.4.2 Symbol Tables for Lightweight Classes</H4><!-------------------------------------------------------------------------------->Although 
figure {RULE13S} seems simple, there is a little more involved at each step 
shown. 
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE13S.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE13S}</B> </FONT></P></MENU>The first 
thing we need to do is to add a new classblock to the symbol table. We do this 
by calling <TT>Table::appendClassBlock()</TT>. Likewise, at the end of the rule, 
there is a correspond to <TT>Table::leaveBlock()</TT>.
<P>The steps in between involve adding members to the class. The call to 
<TT>RuleVarIdentList()</TT> will add one of more identifiers to the current 
block (the class). This function should also return an integer count of 
variables that were added for this declaration. For this declaration, <PRE>      i, j, k: int;
</PRE><TT>RuleVarIdentList()</TT> should return a count of three.
<P>The next thing to do is to get the type. This might be a pre-declared type, 
as in the previous tiny example, or it can be an anonymous type. Here is an 
example of an anonymous type declaration: <PRE>      i, j, k: record
        a: int;
        b: char;
      end record;
</PRE>In either case, we call <TT>RuleDeclareType()</TT>. This rule takes a 
reference to a dummy ident, which it uses to keep track of the type of whatever 
is about to be declared. Once this rule returns, we can initialize each 
identifier in the list in order to set all the types. We can do this using the 
<TT>Ident::init()</TT> function.
<P>Once </TT>RuleVarIdentList()</TT> has been called, adding one or more 
uninitialized variables to the current block; and <TT>RuleDeclareType()</TT> has 
been called, retrieving the type of these new variables, we can proceed to set 
some information for these new variables. We need the value current offset, and 
the total size of all the new variables declared on this line. The current 
offset is basically the total size of the class up to the new variables. This 
size can be used as an offset for the position of each data member in the class 
as each one is declared. The total size is found by multiplying the size of the 
type returned from <TT>RuleDeclareType()</TT> by the count returned by 
<TT>RuleVarIdentList()</TT>.
<P><PRE>      RuleVarIdentList(follow+local, colonsy, count);

      Accept(colonsy, ...);

      RuleDeclareType(follow+local, semicolonsy, &amp;info);

      size:= info.getSize();
      totalsize:= totalsize + count*size;
      curoffs:= totalsize;

      field = 
      table.getLast(); whilecount&gt;0 do
        dec(count);
        curoffs:=   curoffs
        -size;
        field-&gt;init(&amp;info);field-&gt;setOffset(curoffs);
        field = table.goToPrev();
      loop;
</PRE>We then loop backwards through the table for each identifier that was 
declared, as shown above. This will effectively initialize each identifier with 
the proper offset and type information. The final thing that we do is if the 
type is an anonymous array, class, or record, we need to set the parent field 
for the type's block so that it references the <I>first</I> identifier on that 
line. In other words, the last example above declared three instaces of an 
anonymous record. The block describing that record has a parent field that 
should point to the first variable, in other words, <TT>i</TT>. This step is 
critical for classes, since the symbol table's search algorithm relies on this 
pointer to the parent in order to backtrack through each level of scope.
<P>This code is exactly identical to a portion of the code for declaring record 
fields. Again, there are many similarities between classes and records.
<P>The next rule that we have to modify requires very little work. We can see it 
in figure {RULE07S}. 
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE07S.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE07S}</B> </FONT></P></MENU>This sets 
one more field for the type information that is not set deeper in the parse 
tree.
<P>The next thing we need to do is to fix the condition in rule MemberField when 
it calls member functions so that the proper information is passed into 
<TT>RuleProcFuncCall()</TT>. It needs a pointer to the identifier of the 
function being called. We have already retrieved that by successfully searching 
through the list of members. Finally, it needs a pointer to the type information 
for <TT>self</TT>. This information should be the type information for the class 
that was just searched. The code should resemble this: <PRE>      func RuleMemberField ( follow: Set; last: Symbol; block: Blockvar; rType: Type, var procFuncCall: boolean ): ^Block;
        var
            ident: ^Ident; 
            nextblock : ^Block
            ...                //   Local vars 
        begin
          //*** Find the identifier in the list of fields/members
          ident:= rType-&gt;getExtra()-&gt;toBlock()-&gt;Elements.to_front();
          while  ident &lt;&gt;  null and not Found do
            if <I>Token.data.id = ident-&gt;name()</I> then
              Found= true;
            endif;
            ident:=rType-&gt;getExtra()-&gt;toBlock()-&gt;Elements.next(); 
          loop;
          if Found then
              rType.Init(ident); nextblock := ident-&gt;getParent();
              if ident-&gt;getObj() ==  varobj then
                if  ident-&gt;getOffset() &lt;&gt; 0 then 
                   // Emit Offset;
                endif;
                if(temp-&gt;getType()-&gt;getType()== recordtyp) then
                  nextblock :=temp-&gt;getExtra()-&gt;toBlock();
                end if;
              else if ident-&gt;getObj() ==  procobj or ident-&gt;getObj() == funcobj then
                 ProcFuncCall ( follow, last, ident, rType, rType); // the last two parameters are for type and self
                 procFuncCall := true; // see note about RuleDesignator below
              end if;
          else
             SemanticErrorMsg("Identifier %s is not a member of %s.....
          end if;
          return nextblock;
        end proc;

</PRE>One final thing that we do is to make sure that the rType is set as 
the&nbsp;return type for <TT>RuleMemberField().&nbsp;</TT> We can do this by 
setting the rtype within rule ProcFuncCall (this is what is done in the code 
above assumes). We do not initialize the return type using the type in 
<TT>ident</TT>, since that would make it a pointer to a function. This works 
fine for data members and record fields, however. 
<P></P>
<P>Rule Designator will also need to be changed. Normally, RuleDesignator will 
dereference the address left on the stack by RuleMemberField. A function call 
will have already put the value, not an address on the stack. So rule designator 
must know from RuleMemberField wether a field was accessed or a method call. We 
can do this with the code below: <PRE>  proc RuleDesignator(Set follow, Symbol last, Ident* ident, Type* rval, bool makerval)
  var
    procFuncCall: boolean;
    ...  // other local variables
  begin
    procFuncCall:= false;
  
    ...
    if TestToken(FirstMemberField) then
         block := RuleMemberField(follow+local, badsy, block, rval,<B>procFuncCall</B>);
    else
    ...
    if makerval &amp;&amp; !procFuncCall
        then EmitLoad(_LS_,GET_TYPE_BYTE_SIZE(rval-&gt;subtype) , 0);
    endif;
 
  end proc;
</PRE><BR>Here, procFuncCall is passed by reference, and is set accordingly in 
RuleMemberField. 
<P>The last thing that needs to be done is a small modification to rule 
ProcFuncCall(). Somehow it needs to know that it is processing a method instead 
of a standard function or procedure. The difference is that with a method call, 
the first parameter (the reference to <TT>self</TT>) will already be on the EES, 
and will not have to be generated. If <TT>RuleProcFuncCall()</TT> is processing 
a method, then it needs to skip over and not process the self parameter.
<P>
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE21S.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE21S}</B> </FONT></P></MENU>We need to 
make a similar change in <TT>RuleProcHeading()</TT> and 
<TT>RuleFuncHeading()</TT>. In each of these rules, we have an implicit self 
parameter when a procedure or function is a method of a class. To do this, we 
pass in a pointer to a <TT>ClassBlock</TT>. If the procedure/function is not a 
class method, then the pointer will be null. Otherwise, the pointer will point 
to a block that describes the class of which the procedure/function is a method. 
Then we just add a new parameter 
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE56S.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE56S}</B> </FONT></P></MENU>
<MENU><IMG src="Chapter 9 Designing Classes.files/RULE57S.gif">
  <P><FONT face=arial size=-1><B>Figure {RULE57S}</B> </FONT></P></MENU>Another 
solution would be to pass in a string containing the name of the class. If the 
string were not null, we could push a <PRE>      self: theClass;
</PRE>where <TT>theClass</TT> is the string. Of course, the presence of the 
semicolon would depend upon the existance of additional formal parameters.
<P>
<H4>9.1.4.3 Code Generation for Lightweight Classes</H4><!-------------------------------------------------------------------------------->So 
far all that we have done has been to manipulate syntax. We have done relatively 
little to the language, itself. Really all that we have done is put existing 
functionality to some new uses. for this reason, no extra code generation is 
required.
<P><!-------------------------------------------------------------------------------->
<H2>9.2 Inheritance</H2><!-------------------------------------------------------------------------------->Inheritance 
mechanisms are unique for every language. For most languages, classes and class 
inheritance behaves like that of C++. For other languages like Ada or Modula 3, 
inheritance can be quite different. SAL's inheritance functions like that of 
C++, for the most part. In this section, we will discuss the issues that are 
involved with most aspacts of class inheritance, including multiple inheritance.
<P>Let us review some terminology. We can represent the inheritance using a 
graph. If class&nbsp;B inherits from class A, a graph representing the hierarchy 
for a single instance would look like this: 
<MENU><IMG src="Chapter 9 Designing Classes.files/9-2-a.gif">
  <P><FONT face=arial size=-1><B>Figure {9.2a}</B> </FONT></P></MENU>Notice that 
the representation is such that the base classes are on the top, and the 
deriving classes are on the bottom. In this hierarchy, A is said to be the 
superclass, and B is the subclass. If B inherits from A, we can say that B is 
derived from A, or that B subclasses A. We can also referr to A as the topmost 
class, and B is the most-derived class. This graph would be representative of 
the following code:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -