Iterace se separátory

Velmi obvyklou konstrukcí vyskytující se ve většině programovacích jazyků je posloupnost položek oddělených separátory. Lze ji zpracovat způsobem, který je poněkud obecnější formou iterované aplikace parseru, pro niž jsme v minulé části vybudovali rozsáhlou sadu mutátorů.

Jejím příkladem mohou být parametry predikátu či položky prologovského seznamu oddělené čárkou nebo posloupnost příkazů v níž je jako oddělovač použit středník.

Jedním z typických případů použití knihovny konstruktorů parserů je úloha analýzy dat s cílem jejich převodu do formátu vhodného pro další zpracování. Vstupní data mohou mít buď formu čitelnější pro člověka nebo mohou být třeba výstupem nějakého programu. V takových situacích je nutné převést je z externí reprezentace do interního formátu či vhodné (v našem případě prologovské) datové struktury. S pomocí knihovních konstruktorů je možné snadno a rychle vytvořit vhodný parser.

Jako příklad si ukažme uživatelskou reprezentaci determinantu matice, v níž se objevuje syntaktická konstrukce zmíněná v počátku této části. Řádky determinantu jsou ohraničeny svislítky a jednotlivé položky v nich jsou odděleny čárkami. Dále se připouští výskyt prázdných znaků mezi jednotlivými lexikálními elementy.

| 1, -5, 7e-3 |
| 13, 6, 0 |
| -9, 3, 1.5 |
Podobných příkladů, jako je tento motivační, bychom nalezli jistě mnoho. Naším cílem tedy bude vytvořit v závěru této části, s pomocí nových kombinátorů, parser přijímající řetězec s takovouto reprezentací a vydávající jeho obsah ve vhodné prologovské struktuře.

Pro analyzování takovýchto konstrukcí nám poslouží kombinátor separatedBy vytvářející pro daný parser položek $P$ a separátorů $S$ parser pro posloupnost těchto elementů:

separatedBy(P,S,W):-
 W :->
        (P <&> ( S &> P )<<*>>).
Separátory jsou v průběhu rozkladu odstraňovány a jednotlivé členy posloupnosti uloženy v odpovídajícím pořadí do seznamu. Nejobvyklejšími speciálními případy tohoto kombinátoru jsou:
commaListOf(P,W):-
 W :->  P separatedBy symbol(",").

semicolonListOf(P,W):-
 W :->  P separatedBy symbol(";").
V případech, kterým je například prologovský seznam, je nutné, aby kombinátor ošetřoval situaci prázdného seznamu -- zavedeme tedy ještě varianty kombinátorů pro tento případ. Podobně jako u mutátorů pro iteraci k tomu použijeme již vytvořené definice:
separated0By(P,S,W):-
 W :->
        (P separatedBy S
          <:>
         epsilon).
Definice zbylých dvou speciálních případů tohoto kombinátoru je zřejmá. A teď již slíbený parser determinantu matice:
determinant(W):-
 W :->
        (   commaListOf #>double<#
           separatedBy
            (symbol("|") <&> #>symbol("|"))
          enclosedIn
           symbol("|") and symbol("|")
        ).
Determinant matice je tedy posloupnost posloupností desetinných čísel oddělených čárkami, která je oddělena dvěma svislítky mezi nimiž mohou být prázdné znaky. Celý determinant je uzavřen opět do svislítek.

Velice snadno jsme tedy pro náš příklad vytvořili parser:

?- s("| 1, -5, 0.007 |
|     | 13, 6,    0  |
|     | -9, 3,  1.5  |")+L :-> determinant.

L= [s([])>[[1, -5, 0.007], [13, 6, 0], [-9, 3, 1.5]]]
Yes

Náročnější situací je případ, kdy separátory nesou nějakou informaci -- nejsou tedy pouhými oddělovači, ale mají význam i z hlediska sémantického. Zpracování takových vstupů se budeme věnovat v první části kapitoly [*].

dvorka 2013-12-31