The Picky programming language - Lsub.org

40
The Picky programming language 6/9/11 Francisco J Ballesteros Laboratorio de Sistemas Universidad Rey Juan Carlos ABSTRACT 2E O EI FHCH EC C CA AIECA BH IA E BEHI ALA E H HO FHCH EC HIA 6DA C CA EI I IEFA EI I HE HAC H EC MD EI AC FHCH 6DEI A AI HE AI DA C CA 1. Motivation ) A C C CA BH A DEC E EI G E A LAH IA AHO FAN 6DEI AI DECI D H BH I A I E E H HO HIAI A IA DAHA HA O EBBAHA I H I I AH 2E EC I IA EI A E FH E A A IA O BA HAI AB I E IDM F ALA BH AI I IA I 6OFA I BA O EI I E BA HAI EA E AHABAHA EC B FE AHI AI E A H BH I A I MD DA A O AI )I H I H HAI HAG EHEC exit when IH I HA A IEO EI IA .EA D EC E ) EI IO I O DA A I .H AN FA EC End_Of_File O FHCH HA EC BH AHE I A I ME M MDO . H DAHHA MA A D D B EI ID D LA AH ABBA I O BEA 1 I HA B EI M ALA C CAI EA HA I E A 6OFA I BA O EI I IH HA E EC I HC OFEC H CA DA I HA C D LA MDA A HEC DM FHCH BH BEHI EA 5 HEF EC C CAI ABH A C FH E A D LA AIEH A BA HAI E O IAI .H AN FA E EC MDE A IF A I F H B DA IO N AC HI H E AH E B L HE AI A HEA A C CAI HA FAN BH IA I BEHI C CA 6DAO O A FF H DAO HA A EA CE I I A I 2I EI C BEHI C CA 0MALAH E I H IO N EI LAH IA )I DA C CA IO N EI HA FAN D AA A .H AN FA DA IA B IAE I I IAF H HI EI A B AHE HI BH IA A AI EI FH A BH I A I 6DAO A F C AIIEC MDA IAE MDA A 9A M A C CA I IEFA I2I ME D AHIA IO N EA HA EI E D EC B BEA 1 .EA 1 EI EFH I FAHBH 1 I A I A I A H DM IA H I H HAI C E A I F E ME D LE EC BEA 1 H AI EFIA O DA BEA IH E )I HAI MA AIECA AM C CA A 2E O 6DA C CA FEAI O A A BH IH DEA A 2) ) E AH FHA AH BH 2) A EI I FFEA C ME D DA FEAH 6DEI EI AI I A I BH FH EE O EII AI D M HEIA DAHMEIA

Transcript of The Picky programming language - Lsub.org

Page 1: The Picky programming language - Lsub.org

The Picky programming language

6/9/11

Francisco J BallesterosLaboratorio de Sistemas

Universidad Rey Juan Carlos

ABSTRACT

Picky is a programming language designed for use in a first level,introductory, programming course. The language is small and simple,and is strict regarding what is a legal program. This document describesthe language.

1. Motivation

Ada could be a good language for teaching, but it is quite verbose and utterly com­plex. This makes things hard for students in introductory courses, because there aremany different constructs to master. Picking a subset is not doable in practice, becausemany features left out still show up even for modest subsets. Type safety is a must, butautomatic features (like automatic dereferencing of pointers) makes it unclear for stu­dents what the code actually does. Also, control structures requiring exit when con­structs are easily misused. File handling in Ada is clumsy, to say the least. For example,calling End_Of_File may block a program, reading from a terminal, and students will notknow why. Furthermore, we teach that functions should not have lateral effects, butmany file I/O tools are functions.

Low level languages, like C, are not suitable at all. Type safety is a must and struc­tured data including strong typing and range checks are good to have when learninghow to program for a first time.

Scripting languages do not enforce good practice, and have undesirable features inmany cases. For example, including white space as part of the syntax (e.g., tabulators)or automatic declaration of variables.

Object oriented languages are too complex for use as a first language. They maybe popular, but they are not clean and look like magic to most students.

Pascal is a good first language. However, its control syntax is verbose. Also, thelanguage syntax is more complex than needed. For example, the use of semicolons asseparators instead of terminators for sentences is a problem for students. They end upguessing when to add a semicolon and when not to add one.

We wanted a language as simple as Pascal, with terse syntax (like C), and a realistichandling of file I/O. File I/O is important not just to perform I/O, but also to make stu­dents learn how to use control structures to guide data consumption without violatingfile I/O rules imposed by the file abstraction. As a result, we designed a new language,called Picky.

The language compiles to byte-code for an abstract machine called PAM. An inter­preter for PAM code is supplied along with the compiler. This isolates students fromportability issues that would arise otherwise.

Page 2: The Picky programming language - Lsub.org

­ 2 ­

When a kid learns how to ride a bicycle it is convenient to use side-wheels for awhile. Only after such artifact is under control, a new bicycle (one without side-wheels,and perhaps with an engine) is more convenient. In the same way, Picky is highlyrestrictive regarding what can be done and what can not in a program. It has side-wheels attached. Both the compiler and the run time include extra checks and wastememory and time to provide additional safety features (e.g., more informative diagnos­tics regarding accidental use of dangling pointers).

2. The language

2.1. Picky programs

Picky has control structures reminiscent of C and data declarations in the style of Pascal.A source program is made of a single file. This is a hello world:

1 /*

2 * Hello world

3 */

5 program Hello;

7 procedure main()

8 {

9 writeln("hello, world");

10 }

Comment syntax is taken from C. A program is introduced by a program clause (line 5)that assigns an identifier to the program. A program may have constant and type defini­tions, variable declarations, procedure definitions and function definitions. A procedurenamed main must be included, like in C. The program starts executing its body and ter­minates when returning from it.

All declarations and statements are terminated by a semicolon, but note that proce­dure and function definitions are not terminated by a semicolon. Constants, types, pro­cedures, and functions may not be declared within the scope of a procedure or function.That is, subprograms may not be nested and constants and types must be declared inthe global scope.

The language is case-sensitive. Thus, main, Main, and MAIN are different identi­fiers. An identifier must start with an alpha rune followed by zero or more alphanu­meric runes.

The following names are reserved and correspond to keywords, pre-defined vari­ables, types, procedures, functions, and constants. All other names are available for newidentifiers.

Page 3: The Picky programming language - Lsub.org

­ 3 ­

acos dispose flush log pow stdoutand do for log10 pred succarray else fpeek Maxchar procedure switchasin Eof fread Maxint program Tabatan Eol freadeol Minchar read tanbool Esc freadln Minint readeol Truecase exp frewind new readln typeschar False function nil record varsclose fatal fwrite not ref whileconsts feof fwriteeol Nul return writecos feol fwriteln of sin writeeol

fflush if open sqrt writelndata file int or stackdefault float len peek stdin

A program starts with the program clause and must include a procedure with noparameters and named main, as shown.

A program may aso include one or more constant declaration blocks, one or moretype declaration blocks, one or more variable declaration blocks, and procedure andfunction definitions. The scope for a declaration goes from the point where it happensin the source to the end of file.

Constant, type, and variables declaration blocks start with the keyword consts,types, and vars (respectively) followed by declarations. This program is an example:

1 program Xample;

3 consts:

4 C1 = 11;

5 Greet = "hi";

7 types:

8 Tmonth = (Ene, Feb, Mar);

9 Tyesno = bool;

11 consts:

12 Zmonth = Ene;

14 vars:

15 a: month;

17 procedure main()

18 {

19 /* ... */ ;

20 }

2.2. Constants

Constants are defined like in the example. Constants for basic types have data typesderived from their values, which may be expressions as long as their resulting value maybe computed at compile time.

Integer literals are digits, base 10, one after another. A leading plus or minus signis actually an unary expression adjusting the sign of the following operand. Float (real)literals are digits with a decimal point and at least one more digit, perhaps followed byan exponential notation (i.e., an ��E�� an optional sign, and one or more digits). Booleanvalues are named True and False. Character literals are a single rune within singlequotes. Array of character (string) literals are one or more runes within double quotes.These are some examples:

Page 4: The Picky programming language - Lsub.org

­ 4 ­

1 consts:

2 C1 = 11; /* int */

3 C2 = −2; /* int */

4 C3 = 3.0; /* float */

5 C4 = 4.3E10; /* float */

6 Ok = True; /* bool */

7 X = ’X’; /* char */

8 Msg = "hi"; /* array[0..1] of char */

Aggregates are discussed later, along with arrays and records.

2.3. Basic data types

Picky is strongly typed. Too strongly, hence its name. Basic types are bool, char, int,float, and file. They correspond to booleans, characters, integers, real numbers in float­ing point, and external (text) files.

Two types are compatible (for assignment and other operators) only if they havethe same name. Predefined types also obey this rule. Constants and literals are anexception, they belong to ��universal�� types that are assumed to be compatible with anybasic data type of the same kind. This is reasonable, for example, to permit using inte­ger literals in expressions that belong to a user defined integer type. Another exceptionare subranges. Subranges do not introduce a new type; they declare a restriction defin­ing a subset of an existing type.

A type definition defines a new type and declares its name. For example

1 types:

2 Apples = int;

3 Oranges = int;

defines two new types: Apples and Oranges. It is not legal to mix apples with oranges,and it is not legal to mix any of them with int values. However, integer constants and lit­erals may be mixed with any of them.

2.4. Predefined variables and constants

There are several constant character values defined: Eof (representing the end of file),Eol (representing the end of line), Tab (tabulator), Esc (escape), and Nul (null byte).

Constants Maxint and Minint report the maximum and minimum values for the intdata type. Like Maxchar and Minchar do for the char data type.

Predefined variables named stdin and stdout, of type file, exist for standard inputand output.

The special value nil is predefined and represents a null pointer. It is type compati­ble with any pointer type.

2.5. Operators and builtin operations

