📄 chapter 6 assignment and expressions.htm
字号:
_u8, and its block pointer as NULL. This is how we tell a string literal apart
from a simple array of characters. The stack is set up in the same way as for
an array/record copy. The only difference is that we use function 80h (string
N copy). Code for a string copy from a variable to a variable looks like this:
<P><PRE> Emit ( LID,RType.getExtra()->size() );
Emit ( SYS, 04h, 80h );
</PRE></MENU>
<BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px">
<P>Since the Block pointer (getExtra()) for string literals is NULL, we have
to treat them differently. Fortunately, their size is fixed and can be
determined at compilation time. Code for a string copy from a sting literal to
a variable looks like this: </P></BLOCKQUOTE>
<MENU><PRE> Emit ( LID, strlen(....));
Emit ( MCP );
</PRE>
<P></P></MENU><B>6.1.3.2 Assigning Simple data types</B><BR>This is done by
using the <TT>SSx</TT> instructions (i.e., <TT>SSB</TT>, <TT>SSW</TT>,
<TT>SSD</TT>, and <TT>SSQ</TT>) with an offset of zero. Basically, we already
have the exact address of where the data is to be stored, so we need no offset,
and we know that since <TT>LType</TT> is equal to <TT>RType</TT> that the result
of the expression is the proper size. <PRE> select LType.subtype from
case _u8, _s8:
Emit ( SSB, 0 );
case _u16, _s16:
Emit ( SSW, 0 );
case _u32, _s32, _f32:
Emit ( SSD, 0 );
case _u64, _s64, _f64:
Emit ( SSQ, 0 );
end select
</PRE>
<P>This is all there is to <TT>RuleAssignment()</TT>. Pseudocode looks like
this:
<MENU><B>var</B><BR>
<MENU>Type LType, RType;<BR></MENU>
<P>//*** Tell IdentExpr() to make an lvalue<BR>IdentExpr( Local + Follow,
false, LType );
<P>//*** Checking for procedure call<BR><B>if</B> LType.type = notyp
<B>and</B> Token.sy = ';' <B>then</B>
<MENU>return; </MENU><B>else if</B> Token.sy <> ';' <B>then</B>
<MENU>RType := LType;<BR>Expression ( Local + Follow, RType );
<P><B>if</B> LType.type <> RType.type <B>then</B><BR>
<MENU>Error "Type mismatch"<BR></MENU><B>else</B>
<MENU><B>if</B> LType.IsComplexType() <B>then</B><BR>
<MENU>Do a mem-copy or a string-copy </MENU><B>else</B>
<MENU>Store the result using <TT>SSx 0</TT>. </MENU><B>end if</B>
</MENU><B>end if</B> </MENU><B>end if</B><BR></MENU><!----------------------------------------------------------------------------->
<H3>6.2 Evaluating an Expression</TT> </H3><!----------------------------------------------------------------------------->An
expression consists of all the rules from <TT>RuleExpression()</TT> through
<TT>RuleUnaryExpression()</TT>. For the most part these rules all follow the
same pattern, with some noted exceptions. Rule Expression is the root rule for
<I>any</I> expression (i.e., rvalue) in SAL. Its prototype should look like
this: <PRE> void RuleExpression(Set Follow, Type &RType);
</PRE>Prior to calling <TT>RuleExpression()</TT> <I>anywhere in the
compiler</I>, the fields of <TT>RType</TT> should be initialized to an input
value. The input value will be used by <TT>RuleIdentExpr()</TT> or
<TT>RuleFactor()</TT> to resolve the type of any constants encountered. The
input value will be ignored, otherwise. If there is no discernable type, for
example with the <TT>write</TT> statement, then the type should be initialized
to an <TT>inttyp</TT> an subtype should be <TT>_u32s</TT>. As an example,
considering the following fragment of code: <PRE> var
x: int;
begin
x:= 1;
</PRE>Before rule Assignment calls Expression, it will initialize a type
variable to the type of <TT>x</TT>, i.e., <TT>inttyp</TT>, <TT>_u32</TT>. It
then passes this value into Expression. Rule Expression calls rule
LandExpression, and so on until rule Factor is called. Rule Factor handles
literal constants, and rule IdentExpr handles symbolic constants. These two
rules analyze the size of the number and determine whether or not it
appropriately fits into the requested value contained in RType. If so, they make
the constant to be that type. Otherwise they pick a value that is appropriate
and set RType to be that type. We will talk about this more when we discuss
<TT>RuleFactor()</TT> in section 6.3.1, when we talk about literal constants. In
short, <TT>RuleExpression()</TT> requires that its RType parameter be at least
somewhat initialized (i.e., at least RType.Type).
<P>Due to the nature of stack-based architectures, a compiler needs to arrange
computations so that both arguments are on the stack <I>before</I> the operation
is computed. A simple expression like <PRE> x + y
</PRE>Needs to be converted to <PRE> x y +
</PRE>The idea is to load the value of x and the value of y onto the EES and
then to compute their sum. All operators are binary; in other words, they work
on only two operands. All binary operators take on the following form in memory:
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/OPRCOD.gif">
<P><FONT face=arial size=-1><B>Figure {OPRCOD}.</B></FONT> </P></MENU>We can see
an in the left of figure {OPRCOD} a simple memory map. The code for an operand
can be as simple as one instruction, or it can be some complex array/record
access. The importance is the order and the arrangement of the assembly
instructions. In the center of figure {OPRCOD} we can see a section of code, and
on the left we have the assembly instructions that the code evaluates to.
<P>Computation on a stack architecture is always a mixture of loading operands
onto the EES and then performing the right computations in the right order. Most
rules from <TT>RuleExpression()</TT> to <TT>RuleMulExpression()</TT> follow one
general form:
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/GENEXPR.gif">
<P><FONT face=arial size=-1><B>Figure {GENEXPR}.</B></FONT> </P></MENU>We can
see the genreal form in pseodocode here:
<MENU><B>var</B><BR>
<MENU>LType, RType: Type;<BR>op: Symbol;
<P></P></MENU>Rule<I>Next</I>Expression(Follow, RType);
<P><B>while</B> Token.sy not in {set of operators for this rule} <B>do</B><BR>
<MENU>LType:= RType;<BR>op:= Token.sy<BR>NextToken();
<P>Rule<I>Next</I>Expression(Follow, RType);
<P><B>if</B> LType.type <> RType.type <B>then</B><BR>
<MENU>Error "Types not compatible"; (Do not eat tokens) </MENU><B>else</B>
<MENU>Emit(<B><I>op</I></B>, LType.subtype, RType.subtype); </MENU><B>end
if</B>
<P></P></MENU><B>loop;</B> </MENU>At the end of the pseudocode, there is a place
where we emit code for the operand. The variable <B><I>op</I></B> in this case
is italicized and in bold to emphasize that we emit the instruction that
corresponds to the symbol that was found. If the types are the same, then we can
emit the op code. The op-code's parameters are going to be the subtype of LType
and RType. In addition, certain operators are only going to be able to work with
certain operands. For instance, we can not bit-shift a floating point number. We
can not add two characters.
<P>
<H3>6.2.1 Rule Expression</TT> </H3><!----------------------------------------------------------------------------->Here
is our first rule:
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE34.gif">
<P><FONT face=arial size=-1><B>Figure {RULE34}.</B></FONT> </P></MENU>In this
rule, we only have one operator, a boolean OR. Notice that in the pseudocode to
the right, we emit an <TT>ORB</TT> instruction. All boolean values are a single
byte.
<H3>6.2.2 Rule LandExpression</TT> </H3><!----------------------------------------------------------------------------->
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE33.gif">
<P><FONT face=arial size=-1><B>Figure {RULE33}.</B></FONT> </P></MENU>Like the
previous rule, we only have one operator, a boolean AND. Here we also use the
<TT>ANDB</TT> instruction, specifically, since all boolean values are a single
byte.
<H3>6.2.3 Rule CmpExpression</TT> </H3><!----------------------------------------------------------------------------->
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE32.gif">
<P><FONT face=arial size=-1><B>Figure {RULE32}.</B></FONT> </P></MENU>This rule
compares any two expressions of the same data type and returns a boolean result.
This rule has a different format; there is no loop. This is because we cannot do
expressions like <PRE> if a < b < c then
. . .
end if;
</PRE>The reason for this is because the result of the first comparison is a
boolean, i.e., either a true or a false, which cannot be directly compared to an
integer or a real.
<P>Each comparision is comprised of a pair of instructions. The first is, aptly,
a compare instruction such as <TT>CMPx</TT>, <TT>UCMPx</TT>, or <TT>FCMPx</TT>.
The comparison instruction works by (internally) performing a subtraction of the
second number from the first. If the result is negative, the instruction pushes
a 0FFh onto the EES. If it is positive, it pushes a 01h onto the EES. If the
result is zero, it pushes a 00h.
<P>The table below shows the comparison instructions that are to be used whith
each data type: <PRE> bool char int8 int16 int32 int64 card8 card16 card32 card64 real32 real64
=====================================================================================
UCMPB UCMPB CMPB CMPW CMPD CMPQ UCMPB UCMPW UCMPD UCMPQ FCMPS FCMPD
</PRE>The second instruction takes the result of the comparison and converts it
into a boolean value, either a zero or a one depending on the instruction. Here
is a table of comparison operators, their corresponding VM instructions, and
their results based on the three input values: <PRE> Output From
Operator: OpCode: -1 0 1
=============================================
= EQL 0 1 0
<> NEQ 1 0 1
< LES 1 0 0
<= LEQ 1 1 0
> GTR 0 0 1
>= GEQ 0 1 1
</PRE>Notice also that prior to exiting, we set RType to <TT>booltyp</TT>.
<P>
<H3>6.2.4 Rule BorExpression</TT> </H3><!----------------------------------------------------------------------------->
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE31.gif">
<P><FONT face=arial size=-1><B>Figure {RULE31}.</B></FONT> </P></MENU>This rule
allows for either a bitwise OR, or a bitwise exclusive OR (XOR). This operator
can only be carried out on integers and cardinals. Here is a table of
instructions to use: <PRE> OP | bool char int8 int16 int32 int64 card8 card16 card32 card64 real32 real64
===========================================================================================
| | - - ORB ORW ORD ORQ ORB ORW ORD ORQ - -
% | - - XORB XORW XORD XORQ XORB XORW XORD XORQ - -
</PRE>
<P>
<H3>6.2.5 Rule BandExpression</TT> </H3><!----------------------------------------------------------------------------->
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE30.gif">
<P><FONT face=arial size=-1><B>Figure {RULE30}.</B></FONT> </P></MENU>The
instructions to be used for a bitwise AND are: <PRE> OP | bool char int8 int16 int32 int64 card8 card16 card32 card64 real32 real64
===========================================================================================
& | - - ANDB ANDW ANDD ANDQ ANDB ANDW ANDD ANDQ - -
</PRE>
<P>
<H3>6.2.6 Rule BitwExpression</TT> </H3><!----------------------------------------------------------------------------->
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE29.gif">
<P><FONT face=arial size=-1><B>Figure {RULE29}.</B></FONT> </P></MENU>SAL has a
fairly robust set of bit-shifting operators. We can do all manner of shifting
and rotating. This table represents the instructions used for the types of
operations involved. <PRE> OP | bool char int8 int16 int32 int64 card8 card16 card32 card64 real32 real64
===========================================================================================
>> | - - USHRB USHRW USHRD USHRQ USHRB USHRW USHRD USHRQ - -
>>> | - - SSHRB SSHRW SSHRD SSHRQ SSHRB SSHRW SSHRD SSHRQ - -
<< | - - USHLB USHLW USHLD USHLQ USHLB USHLW USHLD USHLQ - -
#> | - - RORB RORW RORD RORQ RORB RORW RORD RORQ - -
<# | - - ROLB ROLW ROLD ROLQ ROLB ROLW ROLD ROLQ - -
</PRE>As can be seen by the table, these operators only work on integers and
cardinals.
<P>
<H3>6.2.7 Rule AddExpression</TT> </H3><!----------------------------------------------------------------------------->
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE28.gif">
<P><FONT face=arial size=-1><B>Figure {RULE28}.</B></FONT> </P></MENU>Although
this rule is named after the addition operator, it includes all operators of the
same precidence: addition and subtraction. <PRE> OP | bool char int8 int16 int32 int64 card8 card16 card32 card64 real32 real64
===========================================================================================
+ | - - ADDB ADDW ADDD ADDQ ADDB ADDW ADDD ADDQ FADDS FADDD
- | - - SUBB SUBW SUBD SUBQ SUBB SUBW SUBD SUBQ FSUBS FSUBD
</PRE>
<P>
<H3>6.2.8 Rule MulExpression</TT> </H3><!----------------------------------------------------------------------------->
<MENU><IMG src="Chapter 6 Assignment and Expressions.files/RULE27.gif">
<P><FONT face=arial size=-1><B>Figure {RULE27}.</B></FONT> </P></MENU>Likewise,
this rule includes all operators that share precidence with multiplication,
namely division and modulus. <PRE> OP | bool char int8 int16 int32 int64 card8 card16 card32 card64 real32 real64
============================================================================================
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -