Skip to content

Language Specification (10) : Syntax Binding

Benjamin Kowarsch edited this page Jun 9, 2023 · 17 revisions

Syntax Binding

Syntax Binding is the process of mapping library defined procedures to built-in syntax. A library defined procedure is bound to a given built-in syntax form by including a binding specifier within the procedure’s header. The binding specifier determines to which syntax form the procedure is bound, and it must be included in both the procedure’s definition and declaration.

procedureHeader :=
  PROCEDURE ( '[' bindingSpecifier ']')? procedureSignature ;

bindingSpecifier :=
  NEW ( ARGLIST | CAPACITY )? | RETAIN | RELEASE |
  READ NEW? | WRITE ( '#' )? | bindableIdent ;

bindableIdent := Pervasive | Primitive ;

A syntax form to which library defined procedures may be bound is said to be bindable. Procedures bound to it are called its bindings. While bindable syntax forms are type agnostic, their bindings are type specific, that is to say, each binding supports arguments of a specific type. For each type to be supported by a bindable syntax form, a binding specific to arguments of the type is required.

A bindable syntax form S is said to have a binding for type T when a library procedure p that supports arguments of T has been bound to S. Procedure p is said to be bound to S in respect of T.

Syntax Transformation

Bindable syntax forms act as Wirthian macros. A syntax form S with binding p for type T, an occurrence of S with a primary argument or argument list a of type T is resolved into a procedure call to p, passing a.

S a ...T.p(a...)

Memory Management Bindings

Binding to NEW

Statement NEW has three bindable syntax forms.

NEW without Arguments

An ADT module Foo with a binding

PROCEDURE [NEW] New ( VAR p : Foo );

binds ADT library procedure Foo.New() to the NEW statement in respect of type Foo.

Any use of the NEW statement with a designator of type Foo and without initialiser nor capacity specifier is then replaced by a call to procedure Foo.New().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

a statement of the form

NEW foo;

is resolved into a library call

Foo.New(foo);

NEW with Initialiser

An ADT module Foo with the binding

PROCEDURE [NEW ARGLIST] NewWithArgs ( VAR p : Foo; initVal : ARGLIST OF Value );

bind ADT library procedure Foo.NewWithArgs() to the NEW statement in respect of type Foo.

Any use of the NEW statement with a designator of type Foo and an initialiser is then resolved into a call to procedure Foo.NewWithArgs().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

a statement of the form

NEW foo := { a, b, c };

is resolved into a library call

Foo.NewWithArgs(foo, a, b, c);

NEW with Capacity

An ADT module Foo with a binding

PROCEDURE [NEW CAPACITY] NewWithCapacity ( VAR p : Foo; capacity : LONGCARD );

binds ADT library procedure Foo.NewWithCapacity() to the NEW statements in respect of type Foo.

Any use of the NEW statement with a designator of type Foo and a capacity specifier is then resolved into a call to procedure Foo.NewWithCapacity().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

a statement of the form

NEW foo CAPACITY n;

is resolved into a library call

Foo.NewWithCapacity(foo, n);

Binding to RETAIN

An ADT module Foo with a binding

PROCEDURE [RETAIN] Retain ( p : Foo );

binds ADT library procedure Retain() to the RETAIN statement in respect of type Foo.

Any use of RETAIN with a designator of type Foo is then resolved into a call to procedure Foo.Retain().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

a statement of the form

RETAIN foo;

is resolved into a library cal

Foo.Retain(foo);

Binding to RELEASE

An ADT module Foo with a binding

PROCEDURE [RELEASE] Release ( VAR p : Foo );

binds ADT library procedure Release() to the RELEASE statement in respect of type Foo.

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

a statement of the form

RELEASE foo;

is resolved into a library cal

Foo.Release(foo);

I/O Bindings

Binding to READ

Statement READ has two associated bindings.

  • [READ] bindings are used to generate calls for plain designators in a READ statement
  • [READ NEW] bindings are used to generate calls for NEW-prefixed designators in a READ statement

An IO module FooIO with a binding

PROCEDURE [READ] Read ( chan : IOChannel; VAR value : Foo );

binds IO library procedure Read() to the READ statement in respect of type Foo.

Any plain designator of type Foo within a READ statement will then generate a call to procedure FooIO.Read().

An IO module FooIO with a binding

PROCEDURE [READ NEW] ReadNew ( chan : IOChannel; VAR ptr : Foo ); (* Foo is a pointer type *)

binds IO library procedure ReadNew() to the READ statement in respect of pointer type Foo.