We describe here the operators available in the language (but for the len operator,which is discussed along with structured data types). For binary operators, bothoperands must be type compatible. The resulting type is always of the same type of thearguments, but for obvious exceptions (i.e., relational operators always yield bool val­ues).

Values of data types other than file may be compared using equality operators:___________________________Operator Meaning___________________________

== Equal to___________________________!= Not equal to___________________________

Page 5: The Picky programming language - Lsub.org

­ 5 ­

Equality yields True if and only if values are equal. Inequality yields True if and only ifvalues are not equal. For structured types (described later), these operators comparetheir inner elements, one by one.

Values of ordinal data types (that is, bool, char, int, and user defined enumera­tions) have fixed positions in their abstract sets, and may be compared using the follow­ing:

___________________________________Operator Meaning___________________________________

< Less than___________________________________> Greater than___________________________________<= Less or equal than___________________________________>= Greater or equal than___________________________________

Ordinal values have two more functions defined:_______________________________

Built−in Meaning_______________________________pred(v) Predecessor of v_______________________________succ(v) Successor of v_______________________________

Pred yields the predecessor of v in the data type. Succ yields the successor of v in thedata type.

Boolean values accept usual boolean operators:____________________________________Operator Meaning____________________________________and binary logical and____________________________________or binary logical or____________________________________not unary logical negation____________________________________

And and or evaluate both operands. That is, there is no short-circuit evaluation as foundin C.

Numeric data types accept the following operators, their operands must be typecompatible, as usual. Not all operators are defined for both integers and floating pointnumbers (the table shows legal operand types).

_________________________________________________________________________Operator Meaning Argument types_________________________________________________________________________

+ binary addition or unary nop float int_________________________________________________________________________− binary subtraction or unary sign change float int_________________________________________________________________________* binary multiplication float int_________________________________________________________________________/ binary division float int_________________________________________________________________________% binary modulus int_________________________________________________________________________** binary exponentiation float int_________________________________________________________________________

Expressions may be parenthesized as required. The precedence of operators is indicatedby the following table, from low to high precedence. Operators in the same row havethe same precedence. All operators associate to the left. Expressions are evaluated leftto right.

Page 6: The Picky programming language - Lsub.org

­ 6 ­

________________________________Precedence________________________________or and

== != < > <= >=+ − (binary)

* / %low

**high + − (unary)

len not________________________________

The len operator returns the number of elements in the object given as an argument. Itis discussed later, in the section for structured types.

The following functions are defined for float arguments, and yield a float result.They inherit their names and behavior from C, so we do not describe them any further.

__________________________________________Function Meaning__________________________________________

acos(r) arc-cosine__________________________________________asin(r) arc-sine__________________________________________atan(r) arc-tangent__________________________________________cos(r) cosine__________________________________________exp(r) exponential__________________________________________log(r) logarithm__________________________________________log10(r) base 10 logarithm__________________________________________

pow(r1, r2) power__________________________________________sin(r) sine__________________________________________sqrt(r) square root__________________________________________tan(r) tangent__________________________________________

The following functions are defined to perform I/O. Some of them operate on stdin orstdout, others operate on the file given, as indicated. The argument obj may be a valueor l-value of any basic type (i.e., non structured type), and it may be also an array ofchar.

Page 7: The Picky programming language - Lsub.org

­ 7 ­

________________________________________________________________________________________________Built−in Proc/Func Meaning________________________________________________________________________________________________

close(file) procedure Close the file________________________________________________________________________________________________eof() function Report if Eof has been met in stdin________________________________________________________________________________________________eol() function Report if Eol has been met in stdin________________________________________________________________________________________________

feof(file) function Report if Eof has been met in file________________________________________________________________________________________________feol(file) function Report if Eol has been met in file________________________________________________________________________________________________fflush(file) procedure Flush the output buffer for file________________________________________________________________________________________________

flush() procedure Flush the output buffer for stdout________________________________________________________________________________________________fpeek(file, char) procedure Look ahead next char from file, or Eof, or Eol________________________________________________________________________________________________fread(file, obj) procedure Read object from text representation in file________________________________________________________________________________________________freadln(file, obj) procedure Idem, and skip the rest of line (and Eol)________________________________________________________________________________________________freadeol(file) procedure Read end of line from file________________________________________________________________________________________________frewind(file) procedure Seek to start of file________________________________________________________________________________________________

fwrite(file, obj) procedure Write text representation for object in file________________________________________________________________________________________________fwriteln(file, obj) procedure fwrite(file,obj); fwriteeol(file);________________________________________________________________________________________________fwriteeol(file) procedure Write end of line in file________________________________________________________________________________________________

open(file, name, mode) procedure Open file with given name for mode (whichmay be "r", "w", or "rw")________________________________________________________________________________________________

peek(char) procedure Look ahead next char from stdin, or Eof, or Eol________________________________________________________________________________________________read(obj) procedure Read object from text representation in stdin________________________________________________________________________________________________readln(obj) procedure Idem, and skip the rest of line (and Eol)________________________________________________________________________________________________readeol() procedure Read end of line from stdin________________________________________________________________________________________________write(obj) procedure Write text representation for object in stdout________________________________________________________________________________________________writeln(obj) procedure write(obj); writeeol();________________________________________________________________________________________________writeeol() procedure Write end of line in stdout________________________________________________________________________________________________

L-values of pointer types may use the following builtins to allocate and deallocate mem­ory.

______________________________________________________________________________Built−in Proc/Func Meaning______________________________________________________________________________

dispose(ptr) procedure Dispose memory referenced by ptr______________________________________________________________________________new(ptr) procedure Set ptr to point to newly allocated memory______________________________________________________________________________

Three other built-ins are provided for debugging and abnormal termination.___________________________________________________________________

Built−in Proc/Func Meaning___________________________________________________________________fatal(text) procedure Print text and abort execution___________________________________________________________________stack() procedure Dump the stack for debugging___________________________________________________________________data() procedure Dump global data for debugging___________________________________________________________________

2.6. Type casts

In general, the language does not permit type casts. However, type casts are permittedto convert ordinals to the integer representing their position in the type and vice-versa.Also, integers may be converted to floating point numbers and vice-versa.

To convert a value to a type use the target type name as a function. For example,these are legal expressions:

Page 8: The Picky programming language - Lsub.org

­ 8 ­

char(int(’A’) + 1)float(3)int(4.2)

2.7. Basic type definitions

A new type may be defined as new instance of an existing type by using the existingtype as its definition. For example,

1 types:

2 Apples = int;

3 Oranges = int;

Enumerated types are also ordinal types, and are defined by enumeration of their literalsas in the example:

1 types:

2 Month = (Jan, Feb, Mar);

3 Yesno = (No, Yes);

Line 2 introduces both the Month data type and new literals Jan, Feb, and Mar.

Subranges of existing ordinal data types (i.e., bool, char, int, and enumerated datatypes) may be declared. Subranges do not introduce a new data type. They introduce arange limit for an existing type, and remain type compatible with that type. Ranges arechecked at run-time and may lead to a program panic if not obeyed by the user code. Asubrange is defined by naming the actual type and the range, as in this example:

1 types:

2 Mrange = Month Jan..Feb;

3 Letter = char ’a’..’z’;

2.8. Structured Types

Array types may be declared using an ordinal type (usually a subrange) as an indexspecifier and any other type as the element specifier. For example:

1 types:

2 Days = array[Month] of int;

3 Days2 = array[Jan..Feb] of int;

There is no data type for strings. Instead, an array of characters indexed by integersstarting with 0 is used.

The syntax does not allow to nest definitions for data types. Only in the rangeindex specifier can be nested, instead of defining a type name and then using it. Thisenforces the policy of declaring type names for inner components of structured data. Asa result, multi-dimensional arrays require defining the type for a row or column (in n-1dimensions) and then the type for the array, using the previous one as the element type.Syntax to refer to array elements is as expected in C-like languages:

days[Jan]matrix[3][2]

Record (or structure, or tuple) types may be declared using the record keyword and abracketed list of field declarations. As in this example:

Page 9: The Picky programming language - Lsub.org

­ 9 ­

1 program Example;

2 types:

3 Prange = int 1..10;

4 Point = record

5 {

6 x: int;

7 y: int;

8 };

9 Points = array[Prange] of Point;

10 Poly = record

11 {

12 points: Points;

13 npoints: int;

14 };

It is feasible to switch on a value of a enumerated-type field to define some fields onlyfor particular values of that switch-field. For example:

1 Cmd = record

2 {

3 code: Code;

4 kind: Kind;

5 switch(kind){

6 case Rangecmd:

7 r: Rangetype;

8 case Recmd, Strcmd:

9 s: Str;

10 case Intcmd:

11 i: int;

12 }

13 };

In this case, the field s is available only when the field kind has either Recmd or Strcmdas values. For values of kind other than Rangecmd, Recmd, Strcmd, and Intcmd, theonly fields of Cmd are: code and kind.

As explained before, type definitions may not be nested. For example, it is impera­tive to define the types Point and Points in this example before defining Poly. Other­wise, members of Poly couldn�t be arrays or records. Only Prange might be avoided, byusing the range directly in the definition of Points.

Syntax for member access is as expected, using the dot notation. For example:

poly.points[1].x

The operator len may be used with a type, variable, or constant name to yield the num­ber of members of the given object or type. For example,

len Points

would be the integer value 10 in the previous example. This operator is evaluatedalways at compile time and does not evaluate its arguments.

2.9. Aggregates

For arrays and records, literal values may be constructed using the type name as a (con­structor) function and supplying as arguments values of appropriate types for each oneof the members, in the order used in the type definition. An aggregate value may beused in any place a value of the corresponding type may be used, including constantdefinition and subprogram arguments. For example:

Page 10: The Picky programming language - Lsub.org

­ 10 ­

1 types:

2 Arry = array[0..1] of char;

3 Word = record{

4 chars: Arry;

5 n: int;

6 };

