• No results found

Concrete Syntax for SKI Terms

5.3 Case Study: Implementing Combinatory Logic

5.3.3 Concrete Syntax for SKI Terms

The implementation discussed in the previous section can be used for experimenting with reductions of the SKI calculus. By introducing new rules, the calculus can be extended with new combinators. However, the verbosity of the representation of SKI terms makes this inconvenient. Unlike in the mathematical notation, all parentheses need to be written, the symbols have to start with an apostrophe and the terms must be surrounded by a typed sequence. In the following, the introduction of a concrete syntax based on the mathemati- cal notation will be discussed. The syntax is implemented as a view on the typed sequence representation. Transformations need to be defined that map bidirectionally between the mathematical notation and the internal representation used so far.

As discussed in Section 5.3.1, the mathematical notation of SKI terms can be writ- ten with or without parentheses. If there are no parentheses the concatenation of terms is left associative. Parsers for left associative operators are typically implemented using left recursive grammar rules [133]. However, as the execution semantics of Core Concat imply that patterns are matched strictly from left to right, left recursion leads to infinite

regress. This is a well known problem for top down parsers [3]. In Core Concat, this prob- lem is solved by providing a mechanism to avoid left-recursion altogether: dynamically parameterised productions.

The following code defines the internalisation part of the view definition for SKI terms. It can be understood as a grammar that defines how a term is parsed into an in- ternal representation.

Listing 13 Grammar for Internalising SKI Terms

1 ski_start ) ˆ‘.

2 ski_char ) ˆS | ˆK | ˆI | ˆa | ˆb | ˆc. 3 ski_sym ) (c:<ski_char> ) [:symbol $c]). 4 ski_group ) (ˆ( t:<ski_term> ˆ) ) $t). 5 ski_item ) <ski_sym> | <ski_group>.

6 ski_term ) (&i(s:<ski_item>) <$s ski_rest>)

7 | <nothing>.

8 ski_literal ) (<ski_start> t:<ski_term> ) [:ski $t]). 9 $v ski_rest ) ((&i(s:<ski_item>) <[,$v $s] ski_rest>)

10 | (<nothing> ) ,$v)).

The only difference between the original mathematical notation defined in Section 5.3.1 and the one introduced in Concat is that every SKI term starts with an apostrophe. This is defined by ski_literal and ski_start. Production ski_term defines that an SKI term consists of an ski_item and an ski_rest or is empty. An ski_item is an ski_symbol or an ski_group. SKI symbols (ski_sym) are represented by characters S, K, I, a, b and c. An ski_group is an ski_term in parentheses. The parameterised production ski_rest expects the current result of the matching process. It attempts to match an ski_item and, if successful, calls itself recursively with a se- quence that consists of its previous argument and the result of recognising ski_item – both combined into a sequence. If there is no ski_item to match, i.e., the term is completely recognised, the argument is returned.

Figure 5.2 depicts the parse tree for the term ‘S(KS)K. To improve the presenta- tion, the ski_ prefix is omitted from all tree nodes. The parse result is the structure [:ski [[’S [’K ’S]] ’K]]. The parsing process corresponds to a left-to-right, depth first traversal of this tree. Without parameterised productions, the data flow for con- structing the result of a parse is strictly from the leafs upwards to the root, with each

literal XXXXX ⇠ ⇠ ⇠ ⇠ ⇠ start ‘ term````` `` item sym S rest XXXXX ⇠ ⇠ ⇠ ⇠ ⇠ item group ``````   ( term HHH item sym K rest ZZ ⇢ ⇢ item sym S rest ✏ ) rest ZZ ⇢ ⇢ item sym K rest ✏

Figure 5.2: Parse Tree for SKI Terms

Because the grammar in Listing 13 avoids left recursion and thereby automatic as- sociation to the left, the results need to be combined differently in order to reflect the left-associativity of concatenating SKI terms. Passing parameters to ski_rest corre- sponds to sending results downwards in the tree. It is helpful to think about the parsing process in terms of function calls. For example, the node term that is under the node groupin Figure 5.2 has two children: item and rest. First, term calls item, item calls symand finally sym recognizes K. The result K is returned to term, which uses it as a pa- rameter when calling rest. This is the point where the result is passed downwards. Next, restcalls item which returns the result S. Results K and S are combined into [K S] and restis called again with this result as an argument. Because there is nothing left to parse, the result [K S] is returned.

With the first part of the view just described, SKI terms in mathematical notation can be parsed into the typed sequence structure on which the reduction and derivation opera- tions are defined. To display results of these operations in the mathematical notation, the reverse operation is required: the internal typed sequence representation has to be trans- formed into concrete syntax. This is defined by the second part of the view that defines externalisation, as shown in Listing 14.

Listing 14 Grammar for Externalising SKI Terms

1 e_ski_sym ) ([:symbol c:char] ) $c).

2 e_ski_group ) (t:<e_ski_term> ) ˆ( $t ˆ)). 3 e_ski_term ) ([l:(<e_ski_sym> | <e_ski_term>) 4 r:(<e_ski_sym> | <e_ski_group>)] ) [$l $r]) 5 | <e_ski_sym> | <nothing>.

6 e_ski_literal ) ([:ski x:<e_ski_term>] ) ˆ‘ @x).

The conversion between internal and external representation can be broken down into three parts: recognising the typed sequence that wraps the content, converting symbols to characters and converting the tree structure expressed by nested sequences into con- catenated terms or groups. Production e_ski_literal generates the start character followed by the result of e_ski_term. Production e_ski_sym unwraps characters by removing the surrounding symbol type. Production e_ski_term distinguishes several cases: the basic distinction is that a term is either a sequence with two elements, a symbol or nothing. In case of a sequence, the left element is either a symbol or again a term. The right element is either a symbol or a group. By being able to recognise a group when the right-hand element is a sequence, the externalisation manages to generate only those parentheses that are actually needed.