Any NEW-prefixed designator of pointer type Foo within a READ statement will then generate a call to procedure FooIO.ReadNew().

READ with Explicit IO Channel

Given the following imports and declaration within a client module

IMPORT Foo, FooIO, IOChannel; VAR foo : Foo; file : IOChannel;
  • a statement of the form READ @file : foo is resolved into a library call FooIO.Read(file, foo)
  • a statement of the form READ @file : NEW foo is resolved into a library call FooIO.ReadNew(file, foo)

READ with Default IO Channel

If the IO channel argument is omitted, default input channel primitive STDIN is automatically inserted. In order to resolve STDIN an appropriate IO library must be imported to bind an input channel to STDIN and make it visible within the current module scope. Import of module StdIO binds StdIO.in() to STDIN.

Given the following imports and declaration within a client module

IMPORT Foo, FooIO, StdIO; VAR foo : Foo;
  • a statement of the form READ foo is resolved into a library call FooIO.Read(StdIO.in(), foo)
  • a statement of the form READ NEW foo is resolved into a library call FooIO.ReadNew(StdIO.in(), foo)

READ with Multiple Arguments

Given modules Foo, Bar, Baz and Bam, their respective IO modules FooIO, BarIO, BazIO and BamIO with bindings to for their respective types and the following imports and declaration within a client module

IMPORT Foo, FooIO, Bar, BarIO, Baz, BazIO, Bam, BamIO, StdIO;
VAR foo : Foo; bar : Bar; baz : Baz; bam : Bam;

a statement of the form

READ foo, NEW bar, baz, NEW bam;

is resolved into a statement sequence

FooIO.Read(StdIO.in(), foo); BarIO.ReadNew(StdIO.in(), bar);
BazIO.Read(StdIO.in(), baz); BamIO.ReadNew(StdIO.in(), bam);

Binding to WRITE