8 consts:

9 Greet = Word("hi", 2);

2.10. Pointers

A pointer data type refers to another type and permits using new and dispose to handledynamic variables of the pointed-to type. Type definition uses the ��^�� notation, takenfrom Pascal:

1 types:

2 Arry = array[1..10] of int;

3 Iptr = ^int;

4 Aptr = ^arry;

Line 2 declares an array data type used in line 4, to declare a pointer to Array data type.Line 3 declares a pointer to integer. It is legal to declare a pointer to a type that is notyet defined in the program, but the target type must de defined later. This permitsdeclaring circular data types, like linked lists. In no other case may a type be defined interms of not yet defined types.

Syntax to dereference a pointer value is taken from Pascal, and also uses the ��^��

sign:

iptr^ = 2;aptr^[1] = iptr^;

All memory allocated with new must be released by calling dispose before completion ofthe program, or the program will abort and report memory leaks.

2.11. Procedures and functions

Procedures are actions with names and do not return values. Argument passing is by-value by default. Multiple arguments are declared separated by commas. Using the key­word ref before an argument name makes pass-by-reference active for that parameter.For example,

1 procedure initword(ref w: Tword)

2 {

3 w = nil;

4 }

defines a procedure with a single argument, passed by reference, of type Tword.Instead,

1 procedure addtoword(ref w: Tword, c: char)

2 {

3 ...

4 }

defines a procedure with two arguments. w is of type Tword and passed by reference.However, c is of type char and is passed by value.

Functions are declared in a similar way, using the function keyword and declaring thereturn type like in this example:

Page 11: The Picky programming language - Lsub.org

­ 11 ­

1 function isblank(c: char): bool

2 {

3 return c == ’ ’ or c == Tab or c == Eol;

4 }

All function arguments must be passed by value. All in all, we teach that functionsshould have no lateral effects and should preserve referential transparency.

2.12. Global and local variables

Global variables are declared like types and constants, with a declaration block. In thiscase, the keyword vars must be used instead. For example:

1 program Xample;

2 vars:

3 n: int;

4 procedure main()

5 {

6 ...

7 }

The declaration uses the pascal colon syntax. Unlike in Pascal, it is not allowed todeclare a type on the fly in the variable declaration. A type identifier is required after thecolon. Also, there is no initialization syntax, by design. Variable initialization must hap­pen in the body of procedures and functions.

All variables are initialized to random values. That means that it is unlikely to findthem zeroed even the first time they are used.

Local variables are declared within the procedure or function header and its body.In this case, the vars declaration specifier is not used. Procedures and functions may notcontain constant or type definitions and so, declarations always refer to (local) variables.

This example declares a local variable named f:

1 function fact(n: int): int

2 f: int;

3 {

4 ...

5 return f;

6 }

2.13. Statements

Statements are not expressions (like in C), but actions (like in Pascal). They must be ter­minated by a ��;��. The null statement is just the ��;��, on its own. Statement blocks areenclosed by curly brackets, as it has been seen for procedure and function bodies,which are blocks.

Assignment uses the ��=�� operator, like in C. For example:

x = 0;

Needless to say that arguments must be type compatible and that the left part must bean L-value.

Function calls are not allowed as statements, because they are expressions. Proce­dure calls are allowed as statements (and not in expressions), and use the obvious syn­tax:

1 write(3);

2 writeln();

3 fwrite(stdout, Eol);

Page 12: The Picky programming language - Lsub.org

­ 12 ­

If there are no arguments, parenthesis must still be supplied.

The statement return returns a value from a function, like in the example of theprevious section. It is required that return is the last statement in the function body.Early returns are not allowed. It is permitted to use a conditional as the last statementin a function, as long as all its arms include a return statement as their last sentence.Procedures may not use return.

2.14. Control structures.

Conditional execution is controlled by the if statement, which borrows syntax from C.But there are differences. Statements used for then and else arms must be blocks. Thatis, brackets must be used always. For example:

1 if(len(w) > len(max)){

2 max = w;

3 }

or

1 if(c == ’ ’ or c == ’ ’){

2 read(c);

3 }else if(c == Eol){

4 readeol();

5 }

Multiple if statements may be chained by using an if statement directly in the else of aprevious if.

1 if(c == ’ ’ or c == ’ ’){

2 read(c);

3 }else if(c == Eol){

4 readeol();

5 }

while and do−while loops borrow the syntax from C:

1 do{

2 read(c);

3 }while(not eof() and isblank(c));

and

1 while(w != nil){

2 tot = tot + w^.len;

3 w = w^.next;

4 }

The for loop reminds to that of C, but has semantics closer to Pascal. Two expressions,an initialization and a condition, are present within parenthesis in the loop header. Theinitialization must be an assignment for a variable of an ordinal type. The conditionmust use any of the ��<��, ��<=��, ��>��, ��>=�� operators. The first two ones make the vari­able increase automatically after each iteration. The last two ones make the variabledecrease automatically after each iteration. For example:

1 for(i = 0, i < Nitems){

2 write(item[i]);

3 }

After the for loop, the control variable would be equal to the value on the right of thecondition. This implies that there is no out of range condition for the control variableeven when using ��<=��, or ��>=�� with the first or last valid value of an ordinal type. In

Page 13: The Picky programming language - Lsub.org

­ 13 ­

our example, i value would be Nitems when the loop is done.

Multi-way conditionals use a switch syntax that reminds to (but differs from) thatin C. Unlike in C, there is no fall-through; and there is no break statement. Expressionsused in each case may be single values (of an ordinal type), or multiple values separatedby commas (matching any of the arguments), or a range using the dot−dot notation. Forexample:

1 switch(4){

2 case 3,4..8:

3 c = True;

4 case 1..4:

5 c = True;

6 case 5:

7 c = True;

8 default:

9 ;

10 }

3. The compiler

The picky compiler, pick, is implemented in C for Plan 9 as of today. Ports to Linux, Win­dows and MacOS X are available. The description of the compiler provided in this sectioncorresponds to an early version of the implementation. It is meant to provide a hint topeople that must modify the compiler, but it is not up to date with respect to the imple­mentation. The language description of previous sections is, of course, up to date.

The compiler is implemented using yacc, and should be easy to understand. Thereare several things to know before attempting to modify it, which are documented here.

The compiler leaks memory. Programs are expected to be small, and we prefercompilation to be fast and the compiler to be robust. Therefore, data structures are sel­dom deallocated. Allocators for data structures request Aincr items at once whenexhausted, and they never release memory.

Symbol table handling as implemented is fast enough, but it is both simple andclumsy, and is the first thing that should be improved if more work is put in the com­piler.

There are no warnings. All diagnostics correspond to compile time errors. In manycases, when an error is detected, a symbol or node in the syntax tree is still built, forsafety; other parts of the compiler still get a data structure as expected, and it�s lesslikely that an invalid value causes a bug.

3.1. Symbol table

The symbol table is implemented as a stack of environments

/*

* One per program, procedure, and function.

* Used to keep symbols found in it and also to collect

* definitions for arguments, constants, types, variables, and statements.

*/

struct Env

{

ulong id;

Sym* tab[Nhash]; /* symbol table */

Env* prev; /* in stack */

Sym* prog; /* ongoing program, procedure, or function */

Type* rec; /* ongoing record definition */

};

Page 14: The Picky programming language - Lsub.org

­ 14 ­

The global env points to the top of the stack. There is an initial environment used forthe top-level (the outer scope). Another environment is pushed for each procedure,function, argument list, and record field list that is found. In some cases, the attributesin the grammar are not used to populate a node in the syntax tree. Instead, the globalenv is accessed to locate the procedure, function, or program being defined. The sameis done to define fields for records. In most other cases, attributes as handled by yaccsuffice.

Each environment is a hash table that keeps symbols for the compiler. Two addi­tional hash tables are kept. One to store strings and another to store keywords.

static Sym *strs[Nbighash]; /* strings and names */

static Sym *keys[Nhash]; /* keywords and top−level */

The former is used to keep an entry for each name found in the source. For simplicity, itmaintains Syms and not strings. The later is used to keep keywords and global defini­tions. The scanner (done by hand) looks up in these tables to learn if a token for a key­word should be given to the parser. In most other cases, it allocates a new entry in thestrings table and returns its symbol.

The grammar uses different tokens for identifiers and type identifiers. Therefore,the scanner checks if an (already defined) identifier is for a type or for any other value.

A symbol is represented by this data structure. For simplicity, the same data struc­ture is used to correspond to nodes in the syntax tree for expressions, albeit strictlyspeaking they are not symbols.

/*

* Symbol table entry.

*/

struct Sym

{

ulong id;

char* name;

Sym* hnext;

int stype;

int op;

char* fname;

int lineno;

Type* type;

Page 15: The Picky programming language - Lsub.org

­ 15 ­

union{

int tok;

long ival;

double rval;

char* sval;

struct{

int used;

int set;

};

struct{ /* binary, unary */

Sym* left;

Sym* right;

};

struct{ /* Sfcall */

Sym* fsym;

List* fargs;

};

struct{ /* "." */

Sym* rec;

Sym* field;

};

Prog* prog;

};

/* backend */

union{

ulong addr;

ulong off; /* fields */

};

};

The union(s) correspond to attributes for the symbol and backend information. In gen­eral, a symbol has a name, belongs to a type of symbol (stype) and depending on thetype may correspond to one operation or another (op). These are the types of symbolsknown:

/* symbol types and subtypes */

Snone = 0,

Skey, /* keyword */

Sstr, /* a string buffer */

Sconst, /* constant or literal */

Stype, /* type def */

Svar, /* obj def */

Sunary, /* unary expression */

Sbinary, /* binary expression */

Sproc, /* procedure */

Sfunc, /* function */

Sfcall, /* procedure or function call */

Symbols used to represent expressions carry in op the operation for the node:

