• No se han encontrado resultados

SECRETARIA DE SALUD

INDICE 0 Introducción

Operations are the main computational mechanism in Core Concat. Operation definitions consist of a number of cases that define a mapping between arguments and a result in a functional way. Each case is defined by an unconditional or conditional transformation. Executing the operation corresponds to trying the cases in order on the input until one succeeds and produces the result of the operation.

Operation definitions start with the keyword opdef followed by the operator symbol, a colon and a sequence of transformations terminated with a full stop. In case there is only a single transformation with an empty left-hand side, the colon in the operation definition may be omitted. Listing 5 shows examples of simple operations on sequences.

Listing 5 Basic Sequence Operations 1 opdef first: 2 [$x @y] ) $x. 3 [] ) ’error. 4 5 opdef rest: 6 [$x @y] ) [@y]. 7 [] ) ’error. 8

9 opdef second ) rest first.

The operation first returns the first item in a sequence and the operation rest returns the sequence with the first item removed. Both return the symbol ’error if the argument sequence is empty.

Operations are purely functional in the sense that their results depend solely on argu- ment values that need to be items. However, the application of operations in Core Concat is not based on function calls with a fixed number of individual arguments, but on a single argument that is a sequence. Programs are executed strictly from left to right with each operation working on the results of previous operations. Data that is unaffected by an operation is passed on to the next operation together with the result. Syntactically, this means that programs are written in postfix notation. Macros are built-in means to break out of this syntactic and semantic scheme. Operators may occur on the right-hand side of transformations within operation definitions. This corresponds to “calling” other op- erations. The definition in line 9 of Listing 5 abstracts the program rest first by introducing the operator second. The following lines show the stepwise execution of a program where rest and first are applied to a sequence:

[1 2 3] second [1 2 3] rest first [2 3] first

2

Internally, operations are rewritten into rules of a pattern-based concatenative rewriting system as defined in Section 4.2.4. An operation definition of the form

opdef name:

is transformed into a sequence of choices. Each choice is a transformation: lhs1 name ) rhs1 | ... | lhsN name ) rhsN

The left-hand side of the transformations have the operator name inserted as the last el- ement on the left-hand side. In effect, the operation definition ensures that each rule has an operator name that serves as a dispatch. By checking that the left-hand side does not contain operators during parsing, the concatenative programming style can be enforced. Listing 20 in Section 5.5.2 defines the transformation of an operator definition into a choice pattern.

Listing 6 shows the implementation of map, an operation that applies a program to each element in a sequence and produces a sequence that contains the results.

Listing 6 Mapping over Sequences in Concat

1 opdef map:

2 do ([ $f @r ] [ @p ] ) [ $f1 @r1 ])

3 if $f @p ) $f1

4 && [ @r ] [ @p ] map ) [ @r1 ]. 5 [ ] [ @p ] map ) [ ].

The stepwise execution of the map operation will be discussed by example based on the program [[1][2][3]] [first] map. This program has the result [1 2 3]. The first transformation starting in line 2 implements the recursive case of the operation. It has two conditionals that are defined in line 3 and 4. The application of the operation to the argument [[1][2][3]] [first] starts by attempting to match the data with the left- hand side of the operation, which is the pattern [$f @r] [@p]. The result of this match are the following variable bindings that are added to the store: (f,[1]), (r,h[2][3]i) and (p, hfirsti). In the context of these bindings, the left-hand side of the first condi- tional $f @p is instantiated. The result is the program [1] first. This program is executed and yields the result 1 which is matched by pattern $f1. The resulting bind- ing (f1,1) is added to the store. With the updated store, the left-hand side of the second conditional [@r][@p] map is instantiated. The result is [[2][3]] [first] map. The execution of this resulting program follows the execution pattern just described and leads to more recursive calls “waiting” for the result, each with its own instance of a store. The two final recursive calls of the execution are [[3]] [first] map and [] [first] map. During the execution of the latter, the first transformation fails to match and, therefore, the second transformation – the base case – is tried. The base case yields the result [] which, when matched with [@r1], yields a binding (r1, ✏). After

this, the instantiation of the pattern [$f1 @r1] results in [3]. This process of returning the result and instantiating the right-hand side of the transformation header is repeated for all waiting “calls” which leads to the stepwise construction of the sequence [1 2 3].

The operation map is higher-order in the sense that it takes a program as an argument and executes it. The execution is expressed by the left-hand side of the conditional in line 3. The variable p is bound to the content to of the second sequence which, for this example, is the program first.

The operations examined so far operate on untyped sequences and use generic types for variables. Listing 7 shows an example of how patterns can be used to ensure correct types are passed to the operation.

Listing 7 Example for using Typed Sequences in Operations

1 opdef valid_char:

2 do ([:string c:char r:char* ] ) $c [:string @r]) 3 if $c valid? ) true.

The operation valid char removes the first character from a string if the operation valid?yields true for this character.