Statement WRITE has two associated bindings.

  • [WRITE] bindings are used to generate calls for unformatted output values in a WRITE statement
  • [WRITE #] bindings are used to generate calls for formatted output values in a WRITE statement

An IO module FooIO with a binding

PROCEDURE [WRITE] Write ( chan : IOChannel; value : Foo );

binds IO library procedure Write() to the WRITE statement in respect of type Foo.

Any unformatted output value of type Foo within a WRITE statement will then generate a call to procedure FooIO.Write().

An IO module FooIO with a binding

PROCEDURE [WRITE #] WriteF ( chan : IOChannel; CONST fmtStr : ARRAY OF CHAR; value : Foo );

binds IO library procedure WriteF() to the WRITE statement in respect of type Foo.

Any formatted output value of type Foo within a WRITE statement will then generate a call to procedure FooIO.WriteF().

WRITE with Explicit IO Channel

Given the following imports and declaration within a client module

IMPORT Foo, FooIO, IOChannel; VAR foo : Foo; file : IOChannel;
  • a statement of the form WRITE @file : foo is resolved into a library cal FooIO.Write(file, foo)
  • a statement of the form WRITE @file : #(fmt, foo) is resolved into a library call FooIO.WriteF(file, fmt, foo)

WRITE with Default IO Channel

If the IO channel argument is omitted, default output channel primitive STDOUT is automatically inserted. In order to resolve STDOUT an appropriate IO library must be imported to bind an output channel to STDOUT and make it visible within the current module scope. Import of module StdIO binds StdIO.out() to STDOUT.

Given the following imports and declaration within a client module

  • a statement of the form WRITE foo is resolved into a library call FooIO.Write(StdIO.out(), foo)
  • a statement of the form WRITE #(fmt, foo) is resolved into a call FooIO.WriteF(StdIO.out(), fmt, foo)

WRITE with Multiple Arguments

Given modules Foo, Bar, Baz and Bam, their respective IO modules FooIO, BarIO, BazIO and BamIO with bindings to for their respective types and the following imports and declaration within a client module

IMPORT Foo, FooIO, Bar, BarIO, Baz, BazIO, Bam, BamIO, StdIO;
VAR foo : Foo; bar : Bar; baz : Baz; bam : Bam;

a statement of the form

WRITE foo, #(fmt1, bar), baz, #(fmt2, bam);

is resolved into a statement sequence

FooIO.Write(StdIO.out(), foo); BarIO.WriteF(StdIO.out(), fmt1, bar);
BazIO.Write(StdIO.out(), baz); BarIO.WriteF(StdIO.out(), fmt2, bam);

Binding to Pervasives

Binding to Procedure APPEND

An ADT module Foo with a binding

PROCEDURE [APPEND] Append ( VAR a : Foo; values : ARGLIST OF Value );

binds ADT library procedure Foo.Append() to pervasive procedure APPEND() in respect of type Foo.

Any call to APPEND() with a first argument of type Foo is then resolved into a call to procedure Foo.Append().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

an invocation of the form APPEND(foo, v1, v2, v3) is resolved into a library call Foo.Append(foo, v1, v2, v3).

Binding to Procedure REMOVE

An ADT module Foo with a binding

PROCEDURE [REMOVE] Remove ( VAR a : Foo; keys : ARGLIST OF Keys );

binds ADT library procedure Foo.Remove() to pervasive procedure REMOVE() in respect of type Foo.

Any call to REMOVE() with a first argument of type Foo is then resolved into a call to procedure Foo.Remove().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

an invocation of the form REMOVE(foo, k1, k2, k3) is resolved into a library call Foo.Append(foo, k1, k2, k3).

Binding to Function COUNT

An ADT module Foo with a binding

PROCEDURE [COUNT] count ( VAR a : Foo ) : LONGCARD;

binds ADT library function Foo.count() to pervasive function COUNT() in respect of type Foo.

Any invocation of COUNT() with an argument of type Foo is then resolved into a call to function Foo.count().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

an invocation of the form COUNT(foo) is resolved into a library call Foo.count(foo).

Within any given ADT module, bindings to COUNT and LENGTH are mutually exclusive.

Binding to Function LENGTH

An ADT module Foo with a binding

PROCEDURE [LENGTH] length ( VAR a : Foo ) : LONGCARD;

binds ADT library function Foo.length() to pervasive function LENGTH() in respect of type Foo.

Any invocation of LENGTH() with an argument of type Foo is then resolved into a call to function Foo.length().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

an invocation of the form LENGTH(foo) is resolved into a library call Foo.length(foo).

Within any given ADT module, bindings to COUNT and LENGTH are mutually exclusive.

Binding to Function FIRST

An ADT module Foo with a binding

PROCEDURE [FIRST] firstKey ( a : Foo ) : KeyType (* of Foo *);

binds ADT library function Foo.firstKey() to pervasive function FIRST() in respect of type Foo.

Any invocation of FIRST() with an argument of type Foo is then resolved into a call to function Foo.firstKey().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

an invocation of the form FIRST(foo) is resolved into a library call Foo.firstKey(foo).

Binding to Function LAST

An ADT module Foo with a binding

PROCEDURE [LAST] lastKey ( a : Foo ) : KeyType (* of Foo *);

binds ADT library function Foo.lastKey() to pervasive function LAST() in respect of type Foo.

Any invocation of LAST() with an argument of type Foo is then resolved into a call to function Foo.lastKey().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo;

an invocation of the form LAST(foo) is resolved into a library call Foo.lastKey(foo).

Binding to Function PREV

An ADT module Foo with a binding

PROCEDURE [PREV] prevKey ( VAR a : Foo; key : Key ) : Key;

binds ADT library function Foo.prevKey() to pervasive function PREV() in respect of type Foo.

Any invocation of PREV() with an argument of type Foo is then resolved into a call to function Foo.prevKey().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo; key : Foo.Key;

an invocation of the form PREV(foo, key) is resolved into a library call Foo.prevKey(foo, key).

Binding to Function NEXT

An ADT module Foo with a binding

PROCEDURE [NEXT] nextKey ( VAR a : Foo; key : Key ) : Key;

binds ADT library function Foo.nextKey() to pervasive function NEXT() in respect of type Foo.

Any invocation of NEXT() with an argument of type Foo is then resolved into a call to function Foo.nextKey().

Given the following import and declaration within a client module

IMPORT Foo; VAR foo : Foo; key : Foo.Key;

an invocation of the form NEXT(foo, key) is resolved into a library call Foo.nextKey(foo, key).

Binding to Macro TLIMIT

An ADT module Foo with a binding

CONST [TLIMIT] Capacity = 1000;

binds library constant Foo.Capacity to pervasive macro TLIMIT() in respect of type Foo.

Given an import directive IMPORT Foo; within a client module,

an expression of the form TLIMIT(Foo) is replaced by library constant Foo.Capacity.

Binding to the Collation Table

A library module may bind an array constant to the language processor’s internal collation table.

CONST [COLLATION] Order = { index0, index1, index2, ... index94 };

where value Order[i] defines the collation index of character CHR(i+32) for i in range [0..94].

Such a library is called a collation library. Import of a collation library causes pervasive function COLLATION() and thereby all string comparisons to use the library defined order within the scope of the importing module.

Binding to Primitives

Binding to Primitive VALUE

An ADT module Foo with a binding

PROCEDURE [VALUE] valueForKey ( VAR a : Foo; key : Key ) : Value;

binds function Foo.valueForKey() to primitive VALUE in respect of type Foo.

Any (internal) use of primitive VALUE with a first argument of type Foo is then resolved into a library call to function Foo.valueForKey() during translation of syntax forms that are synthesised using primitive VALUE.

Binding to Primitive ATVALUE

An ADT module Foo with a binding

PROCEDURE [ATVALUE] valueAtIndex ( VAR a : Foo; atIndex : LONGCARD ) : Value;

binds function Foo.valueAtIndex() to primitive ATVALUE in respect of type Foo.

Any (internal) use of primitive ATVALUE with a first argument of type Foo is then resolved into a library call to function Foo.valueAtIndex() during translation of syntax forms that are synthesised using primitive ATVALUE.

Binding to Primitive STORE

An ADT module Foo with a binding

PROCEDURE [STORE] StoreKeyValue ( VAR a : Foo; key : Key; value : Value );

binds procedure Foo.StoreKeyValue to primitive STORE in respect of type Foo.

Any (internal) use of primitive STORE with a first argument of type Foo is then resolved into a library call to procedure Foo.StoreKeyValue during translation of syntax forms that are synthesised using primitive STORE.

Binding to Primitive ATSTORE

An ADT module Foo with a binding

PROCEDURE [ATSTORE] StoreAtIndex ( VAR a : Foo; atIndex : LONGCARD; value : Value );

binds procedure Foo.StoreAtIndex to primitive ATSTORE in respect of type Foo.

Any (internal) use of primitive ATSTORE with a first argument of type Foo is then resolved into a library call to procedure Foo.StoreAtIndex during translation of syntax forms that are synthesised using primitive ATSTORE.

Binding to Primitive ATINSERT

An ADT module Foo with a binding

PROCEDURE [ATINSERT] AtInsert ( VAR a : Foo; atIndex : LONGCARD; values : ARGLIST OF Value );

binds procedure Foo.AtInsert to primitive ATINSERT in respect of type Foo.

Any (internal) use of primitive ATINSERT with a first argument of type Foo is then resolved into a library call to procedure Foo.AtInsert during translation of syntax forms that are synthesised using primitive ATINSERT.

Binding to Primitive ATREMOVE

An ADT module Foo with a binding

PROCEDURE [ATREMOVE] AtRemove ( VAR a : Foo; startIndex, endIndex : LONGCARD );

binds procedure Foo.AtRemove to primitive ATREMOVE in respect of type Foo.

Any (internal) use of primitive ATREMOVE with a first argument of type Foo is then resolved into a library call to procedure Foo.AtRemove during translation of syntax forms that are synthesised using primitive ATREMOVE.

Binding to Primitive ALLOC

A storage library module MyStorage with a binding

PROCEDURE [ALLOC] Allocate ( VAR p : CAST ADDRESS; size : LONGCARD );

binds procedure MyStorage.Allocate to primitive ALLOC.

Any (internal) use of primitive ALLOC is then resolved into a library call to procedure MyStorage.Allocate during translation of NEW statements for types that do not provide type specific bindings to NEW.

Binding to Primitive DEALLOC

A storage library module MyStorage with a binding

PROCEDURE [DEALLOC] Deallocate ( VAR p : CAST ADDRESS );

binds procedure MyStorage.Deallocate to primitive DEALLOC.

Any (internal) use of primitive DEALLOC is then resolved into a library call to procedure MyStorage.Deallocate during translation of RELEASE statements for types that do not provide type specific bindings to RELEASE.

Binding to Primitive STDIN

An IO module MyIO with a binding

PROCEDURE [STDIN] stdIn : ArbitraryIOChannelType;

binds MyIO.stdIn() to primitive STDIN. (Internal) use of STDIN is then resolved to MyIO.stdIn().

Binding to Primitive STDOUT

An IO module MyIO with a binding

PROCEDURE [STDOUT] stdOut : ArbitraryIOChannelType;

binds MyIO.stdOut() to primitive STDOUT. (Internal) use of STDOUT is then resolved to MyIO.stdOut().

Clone this wiki locally