Page 16: The Picky programming language - Lsub.org

­ 16 ­

Onone = 0,

Ole,

Oge,

Odotdot,

Oand,

Oor, /* 5 */

Oeq,

One,

Opow,

Oint,

Onil, /* 10 */

Ochar,

Oreal,

Ostr,

Otrue,

Ofalse, /* 15 */

Onot,

Olit,

Ocast,

Oparm,

Orefparm, /* 20 */

Olvar,

Ouminus,

In some cases, a symbol keeps a list of symbols as children. In all such cases, a Liststructure is used:

struct List

{

int nitems;

int kind;

union{

Stmt** stmt;

Sym** sym;

void** items;

};

};

where kind must be any of

/* List kinds */

Lstmt = 0,

Lsym,

For example, argument lists are lists of kind Lsym, and statement blocks are lists ofkind Lstmt.

An important symbol type is that for programs (and procedures and functions). Itholds a Prog structure as its value, also linked from the corresponding Env structure.

Page 17: The Picky programming language - Lsub.org

­ 17 ­

struct Prog

{

Sym* psym;

List* parms;

Type* rtype; /* ret type or nil if none */

List* consts;

List* types;

List* vars;

List* procs;

Stmt* stmt;

Builtin *b;

int nrets;

/* backend */

Code code;

ulong parmsz;

ulong varsz;

};

The parser adds new symbols to the lists of constants, types, variables, andprocedures/functions, as new elements are analyzed in the source. The single stmt is ablock for the body of the procedure or function. For built-ins, b keeps a Builtin structureused to decorate the parser node with attributes and to encode the type signature.

struct Builtin

{

char *name;

u32int id;

int kind;

char *args;

char r;

Sym* (*fn)(Builtin *b, List *args);

};

3.2. Data types

Each symbol is expected to have a type attached. The type is described by this datastructure:

Page 18: The Picky programming language - Lsub.org

­ 18 ­

/*

* Types

*/

struct Type

{

int op;

Sym* sym;

int first;

int last;

union{

List* lits; /* Tenum */

Type* ref; /* Tptr */

Type* super; /* Trange */

struct{ /* Tarry, Tstr */

Type* idx;

Type* elem;

};

List* fields; /* Trec */

struct{

List* parms; /* Tproc, Tfunc */

Type* rtype;

};

};

/* backend */

ulong id;

ulong sz;

};

Type constructors allocate new structures. Two types are compatible if their address inmemory are the same. Exceptions are made to support universally compatible datatypes, as used for constants.

The op field in type identifies the kind of type. It is any of:

/* Type kinds */

Tundef = 0,

Tint,

Tbool,

Tchar,

Treal,

Tenum, /* 5 */

Trange,

Tarry,

Trec,

Tptr,

Tfile, /* 10 */

Tproc,

Tfunc,

Tprog,

Tfwd,

Tstr, /* 15; fake: array[int] of char; but universal */

Type Twd is used to temporarily define a type as a forward declaration. This is used forpointers, which permit the target type to be defined later. Type Tstr is an artifact, torepresent strings which are type-compatible with arrays of characters of the samelength.

All ordinal types have their first and last values stored in their Type structure. Thisis to perform range checks without paying attention to the difference between types andsubtypes (only subranges as of today).

Page 19: The Picky programming language - Lsub.org

­ 19 ­

3.3. Statements

Statements are described by stmt structures:

/*

* Statements

*/

struct Stmt

{

int op;

char* sfname;

int lineno;

union{

List* list; /* ’{’ */

struct{ /* = */

Sym* lval;

Sym* rval;

};

struct{ /* IF */

Sym* cond;

Stmt* thenarm;

Stmt* elsearm;

};

Sym* fcall; /* FCALL */

struct{

Sym* expr; /* RETURN, DO, WHILE, CASE */

Stmt* stmt;

};

};

};

The op field identifies the kind of statement. A token representative of the statement isused for this purpose. The union keeps the information describing the statement.

Statements for for loops are rewritten as a block that contains the initialization, awhile loop, and its body adjusted to include the increment or decrement for the controlvariable.

Switch statements are also rewritten, to use a sequence of chained if−then−elsestatements, each one checking the value of the expression we are switching on. To pre­vent multiple evaluation of the switch expression, a variable is declared by the compilerfor each such statement. The switch is rewritten to initialize the variable with the valueof the expression, and then execute the chained if corresponding to the branches.

3.4. Builtins and predefined identifiers.

Builtin procedures and functions have type signatures generated from a descriptionstring within the front-end. Arguments are checked by a generic builtin type check func­tion, which takes into account the polymorphic nature of procedures like write.

Builtin functions check to see if their arguments are evaluated as a result of con­structing their nodes in the front-end. In that case, if the builtin may yield a value atcompile time, the function call is replaced by the resulting value. The implementationtries to check if arguments are legal (e.g., would cause a floating point exception) andissue a sensible diagnostic otherwise. This process is guided by a Builtin structure asshown before.

Calls to file procedures and functions that operate on stdin and stdout are rewrittento pass the file explicitly, using the variants of the builtins that accept a file argument.

Pre-defined constants and variables are added to the environment for the top-levelscope as soon as the parser tries to declare a program. Afterwards, they are handled likeuser defined objects.

Page 20: The Picky programming language - Lsub.org

­ 20 ­

3.5. Code generation

Code generation is straightforward, and uses back-patching to set label addresses. Pro­cedure are called by procedure number, and not by procedure addresses. Therefore, thismechanism is not applied in this case.

Code is generated in blocks (one per procedure), using this structure:

/* generated code */

struct Code

{

u32int addr;

Pcent* pcs;

Pcent* pcstl;

u32int* p;

ulong np;

ulong ap;

};

Here, p is the pointer to byte-codes (actually using a full u32int each); np is the numberof byte-codes (words) produced, and ap is the number of byte-code slots (words) avail­able in p.

For each statement, and for symbol and expression nodes, entries to match pro­gram counter to source file and line are linked into the code structure.

/* pc/src table */

struct Pcent

{

Pcent* next;

Stmt* st;

Sym* nd;

ulong pc;

};

Either st or nd is used, not both at the same time.

4. The interpreter

The description of the interpreter provided in this section corresponds to an early ver­sion of the implementation. It is meant to provide a hint to people that must modify theinterpreter, but it is not up to date with respect to the implementation. The languagedescription of early sections is, of course, up to date.

The interpreter, pam, implements an abstract machine known as PAM. Themachine is a stack based machine. Most operations take arguments from the stack andreplace them with a result, pushed also on the stack. There is a single flow of control,guided by an (almost) endless loop switching on the instruction type.

The interpreter leaks memory for storage allocated with new, to detect when dis­posed data structures are used and issue more descriptive diagnostics than ��segmenta­tion violation��.

Also, it checks that assigned values are in range, more often than needed, to try todetect constraint errors early in the execution.

All memory, both data, stack variables, and dynamic memory, is initialized withrandom values, to let the user discover early that variable initialization is missing. Suchrandom values are always odd, to recognize pointer values not initialized, and issue adescriptive diagnostic for that case at run time, instead of a ��segmentation violation�� orproducing a heisen-bug.

Page 21: The Picky programming language - Lsub.org

­ 21 ­

4.1. PAM

PAM is the Picky Abstract Machine. It has the following elements:

� Some registers:

pc Program counter. Addressing words, each one a byte-code.

fp Frame pointer. Addressing bytes. To locate the activation frame for the cur­rent procedure.

sp Stack pointer. Addressing bytes. To locate the top of the stack.

vp (Local) Variable pointer. Used to translate local variable addresses into actualmemory addresses.

ap Argument pointer. Used to translate local argument addresses into actualmemory addresses.

pid Procedure identifier. Used to locate the descriptor for the procedure executing(or function).

� Text memory. Word addressed area of memory used to keep byte codes. Each bytecode is a word, not a byte. Operations taking an argument use another word forthe argument. The pc register indexes this memory, starting at 0.

� Stack memory. Byte addressed area of memory containing global variables (bottomof stack) and activation frames for procedures and functions. Stack addresses aremachine addresses (i.e., actual addresses as used by the C implementation ofPAM). All of sp, fp, vp, and ap point into this memory (i.e., they are actual C point­ers in the implementation).

� Dynamic memory. Dynamic variables are stored using the underlying C heap. How­ever, pointer values are references to descriptors that refer to the actual memoryallocated. This is used as a fence to detect run time errors in user pointers, toissue diagnostics that help.

� Procedure descriptors. An array indexed by procedure identifier containing meta­data for procedures and functions.

� Type descriptors. An array indexed by type identifier containing descriptions fortypes, both built-in and user defined types.

� Variable descriptors. An array indexed by variable identifier containing metadatafor variables (e.g., their type identifiers).

� Program counter entries. An array mapping program counters to source file namesand line numbers.

A procedure descriptor contains this information:

struct Pent

{

char *name; /* for procedure/function */

ulong addr; /* for its code in text */

int nargs; /* # of arguments */

int nvars; /* # of variables

int retsz; /* size for return type or 0 */

int argsz; /* size for arguments in stack */

int varsz; /* size for local vars in stack */

char *fname;

int lineno;

Vent *args; /* Var descriptors for args */

Vent *vars; /* Var descriptors for local vars. */

};

A type descriptor contains enough to perform range checks, learn how to read values forthe type, or write values for the type, learn the size for objects, and handle or dump

Page 22: The Picky programming language - Lsub.org

­ 22 ­

objects for debugging.

struct Tent

{

char *name; /* of the type */

char fmt; /* value format character */

long first; /* legal value or index */

long last; /* idem */

int nitems; /* # of values or elements */

ulong sz; /* in memory for values */

uint etid; /* element type id */

char **lits; /* names for literals */

Vent *fields; /* only name, tid, and addr defined */

};

A variable descriptor is used to describe variables, mostly for debugging and stackdumps.

struct Vent

