Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Jeffery C.The implementation of Icon and Unicon.2004

.pdf
Скачиваний:
13
Добавлен:
23.08.2013
Размер:
3.05 Mб
Скачать

365

eret Save the descriptor on the top of the stack. Unwind the C stack. Remove the current expression frame from the stack and push the saved descriptor.

escan Dereference the top descriptor on the stack if necessary. Copy it to the place on the stack prior to the saved values of &subject and &pos (see bscan). Exchange the current values of &subject and &pos with the saved values on the stack. Then suspend. If resumed, restore the values of &subject and &pos from the stack and fail.

esusp Create a generator frame containing a copy of the portion of the stack that is needed if the generator is resumed.

field n Replace the record descriptor on the top of the stack by a descriptor for field n of that record.

global n Push a variable descriptor pointing to global identifier n. goto n Set ipc to n.

init n Change init instruction to goto. int n Push a descriptor for the integer n. inter expr1 ** exp'2

invoke n exp1lJ(expr1, exp'2, ..., exprn) keywd n Push a descriptor for keyword n. lconcat expr1 ||| expr2

lexeq expr1 == expr2 lexge expr1 »= expr2 lexgt expr1 » expr2 lexle expr1 «= expr2 lexlt expr1 « expr2 lexne expr1 ­== expr2

limit Convert the descriptor on the top of the stack to an integer. If the conversion cannot be performed or if the result is negative, terminate execution with an error message. If it is zero, fail. Otherwise, create an expression frame with a zero failure ipc.

line n Set the current line number to n. llist n [expr1,expr2,...,exprn]

local n Push a variable descriptor pointing to local identifier n.

lsusp Decrement the current limitation counter, which is immediately prior to the current expression frame on the stack. If the limit counter is nonzero, create a generator frame containing a copy of the portion of the interpreter stack that is needed if the generator is resumed. If the limitation counter is zero, unwind the C stack and remove the current expression frame from the stack.

366

mark Create an expression frame whose marker contains the failure ipc corresponding to the label n, the current efp, gfp, and ilevel.

mark0 Create an expression frame with a zero failure ipc. minus expr1 ­expr2

mod expr1 % expr2 mult expr1 * expr2 neg ­expr

neqv exprl ­=== expr2

nonnull \expr

 

null /expr

 

number

+expr .

numeq expr1 = expr2 numge exprl >= expr2 numgt expr1 > expr2 numle exprl <= expr2 numlt exprl < expr2 numne exprl ­= expr2

pfail

If &trace is nonzero, decrement it and produce a trace message. Unwind the C

stack and remove the current procedure frame from the stack. Then fail.

plus

exprl + expr2

pnull

Push a null descriptor.

pop

Pop the top descriptor.

power

exprl ~ expr2

pret

Dereference the descriptor on the top of the stack, if necessary, and copy it to the

place where the descriptor for the procedure is. If &trace is nonzero; decrement it and produce a trace message. Unwind the C stack and remove the current procedure frame from the stack.

psusp Copy the descriptor on the top of the stack to the place where the descriptor for the procedure is, dereferencing it if necessary. Produce a trace message and decrement &trace if it is nonzero. Create a generator frame containing a copy of the portion of the stack that is needed if the procedure call is resumed.

push1 Push a descriptor for the integer 1. pushn1Push a descriptor for the integer ­1. quit Exit from the interpreter.

random ?expr

 

 

367

rasgn

expr1 expr2

real a

Push a descriptor for the real number block at address a onto the stack.

refresh ~expr

 

rswap

expr1 <­> expr2

sdup

Push a copy of the descriptor on the top of the stackr

sect

expr1 [ expr2: expr3 ]

size

*expr

 

static n Push a variable descriptor pointing to static identifier n.

str n, a Push a descriptor for the string of length n at address a.

subsc expr1[expr2]

swap

expr1 :=: exp12

tabmat =exp'

 

toby

expr1 to expr2 by expr3

unions expr1 ++ expr2

unmark

Remove the current expression frame from the stack and unwind the C

stack.

 

 

value

 

.expr

368

Appendix C: Virtual Machine Code

The virtual machine code that is generated for various kinds of Icon expression is listed below. The form of code given is icode, the output of the Icon linker cast in a readable format. The ucode produced' by the Icon translator, which serves as input to the Icon linker, is slightly different in some cases, since th linker performs some refinements.

C.1 Identifiers

As mentioned in Sec. 8.2.2, the four kinds of identifiers are distinguished by where their values are located. All are referred to by indices, which are zero based.

The values of global identifiers are kept in an array that is loaded from thl icode file and is at a fixed place in memory during program execution. By con vention, the zeroth global identifier contains the procedure descriptor for main The following instruction pushes a variable pointing to the value of main onto the interpreter stack:

main

global

0

 

 

 

Static identifiers are essentially global identifiers that are only known on a per­procedure basis. Like global identifiers, the values of static identifiers are in an array that is at a fixed location. Static identifiers are numbered starting at zero and continuing through the program. For example, if count is static identifier 10 the following instruction pushes a variable descriptor pointing to that static identifier onto the stack:

count

static

10

 

 

 

The space for the values of arguments and local identifiers is allocated on the stack when the procedure in which they occur is called. If x is argument zero and i is local zero for the current procedure, the following instructions push variable descriptors for them onto the stack:

x

arg

0

 

 

 

 

 

 

i

local

0

 

 

 

C.2 Literals

The virtual machine instruction generated for an integer literal pushes the integer onto the stack as an Icon descriptor. The value of the integer is the argument to the instruction:

100

int

100

 

 

 

The instruction generated for a string literal is similar to that for an integer literal, except that the address of the string and its length are given as arguments. The string itself is in a region of data produced by the linker and is loaded as part of the icode file:

"hello"

str

5,a1

 

 

 

The instruction generated for a real or cset literal has an argument that is the address of a data block for the corresponding value. Such blocks are in the data region generated by the linker:

369

100.2

real

a2

 

 

 

 

 

 

'aeiou'

cset

a3

 

 

 

C.3 Keywords

The instruction generated for most keywords results in a call to a C function that pushes a descriptor for the keyword onto the stack. The argument is an index that identifies the keyword. For example, &date is keyword 4:

&date

keywd

4

 

 

 

Some keywords correspond directly to virtual machine instructions. Examples are &null and &fail:

&null

pnull

 

 

 

 

&fail

efail

 

 

C.4 Operators

The code generated for a unary operator first pushes a null descriptor, then evaluates the code for the argument, and finally executes a virtual machine instruction that is specific to the operator:

*expr

pnull

 

code for expr

 

size

 

 

The code generated for a binary operator is the same as the code generated for a unary operator, except that there are two arguments:

expr1 + expr2

pnull

 

code for expr1

 

code for expr2

 

plus

 

 

An augmented assignment operator uses the virtual machine instruction dup to duplicate the result produced by its first argument:

expr1 +:= expr2

pnull

 

code for expr1

 

dup

 

code for expr2

 

plus

 

asgn

 

 

The difference between the code generated for left­ and right­associative operators is illustrated by the following examples:

370

expr1 + expr2 + expr3

pnull

 

pnull

 

code for expr1

 

code for expr2

 

plus

 

code for expr3

 

plus

 

 

expr1 := expr2 := expr3

pnull

 

code for expr1

 

pnull

 

code for expr2

 

code for expr3

 

asgn

 

asgn

 

 

A subscripting expression is simply a binary operator with a distinguished syntax:

 

expr1 [ expr2 ]

 

pnull

 

 

 

code for expr1

 

 

 

code for expr2

 

 

 

subsc

 

 

 

 

A sectioning expression is a ternary operator:

 

 

 

 

 

expr1 [ expr2 : expr3 ]

 

pnull

 

 

 

code for expr1

 

 

 

code for expr2

 

 

 

code for expr3

 

 

 

sect

 

 

 

 

Sectioning expressions with relative range specifications are simply abbreviations. The virtual machine instructions for them include the instructions for performing the necessary arithmetic:

expr1 [ expr2 +: expr3 ]

pnull

 

code for expr1

 

code for expr2

 

dup

 

code for expr3

 

plus

 

sect

 

 

A to­by expression is another ternary operator with a distinguished syntax:

expr1 to expr2 by expr3

pnull

 

code for expr1

 

code for expr2

 

code for expr3

 

toby

 

 

371

If the by clause is omitted, an instruction that pushes a descriptor for the integer is supplied:

expr1 to expr2

pnull

 

code for expr1

 

code for expr2

 

push1

 

toby

 

 

The code generated for an explicit list is similar to the code generated for an operator. The instruction that constructs the list has an argument that indicates the number of elements in the list:

[expr1, expr2, expr3]

pnull

 

code for expr1

 

code for expr2

 

code for expr3

 

llist

 

 

C.5 Calls

The code generated for a call also is similar to the code generated for an operator except that a null descriptor is not pushed (it is provided by the invoke instruction). The argument of invoke is the number of arguments present in the call, not counting the zeroth argument, whose value is the procedure or integer that is applied to the arguments:

expr0(expr1, expr2)

code for expr0

 

code for expr1

 

code for expr2

 

invoke

2

 

 

 

In a mutual evaluation expression in which the zeroth argument of the "call" is omitted, the default value is ­1, for which an instruction is provided:

(expr1, expr2, expr3)

pushn1

 

 

code for expr1

 

code for expr2

 

code for expr3

 

invoke

3

 

 

 

C.6 Compound Expressions and Conjunction

The difference between a compound expression and a conjunction expression is illustrated by the following examples. Note that the code generated for conjunction is considerably simpler than that generated for a compound expression, since no separate expression frames are needed:

372

{expr1; expr2; expr3}

mark

L1

 

code for expr1

 

unmark

 

 

L1:

 

 

mark

L2

 

code for expr2

 

unmark

 

 

L2:

 

 

code for expr3

 

 

 

expr1 & expr2 & expr3

code for expr1

 

pop

 

code for expr2

 

pop

 

code for expr3

 

 

C.7 Selection Expressions

In the code generated for an if­then­else expression, the control expression bounded and has an expression frame of its own:

if expr1 then expr2

mark

L1

else expr3

code for expr1

 

unmark

 

 

code for expr2

 

goto

L2

 

L1:

 

code for expr3

L2:

If the else clause is omitted, mark0 is used, so that if the control expression fails, this failure is transmitted to the enclosing expression frame:

if expr1 then expr2

mark0

 

code for expr1

 

unmark

 

code for expr2

 

 

The code generated for a case expression is relatively complicated. As for similar control structures, the control expression is bounded. The result it produces is placed on the top of the stack by the eret instruction, which saves the result of evaluating expr1, removes the current expression frame, and then push the saved result on the top of the stack. The ccase instruction pushes a null descriptor onto the stack and duplicates the descriptor just below the current efp on the top of the stack. This has the effect of providing a null descriptor and the first argument for the equivalence comparison operation performed by eqv. The second argument of eqv is provided by the code for the selector clause. The remainder of the code for a case clause removes the current expression frame marker. in case the comparison succeeds. and evaluates the selected expression:

373

case expr1 of {

mark0

 

expr2 : expr3

code for expr1

expr4 : expr5

eret

 

default: expr6

mark

L2

}

ccase

 

 

code for expr2

 

eqv

 

 

unmark

 

 

pop

 

 

code for expr3

 

goto

L1

 

L2:

 

mark L3 ccase

code for expr4 eqv

unmark pop

code for expr5 goto L1

L3:

pop

code for expr6

L1:

C.8 Negation

The not control structure fails if its argument succeeds but produces the null value if its argument fails:

not expr

mark

L1

 

code for expr

 

unmark

 

 

efail

 

 

L1:

 

 

pnull

 

 

 

 

C.9 Generative Control Structures

If the first argument of an alternation expression produces a result, esusp produces a generator frame for possible resumption and duplicates the surrounding expression frame on the top of the stack. The result of the first argument is then pushed on the top of the stack, so that it looks as if the first argument merely produced a result. The second argument is then bypassed. When the first argument does not produce a result, its expression frame is removed, leaving the second argument to be evaluated:

374

expr1 | expr2

mark

L1

 

code for expr1

 

esusp

 

 

goto

L2

 

L1:

 

code for expr2

L2:

Since alternation is treated as a binary operation, a succession of alternations produces the following code:

expr1 | expr2 | expr3

mark

L1

 

code for expr1

 

esusp

 

 

goto

L2

 

L1:

 

 

mark

L3

 

code for expr2

 

esusp

 

 

goto

L2

 

L2:

 

code for expr3

L3:

Repeated alternation is complicated by the special treatment of the case in which its argument does not produce a result. If it does not produce a result, the failure is transmitted to the enclosing expression frame, since the failure ipc is 0. However, if it produces a result, the failure ipc is changed by chfail so that subsequent failure causes transfer to the beginning of the repeated alternation expression. The esusp instruction produces the same effect as that for regular alternation. Note that changing the failure ipc only affects the expression frame marker on the stack. When mark is executed again, a new expression frame marker with a failure ipc of 0 is created.

| expr

L1:

mark0

code for expr chfail L1 esusp

In the limitation control structure, the normal left­to­right order of evaluation is reversed and the limiting expression is evaluated first. The limit instruction checks that the value is an integer and pushes it. It then creates an expression frame marker with a zero failure ipc. Thus, the limit is always one descriptor below the expression marker created by the subsequent mark instruction. The lsusp instruction is similar to the esusp instruction, except that it checks the limit. If the limit is zero, it fails instead of suspending. Otherwise, the limit is decremented:

expr1 \ expr2

code for expr2

 

limit

 

code for expr1

 

lsusp

 

 

Соседние файлы в предмете Электротехника