{

char *name; /* of variable or constant */

uint tid; /* type id */

ulong addr; /* in memory (offset for args, l.vars.) */

char *fname;

int lineno;

char *val; /* initial value as a string, or nil. */

};

Program counter entries have this information. Some fields are used to report leaks afterprogram completion.

struct Pc

{

ulong pc;

char *fname;

ulong lineno;

Pc* next; /* Pc with leaks; for leaks */

uint n; /* # of leaks in this Pc; for leaks */

};

4.2. Instruction set

An instruction has two fields: an instruction code and an instruction type. The formerdescribes the instruction. The later describes if it handles integers, floats, or memoryaddresses (in those cases when the instruction can do several of them). This is theinstruction set:

add daddr eqm idx lt mul not stoaddr data eqr ind ltr mulr or stomand datar fld jmp lvar ne pow subarg div ge jmpf minus nea ptr subrcall divr ger jmpt minusr nem pushcast eq gt le mod ner pushrcastr eqa gtr ler modr nop ret

PAM instructions are described by this enumeration (explained later).

Page 23: The Picky programming language - Lsub.org

­ 23 ­

/* instruction code (ic) */

ICnop = 0, /* nop */

ICle, /* le|r −sp −sp +sp */

ICge, /* ge|r −sp −sp +sp */

ICpow, /* pow −sp −sp +sp */

IClt, /* lt|r −sp −sp +sp */

ICgt, /* gt|r −sp −sp +sp */

ICmul, /* mul|r −sp −sp +sp */

ICdiv, /* div|r −sp −sp +sp */

ICmod, /* mod|r −sp −sp +sp */

ICadd, /* add|r −sp −sp +sp */

ICsub, /* sub|r −sp −sp +sp */

ICminus, /* minus|r −sp +sp */

ICnot, /* not −sp +sp */

ICor, /* or −sp −sp +sp */

ICand, /* and −sp −sp +sp */

ICeq, /* eq|r|a −sp −sp +sp */

ICne, /* ne|r|a −sp −sp +sp */

ICptr, /* ptr −sp +sp */

/* obtain address for ptr in stack */

ICargs, /* those after have an argument */

ICpush=ICargs, /* push|r n +sp */

/* push n in the stack */

ICindir, /* indir|a n −sp +sp */

/* replace address with referenced bytes */

ICjmp, /* jmp addr */

ICjmpt, /* jmpt addr */

ICjmpf, /* jmpf addr */

ICidx, /* idx tid −sp −sp +sp */

/* replace address[index] with elem. addr. */

ICfld, /* fld n −sp +sp */

/* replace obj addr with field (at n) addr. */

ICdaddr, /* daddr n +sp */

/* push address for data at n */

ICdata, /* data n +sp */

/* push n bytes of data following instruction */

ICeqm, /* eqm n −sp −sp +sp */

/* compare data pointed to by addresses */

ICnem, /* nem n −sp −sp +sp */

/* compare data pointed to by addresses */

ICcall, /* call pid */

ICret, /* ret pid */

ICarg, /* arg n +sp */

/* push address for arg object at n */

IClvar, /* lvar n +sp*/

/* push address for lvar object at n */

ICstom, /* stom tid −sp −sp */

/* cp tid’s sz bytes from address to address */

ICsto, /* sto tid −sp −sp */

/* cp tid’s sz bytes to address from stack */

ICcast, /* cast|r tid −sp +sp */

/* convert int (or real |r) to type tid */

Page 24: The Picky programming language - Lsub.org

­ 24 ­

/* instr. type (it) */

ITint = 0,

ITaddr = 0x40,

ITreal = 0x80,

ITmask = ITreal|ITaddr,

All instructions above ICargs (which is not an instruction) do not have a following argu­ment in the program text. A single word contains the entire instruction. Those belowuse a following word to contain the argument for the instruction.

Instructions that have a suffix ��|r�� in their comment have a variant that knowshow to handle reals. For example, the entry for ICpush means that there are two instruc­tions: push and pushr. The former pushes an integer value (the argument) in thestack. The later pushes a float value in the stack.

Instructions with the suffix ��|a�� have a variant that handles addresses.

All atomic values in the stack (booleans, characters, integers, and floats) occupy asingle word (32 bits). Addresses use 64 bits, to simplify execution in 64 bit environ­ments. That is, addresses may be actual pointers. For example, there are three eqinstructions: eq, eqr, and eqa: They compare integers, floats, and addresses (respec­tively).

Besides the argument in the program text, most instructions operate with stackarguments (and pop them off the stack) and push results back into the stack. This isrepresented by the ��+sp�� (push) and ��−sp�� in the description. Each one of the latterrefers to a single argument taken from the stack.

4.3. Builtins

Builtin procedures and functions have addresses that are not procedure ids. Instead,they have the PAMbuiltin bit set and contain a builtin number in remaining bits:

/* Builtin addresses */

PAMbuiltin = 0x80000000,

/* builtin numbers (must be |PAMbuiltin) */

PBacos = 0,

PBasin,

PBatan,

PBclose,

PBcos,

PBdispose, /* 0x5 */

PBexp,

PBfatal,

PBfeof,

PBfeol,

PBfpeek, /* 0xa */

PBfread,

PBfreadeol,

PBfrewind,

PBfwrite,

PBfwriteln, /* 0xf */

Page 25: The Picky programming language - Lsub.org

­ 25 ­

PBfwriteeol,

PBlog,

PBlog10,

PBnew,

PBopen, /* 0x14 */

PBpow,

PBpred,

PBsin,

PBsqrt,

PBsucc, /* 0x19 */

PBtan,

PBstack,

PBdata,

The arguments for each builtin do not always match those supplied by the user. Forexample, file I/O procedures carry a type id besides the object or value to let PAM knowhow to read and write the argument (i.e., which is is its type descriptor). This is not doc­umented here. See the implementation for the builtins in pilib.c.

4.4. Binary files.

A PAM binary is indeed a PAM assembly file and not a binary. It is a text file, both fordebugging and for portability and pedagogical purposes.

The file must start with

#!/bin/pi

Lines starting with ��#�� are ignored. The second line must report the procedure id formain:

entry 3

for example. Following this, there are different sections for types, variables (and con­stants), procedures, text, and PC/source entries. Each section starts with a line that hasthe keyword types, vars, procs, text, and pcs (respectively) followed by the number ofentries in the section. Each entry is a descriptor (see above) or a text instruction (per­haps with an argument in the same line).

Descriptors have the information shown in the structures found before in this doc­ument. Instructions have their address, instruction code (mnemonic, actually) and argu­ment if any.

The compiler adds comments in the assembly file to match PAM instructions withthe source code.

5. Example source

1 /*

2 * Example program. Write the longest word in the input.

3 */

4 program Word;

6 consts:

7 Blocknc = 2;

Page 26: The Picky programming language - Lsub.org

­ 26 ­

9 types:

10 Tblock = array[1..Blocknc] of char;

11 Tword = ^Tnode;

12 Tnode = record{

13 block: Tblock;

14 nc: int;

15 next: Tword;

16 };

19 function isblank(c: char): bool

20 {

21 return c == ’ ’ or c == Tab or c == Eol;

22 }

24 procedure skipblanks(ref end: bool)

25 c: char;

26 {

27 do{

28 peek(c);

29 if(c == ’ ’ or c == ’ ’){

30 read(c);

31 }else if(c == Eol){

32 readeol();

33 }

34 }while(not eof() and isblank(c));

35 end = eof();

36 }

38 procedure initword(ref w: Tword)

39 {

40 w = nil;

41 }

43 function wordnc(w: Tword): int

44 tot: int;

45 {

46 tot = 0;

47 while(w != nil){

48 tot = tot + w^.nc;

49 w = w^.next;

50 }

51 return tot;

52 }

54 procedure writeword(w: Tword)

55 i: int;

56 {

57 write("’");

58 while(w != nil){

59 for(i = 1, i <= w^.nc){

60 write(w^.block[i]);

61 }

62 w = w^.next;

63 }

64 write("’");

65 }

Page 27: The Picky programming language - Lsub.org

­ 27 ­

67 procedure mkblock(ref w: Tword)

68 {

69 new(w);

70 w^.nc = 0;

71 w^.next = nil;

72 }

74 procedure addtoword(ref w: Tword, c: char)

75 p: Tword;

76 {

77 if(w == nil){

78 mkblock(w);

79 }

80 p = w;

81 while(p^.next != nil){

82 p = p^.next;

83 }

84 if(p^.nc == Blocknc){

85 mkblock(p^.next);

86 p = p^.next;

87 }

88 p^.nc = p^.nc + 1;

89 p^.block[p^.nc] = c;

90 }

92 procedure delword(ref w: Tword)

93 {

94 if(w != nil){

95 delword(w^.next);

96 dispose(w);

97 initword(w);

98 }

99 }

101 procedure readword(ref w: Tword)

102 c: char;

103 {

104 do{

105 read(c);

106 addtoword(w, c);

107 peek(c);

108 }while(not eof() and not isblank(c));

109

110 }

Page 28: The Picky programming language - Lsub.org

­ 28 ­

112 function wordchar(w: Tword, n: int): char

113 c: char;

114 {

115 c = ’?’;

116 while(n > 0 and w != nil){

117 if(n <= Blocknc){

118 c = w^.block[n];

119 n = 0;

120 }else{

121 n = n − Blocknc;

122 w = w^.next;

123 }

124 }

125 return c;

126 }

128 procedure cpword(ref dw: Tword, sw: Tword)

129 i: int;

130 {

131 delword(dw);

132 for(i = 1, i <= wordnc(sw)){

133 addtoword(dw, wordchar(sw, i));

134 }

135 }

137 procedure main()

138 done: bool;

139 w: Tword;

140 max: Tword;

141 {

142 initword(max);

143 do{

144 skipblanks(done);

145 if(not done){

146 initword(w);

147 readword(w);

148 if(wordnc(w) > wordnc(max)){

149 cpword(max, w);

150 }

151 delword(w);

152 }

153 }while(not eof());

154 writeword(max);

155 write(" with len ");

156 writeln(wordnc(max));

157 delword(max);

158 }

6. Example binary

This is the binary file produced for the source in the previous section.

Page 29: The Picky programming language - Lsub.org

­ 29 ­

1 #!/bin/pam

2 entry 11

3 types 12

4 0 bool b 0 1 2 4 0

5 1 char c 0 255 256 4 0

6 2 int i −2147483646 2147483647 0 4 0

7 3 float r 0 0 0 4 0

8 4 $nil p 0 0 0 8 0

9 5 file f 0 0 0 4 0

10 6 $range0 i 1 2 2 4 0

11 7 Tblock a 1 2 2 8 1

12 8 Tword p 0 0 0 8 9

13 9 Tnode R 0 0 3 20 0

14 block 7 0x0

15 nc 2 0x8

16 next 8 0xc

17 10 $tstr1 s 0 0 1 4 1

18 11 $tstr10 s 0 9 10 40 1

19 vars 16

20 Maxint 2 0x0 2147483647 dwords.p 4

21 Minint 2 0x4 0 dwords.p 4

22 Maxchar 1 0x8 255 dwords.p 4

23 Minchar 1 0xc 0 dwords.p 4

24 Eol 1 0x10 10 dwords.p 31

25 Cr 1 0x14 13 dwords.p 4

26 Eof 1 0x18 255 dwords.p 4

27 Tab 1 0x1c 9 dwords.p 21

28 Esc 1 0x20 27 dwords.p 4

29 Nul 1 0x24 0 dwords.p 4

30 Blocknc 2 0x28 2 dwords.p 121

31 $s0 10 0x2c ’’’’ dwords.p 57

32 $s1 10 0x30 ’’’’ dwords.p 64

33 $s2 11 0x34 ’ with len ’ dwords.p 155

34 stdin 5 0x5c − dwords.p 4

35 stdout 5 0x60 − dwords.p 4

36 procs 12

37 0 isblank 0x000 1 0 4 4 0 dwords.p 108

38 c 1 0x0 − dwords.p 21

39 1 skipblanks 0x019 1 1 0 8 4 dwords.p 144

40 end 0 0x0 − dwords.p 35

41 c 1 0x0 − dwords.p 34

42 2 initword 0x06b 1 0 0 8 0 dwords.p 146

43 w 8 0x0 − dwords.p 40

44 3 wordnc 0x077 1 1 4 8 4 dwords.p 156

45 w 8 0x0 − dwords.p 49

46 tot 2 0x0 − dwords.p 51

47 4 writeword 0x0ad 1 1 0 8 4 dwords.p 154

48 w 8 0x0 − dwords.p 62

49 i 2 0x0 − dwords.p 60

50 5 mkblock 0x116 1 0 0 8 0 dwords.p 85

51 w 8 0x0 − dwords.p 71

52 6 addtoword 0x13c 2 1 0 12 8 dwords.p 133

53 w 8 0x4 − dwords.p 80

54 c 1 0x0 − dwords.p 89

55 p 8 0x0 − dwords.p 89

56 7 delword 0x1c1 1 0 0 8 0 dwords.p 157

57 w 8 0x0 − dwords.p 97

58 8 readword 0x1e7 1 1 0 8 4 dwords.p 147

59 w 8 0x0 − dwords.p 106

60 c 1 0x0 − dwords.p 108

Page 30: The Picky programming language - Lsub.org

­ 30 ­

61 9 wordchar 0x216 2 1 4 12 4 dwords.p 133

62 w 8 0x4 − dwords.p 122

63 n 2 0x0 − dwords.p 121

64 c 1 0x0 − dwords.p 125

65 10 cpword 0x26d 2 1 0 16 4 dwords.p 149

66 dw 8 0x8 − dwords.p 133

67 sw 8 0x0 − dwords.p 133

68 i 2 0x0 − dwords.p 133

69 11 main 0x2a4 0 3 0 0 20 dwords.p 137

70 done 0 0x0 − dwords.p 145

71 w 8 0x4 − dwords.p 151

72 max 8 0xc − dwords.p 157

73 text 773

74 # isblank()

75 # {...}

76 # return or(or(==($c: char, ’ ’), ==($c: char, Tab=Tab)), ==($c: char, Eol=Eol))

77 00000 push 0x0000000a # Eol=Eol;

78 00002 arg 0x00000000 # $c: char;

79 00004 ind 0x00000004

80 00006 eq

81 00007 push 0x00000009 # Tab=Tab;

82 00009 arg 0x00000000 # $c: char;

83 0000b ind 0x00000004

84 0000d eq

85 0000e push 0x00000020 # ’ ’;

86 00010 arg 0x00000000 # $c: char;

87 00012 ind 0x00000004

88 00014 eq

89 00015 or

90 00016 or

91 00017 ret 0x00000000

92 # skipblanks()

93 # {...}

94 # dowhile(and(not(feof(stdin: file)), isblank(%c: char)))

95 # {...}

96 # fpeek(stdin: file, %c: char)

97 00019 lvar 0x00000000 # %c: char;

98 0001b daddr 0x0000005c # stdin: file;

99 0001d ind 0x00000004

100 0001f call 0x8000000a # fpeek();

101 # if(or(==(%c: char, ’ ’), ==(%c: char, Tab)))

102 00021 push 0x00000009 # Tab;

103 00023 lvar 0x00000000 # %c: char;

104 00025 ind 0x00000004

105 00027 eq

106 00028 push 0x00000020 # ’ ’;

107 0002a lvar 0x00000000 # %c: char;

108 0002c ind 0x00000004

109 0002e eq

110 0002f or

111 00030 jmpf 0x0000003e

112 # {...}

113 # fread(stdin: file, %c: char)

114 00032 lvar 0x00000000 # %c: char;

115 00034 daddr 0x0000005c # stdin: file;

116 00036 ind 0x00000004

117 00038 push 0x00000001

118 0003a call 0x8000000b # fread();

119 0003c jmp 0x0000004d

120 # if(==(%c: char, Eol=Eol))

Page 31: The Picky programming language - Lsub.org

­ 31 ­

121 0003e push 0x0000000a # Eol=Eol;

122 00040 lvar 0x00000000 # %c: char;

123 00042 ind 0x00000004

124 00044 eq

125 00045 jmpf 0x0000004d

126 # {...}

127 # freadeol(stdin: file)

128 00047 daddr 0x0000005c # stdin: file;

129 00049 ind 0x00000004

130 0004b call 0x8000000c # freadeol();

131 0004d lvar 0x00000000 # %c: char;

132 0004f ind 0x00000004

133 00051 call 0x00000000 # isblank();

134 00053 daddr 0x0000005c # stdin: file;

135 00055 ind 0x00000004

136 00057 call 0x80000008 # feof();

137 00059 not

138 0005a and

139 0005b jmpt 0x00000019

140 # &end: bool = feof(stdin: file)

141 0005d daddr 0x0000005c # stdin: file;

142 0005f ind 0x00000004

143 00061 call 0x80000008 # feof();

144 00063 arg 0x00000000 # &end: bool;

145 00065 ind 0x00000008

146 00067 sto 0x00000000

147 # return <nil>

148 00069 ret 0x00000001

149 # initword()

150 # {...}

151 # &w: Tword = nil

152 0006b data 0x00000008 # nil;

153 0006d 0x0

154 0006e 0x0

155 0006f arg 0x00000000 # &w: Tword;

156 00071 ind 0x00000008

157 00073 sto 0x00000008

158 # return <nil>

159 00075 ret 0x00000002

160 # wordnc()

161 # {...}

162 # %tot: int = 0

163 00077 push 0x00000000 # 0;

164 00079 lvar 0x00000000 # %tot: int;

165 0007b sto 0x00000002

166 # while(!=($w: Tword, nil))

167 0007d data 0x00000008 # nil;

168 0007f 0x0

169 00080 0x0

170 00081 arg 0x00000000 # $w: Tword;

171 00083 ind 0x00000008

172 00085 nea

173 00086 jmpf 0x000000a7

174 # {...}

175 # %tot: int = +(%tot: int, .(^($w: Tword), nc: int))

176 00088 arg 0x00000000 # .; ^; $w: Tword;

177 0008a ind 0x00000008

178 0008c ptr

179 0008d fld 0x00000008

180 0008f ind 0x00000004

Page 32: The Picky programming language - Lsub.org

­ 32 ­

181 00091 lvar 0x00000000 # %tot: int;

182 00093 ind 0x00000004

183 00095 add

184 00096 lvar 0x00000000 # %tot: int;

185 00098 sto 0x00000002

186 # $w: Tword = .(^($w: Tword), next: Tword)

187 0009a arg 0x00000000 # .; ^; $w: Tword;

188 0009c ind 0x00000008

189 0009e ptr

190 0009f fld 0x0000000c

191 000a1 arg 0x00000000 # $w: Tword;

192 000a3 stom 0x00000008

193 000a5 jmp 0x0000007d

194 # return %tot: int

195 000a7 lvar 0x00000000 # %tot: int;

196 000a9 ind 0x00000004

197 000ab ret 0x00000003

198 # writeword()

199 # {...}

200 # fwrite(stdout: file, $s0="’")

201 000ad daddr 0x0000002c # $s0="’";

202 000af ind 0x00000004

203 000b1 daddr 0x00000060 # stdout: file;

204 000b3 ind 0x00000004

205 000b5 push 0x0000000a

206 000b7 call 0x8000000e # fwrite();

207 # while(!=($w: Tword, nil))

208 000b9 data 0x00000008 # nil;

209 000bb 0x0

210 000bc 0x0

211 000bd arg 0x00000000 # $w: Tword;

212 000bf ind 0x00000008

213 000c1 nea

214 000c2 jmpf 0x00000108

215 # {...}

216 # {...}

217 # %i: int = 1

218 000c4 push 0x00000001 # 1;

219 000c6 lvar 0x00000000 # %i: int;

220 000c8 sto 0x00000002

221 # while(<=(%i: int, .(^($w: Tword), nc: int)))

222 000ca arg 0x00000000 # .; ^; $w: Tword;

223 000cc ind 0x00000008

224 000ce ptr

225 000cf fld 0x00000008

226 000d1 ind 0x00000004

227 000d3 lvar 0x00000000 # %i: int;

228 000d5 ind 0x00000004

229 000d7 le

230 000d8 jmpf 0x000000fb

231 # {...}

232 # fwrite(stdout: file, [](.(^($w: Tword), block: Tblock), %i: int))

233 000da lvar 0x00000000 # []; %i: int;

234 000dc ind 0x00000004

235 000de arg 0x00000000 # .; ^; $w: Tword;

236 000e0 ind 0x00000008

237 000e2 ptr

238 000e3 idx 0x00000007

239 000e5 ind 0x00000004

240 000e7 daddr 0x00000060 # stdout: file;

Page 33: The Picky programming language - Lsub.org

­ 33 ­

241 000e9 ind 0x00000004

242 000eb push 0x00000001

243 000ed call 0x8000000e # fwrite();

244 # %i: int = succ(%i: int)

245 000ef lvar 0x00000000 # %i: int;

246 000f1 ind 0x00000004

247 000f3 call 0x80000019 # succ();

248 000f5 lvar 0x00000000 # %i: int;

249 000f7 sto 0x00000002

250 000f9 jmp 0x000000ca

251 # $w: Tword = .(^($w: Tword), next: Tword)

252 000fb arg 0x00000000 # .; ^; $w: Tword;

253 000fd ind 0x00000008

254 000ff ptr

255 00100 fld 0x0000000c

256 00102 arg 0x00000000 # $w: Tword;

257 00104 stom 0x00000008

258 00106 jmp 0x000000b9

259 # fwrite(stdout: file, $s1="’")

260 00108 daddr 0x00000030 # $s1="’";

261 0010a ind 0x00000004

262 0010c daddr 0x00000060 # stdout: file;

263 0010e ind 0x00000004

264 00110 push 0x0000000a

265 00112 call 0x8000000e # fwrite();

266 # return <nil>

267 00114 ret 0x00000004

268 # mkblock()

269 # {...}

270 # new(&w: Tword)

271 00116 arg 0x00000000 # &w: Tword;

272 00118 ind 0x00000008

273 0011a push 0x00000008

274 0011c call 0x80000013 # new();

275 # .(^(&w: Tword), nc: int) = 0

276 0011e push 0x00000000 # 0;

277 00120 arg 0x00000000 # .; ^; &w: Tword;

278 00122 ind 0x00000008

279 00124 ind 0x00000008

280 00126 ptr

281 00127 fld 0x00000008

282 00129 sto 0x00000002

283 # .(^(&w: Tword), next: Tword) = nil

284 0012b data 0x00000008 # nil;

285 0012d 0x0

286 0012e 0x0

287 0012f arg 0x00000000 # .; ^; &w: Tword;

288 00131 ind 0x00000008

289 00133 ind 0x00000008

290 00135 ptr

291 00136 fld 0x0000000c

292 00138 sto 0x00000008

293 # return <nil>

294 0013a ret 0x00000005

295 # addtoword()

296 # {...}

297 # if(==(&w: Tword, nil))

298 0013c data 0x00000008 # nil;

299 0013e 0x0

300 0013f 0x0

Page 34: The Picky programming language - Lsub.org

­ 34 ­

301 00140 arg 0x00000004 # &w: Tword;

302 00142 ind 0x00000008

303 00144 ind 0x00000008

304 00146 eqa

305 00147 jmpf 0x0000014f

306 # {...}

307 # mkblock(&w: Tword)

308 00149 arg 0x00000004 # &w: Tword;

309 0014b ind 0x00000008

310 0014d call 0x00000005 # mkblock();

311 # %p: Tword = &w: Tword

312 0014f arg 0x00000004 # &w: Tword;

313 00151 ind 0x00000008

314 00153 lvar 0x00000000 # %p: Tword;

315 00155 stom 0x00000008

316 # while(!=(.(^(%p: Tword), next: Tword), nil))

317 00157 data 0x00000008 # nil;

318 00159 0x0

319 0015a 0x0

320 0015b lvar 0x00000000 # .; ^; %p: Tword;

321 0015d ind 0x00000008

322 0015f ptr

323 00160 fld 0x0000000c

324 00162 ind 0x00000008

325 00164 nea

326 00165 jmpf 0x00000174

327 # {...}

328 # %p: Tword = .(^(%p: Tword), next: Tword)

329 00167 lvar 0x00000000 # .; ^; %p: Tword;

330 00169 ind 0x00000008

331 0016b ptr

332 0016c fld 0x0000000c

333 0016e lvar 0x00000000 # %p: Tword;

334 00170 stom 0x00000008

335 00172 jmp 0x00000157

336 # if(==(.(^(%p: Tword), nc: int), Blocknc=2))

337 00174 push 0x00000002 # Blocknc=2;

338 00176 lvar 0x00000000 # .; ^; %p: Tword;

339 00178 ind 0x00000008

340 0017a ptr

341 0017b fld 0x00000008

342 0017d ind 0x00000004

343 0017f eq

344 00180 jmpf 0x00000196

345 # {...}

346 # mkblock(.(^(%p: Tword), next: Tword))

347 00182 lvar 0x00000000 # .; ^; %p: Tword;

348 00184 ind 0x00000008

349 00186 ptr

350 00187 fld 0x0000000c

351 00189 call 0x00000005 # mkblock();

352 # %p: Tword = .(^(%p: Tword), next: Tword)

353 0018b lvar 0x00000000 # .; ^; %p: Tword;

354 0018d ind 0x00000008

355 0018f ptr

356 00190 fld 0x0000000c

357 00192 lvar 0x00000000 # %p: Tword;

358 00194 stom 0x00000008

359 # .(^(%p: Tword), nc: int) = +(.(^(%p: Tword), nc: int), 1)

360 00196 push 0x00000001 # 1;

Page 35: The Picky programming language - Lsub.org

­ 35 ­

361 00198 lvar 0x00000000 # .; ^; %p: Tword;

362 0019a ind 0x00000008

363 0019c ptr

364 0019d fld 0x00000008

365 0019f ind 0x00000004

366 001a1 add

367 001a2 lvar 0x00000000 # .; ^; %p: Tword;

368 001a4 ind 0x00000008

369 001a6 ptr

370 001a7 fld 0x00000008

371 001a9 sto 0x00000002

372 # [](.(^(%p: Tword), block: Tblock), .(^(%p: Tword), nc: int)) = $c: char

373 001ab arg 0x00000000 # $c: char;

374 001ad lvar 0x00000000 # []; .; ^; %p: Tword;

375 001af ind 0x00000008

376 001b1 ptr

377 001b2 fld 0x00000008

378 001b4 ind 0x00000004

379 001b6 lvar 0x00000000 # .; ^; %p: Tword;

380 001b8 ind 0x00000008

381 001ba ptr

382 001bb idx 0x00000007

383 001bd stom 0x00000001

384 # return <nil>

385 001bf ret 0x00000006

386 # delword()

387 # {...}

388 # if(!=(&w: Tword, nil))

389 001c1 data 0x00000008 # nil;

390 001c3 0x0

391 001c4 0x0

392 001c5 arg 0x00000000 # &w: Tword;

393 001c7 ind 0x00000008

394 001c9 ind 0x00000008

395 001cb nea

396 001cc jmpf 0x000001e5

397 # {...}

398 # delword(.(^(&w: Tword), next: Tword))

399 001ce arg 0x00000000 # .; ^; &w: Tword;

400 001d0 ind 0x00000008

401 001d2 ind 0x00000008

402 001d4 ptr

403 001d5 fld 0x0000000c

404 001d7 call 0x00000007 # delword();

405 # dispose(&w: Tword)

406 001d9 arg 0x00000000 # &w: Tword;

407 001db ind 0x00000008

408 001dd call 0x80000005 # dispose();

409 # initword(&w: Tword)

410 001df arg 0x00000000 # &w: Tword;

411 001e1 ind 0x00000008

412 001e3 call 0x00000002 # initword();

413 # return <nil>

414 001e5 ret 0x00000007

415 # readword()

416 # {...}

417 # dowhile(and(not(feof(stdin: file)), not(isblank(%c: char))))

418 # {...}

419 # fread(stdin: file, %c: char)

420 001e7 lvar 0x00000000 # %c: char;

Page 36: The Picky programming language - Lsub.org

­ 36 ­

421 001e9 daddr 0x0000005c # stdin: file;

422 001eb ind 0x00000004

423 001ed push 0x00000001

424 001ef call 0x8000000b # fread();

425 # addtoword(&w: Tword, %c: char)

426 001f1 lvar 0x00000000 # %c: char;

427 001f3 ind 0x00000004

428 001f5 arg 0x00000000 # &w: Tword;

429 001f7 ind 0x00000008

430 001f9 call 0x00000006 # addtoword();

431 # fpeek(stdin: file, %c: char)

432 001fb lvar 0x00000000 # %c: char;

433 001fd daddr 0x0000005c # stdin: file;

434 001ff ind 0x00000004

435 00201 call 0x8000000a # fpeek();

436 00203 lvar 0x00000000 # %c: char;

437 00205 ind 0x00000004

438 00207 call 0x00000000 # isblank();

439 00209 not

440 0020a daddr 0x0000005c # stdin: file;

441 0020c ind 0x00000004

442 0020e call 0x80000008 # feof();

443 00210 not

444 00211 and

445 00212 jmpt 0x000001e7

446 # return <nil>

447 00214 ret 0x00000008

448 # wordchar()

449 # {...}

450 # %c: char = ’?’

451 00216 push 0x0000003f # ’?’;

452 00218 lvar 0x00000000 # %c: char;

453 0021a sto 0x00000001

454 # while(and(>($n: int, 0), !=($w: Tword, nil)))

455 0021c data 0x00000008 # nil;

456 0021e 0x0

457 0021f 0x0

458 00220 arg 0x00000004 # $w: Tword;

459 00222 ind 0x00000008

460 00224 nea

461 00225 push 0x00000000 # 0;

462 00227 arg 0x00000000 # $n: int;

463 00229 ind 0x00000004

464 0022b gt

465 0022c and

466 0022d jmpf 0x00000267

467 # {...}

468 # if(<=($n: int, Blocknc=2))

469 0022f push 0x00000002 # Blocknc=2;

470 00231 arg 0x00000000 # $n: int;

471 00233 ind 0x00000004

472 00235 le

473 00236 jmpf 0x0000024f

474 # {...}

475 # %c: char = [](.(^($w: Tword), block: Tblock), $n: int)

476 00238 arg 0x00000000 # []; $n: int;

477 0023a ind 0x00000004

478 0023c arg 0x00000004 # .; ^; $w: Tword;

479 0023e ind 0x00000008

480 00240 ptr

Page 37: The Picky programming language - Lsub.org

­ 37 ­

481 00241 idx 0x00000007

482 00243 lvar 0x00000000 # %c: char;

483 00245 stom 0x00000001

484 # $n: int = 0

485 00247 push 0x00000000 # 0;

486 00249 arg 0x00000000 # $n: int;

487 0024b sto 0x00000002

488 0024d jmp 0x00000265

489 # else

490 # $n: int = −($n: int, Blocknc=2)

491 0024f push 0x00000002 # Blocknc=2;

492 00251 arg 0x00000000 # $n: int;

493 00253 ind 0x00000004

494 00255 sub

495 00256 arg 0x00000000 # $n: int;

496 00258 sto 0x00000002

497 # $w: Tword = .(^($w: Tword), next: Tword)

498 0025a arg 0x00000004 # .; ^; $w: Tword;

499 0025c ind 0x00000008

500 0025e ptr

501 0025f fld 0x0000000c

502 00261 arg 0x00000004 # $w: Tword;

503 00263 stom 0x00000008

504 00265 jmp 0x0000021c

505 # return %c: char

506 00267 lvar 0x00000000 # %c: char;

507 00269 ind 0x00000004

508 0026b ret 0x00000009

509 # cpword()

510 # {...}

511 # delword(&dw: Tword)

512 0026d arg 0x00000008 # &dw: Tword;

513 0026f ind 0x00000008

514 00271 call 0x00000007 # delword();

515 # {...}

516 # %i: int = 1

517 00273 push 0x00000001 # 1;

518 00275 lvar 0x00000000 # %i: int;

519 00277 sto 0x00000002

520 # while(<=(%i: int, wordnc($sw: Tword)))

521 00279 arg 0x00000000 # $sw: Tword;

522 0027b ind 0x00000008

523 0027d call 0x00000003 # wordnc();

524 0027f lvar 0x00000000 # %i: int;

525 00281 ind 0x00000004

526 00283 le

527 00284 jmpf 0x000002a2

528 # {...}

529 # addtoword(&dw: Tword, wordchar($sw: Tword, %i: int))

530 00286 lvar 0x00000000 # %i: int;

531 00288 ind 0x00000004

532 0028a arg 0x00000000 # $sw: Tword;

533 0028c ind 0x00000008

534 0028e call 0x00000009 # wordchar();

535 00290 arg 0x00000008 # &dw: Tword;

536 00292 ind 0x00000008

537 00294 call 0x00000006 # addtoword();

538 # %i: int = succ(%i: int)

539 00296 lvar 0x00000000 # %i: int;

540 00298 ind 0x00000004

Page 38: The Picky programming language - Lsub.org

­ 38 ­

541 0029a call 0x80000019 # succ();

542 0029c lvar 0x00000000 # %i: int;

543 0029e sto 0x00000002

544 002a0 jmp 0x00000279

545 # return <nil>

546 002a2 ret 0x0000000a

547 # main()

548 # {...}

549 # initword(%max: Tword)

550 002a4 lvar 0x0000000c # %max: Tword;

551 002a6 call 0x00000002 # initword();

552 # dowhile(not(feof(stdin: file)))

553 # {...}

554 # skipblanks(%done: bool)

555 002a8 lvar 0x00000000 # %done: bool;

556 002aa call 0x00000001 # skipblanks();

557 # if(not(%done: bool))

558 002ac lvar 0x00000000 # %done: bool;

559 002ae ind 0x00000004

560 002b0 not

561 002b1 jmpf 0x000002d6

562 # {...}

563 # initword(%w: Tword)

564 002b3 lvar 0x00000004 # %w: Tword;

565 002b5 call 0x00000002 # initword();

566 # readword(%w: Tword)

567 002b7 lvar 0x00000004 # %w: Tword;

568 002b9 call 0x00000008 # readword();

569 # if(>(wordnc(%w: Tword), wordnc(%max: Tword)))

570 002bb lvar 0x0000000c # %max: Tword;

571 002bd ind 0x00000008

572 002bf call 0x00000003 # wordnc();

573 002c1 lvar 0x00000004 # %w: Tword;

574 002c3 ind 0x00000008

575 002c5 call 0x00000003 # wordnc();

576 002c7 gt

577 002c8 jmpf 0x000002d2

578 # {...}

579 # cpword(%max: Tword, %w: Tword)

580 002ca lvar 0x00000004 # %w: Tword;

581 002cc ind 0x00000008

582 002ce lvar 0x0000000c # %max: Tword;

583 002d0 call 0x0000000a # cpword();

584 # delword(%w: Tword)

585 002d2 lvar 0x00000004 # %w: Tword;

586 002d4 call 0x00000007 # delword();

587 002d6 daddr 0x0000005c # stdin: file;

588 002d8 ind 0x00000004

589 002da call 0x80000008 # feof();

590 002dc not

591 002dd jmpt 0x000002a8

592 # writeword(%max: Tword)

593 002df lvar 0x0000000c # %max: Tword;

594 002e1 ind 0x00000008

595 002e3 call 0x00000004 # writeword();

596 # fwrite(stdout: file, $s2=" with len ")

597 002e5 daddr 0x00000034 # $s2=" with len ";

598 002e7 ind 0x00000028

599 002e9 daddr 0x00000060 # stdout: file;

600 002eb ind 0x00000004

Page 39: The Picky programming language - Lsub.org

­ 39 ­

601 002ed push 0x0000000b

602 002ef call 0x8000000e # fwrite();

603 # fwriteln(stdout: file, wordnc(%max: Tword))

604 002f1 lvar 0x0000000c # %max: Tword;

605 002f3 ind 0x00000008

606 002f5 call 0x00000003 # wordnc();

607 002f7 daddr 0x00000060 # stdout: file;

608 002f9 ind 0x00000004

609 002fb push 0x00000002

610 002fd call 0x8000000f # fwriteln();

611 # delword(%max: Tword)

612 002ff lvar 0x0000000c # %max: Tword;

613 00301 call 0x00000007 # delword();

614 # return <nil>

615 00303 ret 0x0000000b

616 pcs 75

617 00000 dwords.p 21

618 00019 dwords.p 28

619 00021 dwords.p 29

620 00032 dwords.p 30

621 0003e dwords.p 31

622 00047 dwords.p 32

623 0005d dwords.p 35

624 00069 dwords.p 159

625 0006b dwords.p 40

626 00075 dwords.p 159

627 00077 dwords.p 46

628 0007d dwords.p 47

629 00088 dwords.p 48

630 0009a dwords.p 49

631 000a7 dwords.p 51

632 000ad dwords.p 57

633 000b9 dwords.p 58

634 000c4 dwords.p 60

635 000c4 dwords.p 61

636 000da dwords.p 60

637 000ef dwords.p 61

638 000fb dwords.p 62

639 00108 dwords.p 64

640 00114 dwords.p 159

641 00116 dwords.p 69

642 0011e dwords.p 70

643 0012b dwords.p 71

644 0013a dwords.p 159

645 0013c dwords.p 77

646 00149 dwords.p 78

647 0014f dwords.p 80

648 00157 dwords.p 81

649 00167 dwords.p 82

650 00174 dwords.p 84

651 00182 dwords.p 85

652 0018b dwords.p 86

653 00196 dwords.p 88

654 001ab dwords.p 89

655 001bf dwords.p 159

656 001c1 dwords.p 94

657 001ce dwords.p 95

658 001d9 dwords.p 96

659 001df dwords.p 97

660 001e5 dwords.p 159

Page 40: The Picky programming language - Lsub.org

­ 40 ­

661 001e7 dwords.p 105

662 001f1 dwords.p 106

663 001fb dwords.p 107

664 00214 dwords.p 159

665 00216 dwords.p 115

666 0021c dwords.p 116

667 0022f dwords.p 117

668 00238 dwords.p 118

669 00247 dwords.p 119

670 0024f dwords.p 121

671 0025a dwords.p 122

672 00267 dwords.p 125

673 0026d dwords.p 131

674 00273 dwords.p 133

675 00273 dwords.p 134

676 00286 dwords.p 133

677 00296 dwords.p 134

678 002a2 dwords.p 159

679 002a4 dwords.p 142

680 002a8 dwords.p 144

681 002ac dwords.p 145

682 002b3 dwords.p 146

683 002b7 dwords.p 147

684 002bb dwords.p 148

685 002ca dwords.p 149

686 002d2 dwords.p 151

687 002df dwords.p 154

688 002e5 dwords.p 155

689 002f1 dwords.p 156

690 002ff dwords.p 157

691 00303 dwords.p 159