PILS programmeringssproget

1  Indledning
        1.1  Vilkår for brug, udvikling og redistribution
        1.2  Hvordan skal dette dokument læses
        1.3  Nogle generelle egenskaber ved PILS
        1.4  Byggestenene i datamodellen
        1.5  Konstanter og udtryk
        1.6  Regler, pasformer og objekter
        1.7  Fejlhåndtering
        1.8  Programmeringssystemet
        1.9  Installation og kørsel på MS Windows

2  Syntaks og datamodel
        2.1  Konstantsyntaks
                2.1.1  Tal
                2.1.2  Tekststrenge
                2.1.3  Tidsstempler, dateringer, varigheder og farver
                2.1.4  Konstantklammer [ ... ]
                2.1.5  Klicheer, noder og lister
                2.1.6  Haler og forben
                2.1.7  Ord og sprog
                2.1.8  Ordklasser og sprognoder
                2.1.9  Sprogindstilling og internationalisering
                2.1.10  Regulære navne
                2.1.11  Apostroffen
                2.1.12  Forkortelser for sprognoder og ordklasse-nøgler
        2.2  Udtryk
                2.2.1  Nodeudtryk
                2.2.2  Navne- og frasekald
                2.2.3  Objektkald
                2.2.4  Kald, fejl og svipsere
                2.2.5  Frasebryder-punktummet
                2.2.6  Operatorpræcedens
        2.3  Regler, pasformer og tildelinger
                2.3.1  Regelsæt
                2.3.2  Pasformer
                2.3.3  Tildelinger
        2.4  Syntaktisk sukker
                2.4.1  Skjulte ben
                2.4.2  Prikkede pasformer
                2.4.3  Halekrøller
                2.4.4  Kommentarer
                2.4.5  Nummererede gengangere

3  Indbyggede operationer
        3.1  Talberegninger
        3.2  Operationer på tidsstempler og varigheder
        3.3  Operationer på tekststrenge
                3.3.1  Konverteringer
                3.3.2  Store og små bogstaver – med İstanbul-ekspressen
                3.3.3  Tekstsplittere
                3.3.4  Fælles operationer for tekststrenge og lister
        3.4  Listeoperationer
                3.4.1  Simple listeoperationer
                3.4.2  Opbygning, filtrering og foldning af lister
                3.4.3  Sortering, sammentælling, gruppering og omformning af lister
        3.5  Operationer på noder og klicheer
                3.5.1  Dyb søgning og erstatning med  **=**
        3.6  Diverse kontrolstrukturer
                3.6.1  Tolkning og citat
                3.6.2  Erklæring af lokale bindinger og regler
                3.6.3  Betingede udtryk
                3.6.4  Eksplicitte kald
                3.6.5  Prøvelser og gentagelser
                3.6.6  Genkald
                3.6.7  Udgange
                3.6.8  Udtryksfølger
        3.7  Sammesatte objekter
                3.7.1  Omkald
                3.7.2  Bundter
        3.8  Styring af  :hvem -binderen
        3.9  Ikke-filtre

4  Regler og pasformer
        4.1  Pasformer
                4.1.1  Konstanter, variable og jokere
                4.1.2  Lister, noder og omsvøb
                4.1.3  Typetjek
                4.1.4  Søgninger
                4.1.5  Navnesplittere
                4.1.6  Aliasser
                4.1.7  Sammenligninger
                4.1.8  Udtræk af længder fra lister og tekststrenge
                4.1.9  Udtrækning af listeelementer
                4.1.10  Ufuldstændige nodeangivelser og pytter
                4.1.11  Konstantudskiftere
                4.1.12  Systemorienterede udtrækkere
                4.1.13  Om PILS systemets håndtering af pasformer
        4.2  Regelhandlingerne
                4.2.1  Ansvarsforskydere
                4.2.2  Bindere
                4.2.3  Mærkater
        4.3  Udpegning af fejlkilder med ansvarsforskydere

5  Systemnære faciliteter
        5.1  Supplerende konstanttyper
        5.2  Tilstande i en omskiftelig verden
                5.2.1  Kanaler, lyttere og kontakter
                5.2.2  Udefrastyrede huskere
                5.2.3  Stropper
        5.3  Simple filoperationer
                5.3.1  Filer
                5.3.2  Mapper
                5.3.3  Filer og mapper i pasformer
                5.3.4  Zipfiler
        5.4  Tråde, knuder og efternølere
                5.4.1  Opretttelse af en arbejdstråd
                5.4.2  Knudekald
                5.4.3  Efternølere

6  PILS programmer
        6.1  Moduler
                6.1.1  Modulnavne og referencer
                6.1.2  Fælles funktionalitet i === -moduler
                6.1.3  Modulreferencer og ændringer i kørende programmer
                6.1.4  Instantiering af moduler
                6.1.5  Datafiler og dataafhængige moduler
                6.1.6  Modul- og programattributter via »dette«
                6.1.7  Sprogmoduler
        6.2  Biblioteker
        6.3  Programstropper
        6.4  Kommandolinjer og programinstanstjek

7  PILS redigering
        7.1  Oprettelse af et PILS program
        7.2  Åbning af et eksisterende program
        7.3  Redigering af moduler
                7.3.1  Simple redigeringsoperationer
                7.3.2  Navigering i modultræet
                7.3.3  Oprettelse, flytning og sletning af moduler
                7.3.4  Ændring af sprog for et enkelt modul
                7.3.5  Ændring af programsproget
        7.4  Brug af PILS biblioteker
                7.4.1  Søgning på tværs af biblioteker
                7.4.2  Om biblioteksfilernes opbygning
        7.5  Testregler
                7.5.1  Lokale testfunktioner
                7.5.2  Testmoduler
                7.5.3  Testprojekter

8  Brugerflader med PILS
        8.1  Vinduer og ruder
        8.2  Hændelser og udbyggere

9  PILS Grafer

1  Indledning

PILS programmeringssproget og -systemet er udviklet af mig (Ole Nielsby) fra 1979 til 2008, oprindelig som et forsøg på at forbedre på Lisp, men under inflydelse af Prolog, C++, XSLT og SQL, og visse egenskaber ved dansk talesprog udviklede det sig efterhånden til noget ganske andet.

Ligesom Lisp bruger PILS samme datamodel for programmer og data, men hvor Lisp bruger lister, bruger PILS attributbaserede noder som byggesten for datamodellen. For kortheds skyld kaldes attributterne for ben. Efter inspiration fra Prolog er de simple funktioner i Lisp erstattet af regelsæt hvor data skal indpasses i en pasform, og efter inspiration fra C++ håndteres disse regelsæt som objekter og kan kombineres på mange måder.

PILS er velegnet til projekter af mange slags, så længe der ikke er brug for tung talknusning eller mere finkornet synkronisering end PILS kan tilbyde. PILS er udstyret med en omfattende samling  tekst- og listebehandlingsoperationer, og programmeringssystemet er, trods sin lille størrelse, ganske modent og fleksibelt, med understøttelse af fejludpegning i kildeteksterne. Bindinger til juce-biblioteket og flertrådning giver mulighed for velfungerende grafiske brugerflader, selvom bindingerne og biblioteket de er pakket ind i, ikke kan betegnes som fuldt udstyrede.

1.1  Vilkår for brug, udvikling og redistribution

Kildeteksterne til PILS systemet er public domain (det betyder at du kan gøre hvad der passer dig med dem) pånær grafikbiblioteket Juce, se  http://www.rawmaterialsoftware.com/juce/  som er underlagt GPL2 med mindre der betales licens.

PILS er konstrueret til åbne kildetekster og understøtter ikke sløring eller kompilering.

Militære institutioner og våbenindustri skal ikke vente sig samarbejde fra min side; ellers hjælper jeg gerne med anvendelse af PILS hvis det er muligt.

1.2  Hvordan skal dette dokument læses

Jeg har forsøgt at arrangere materialet i en rækkefølge der giver mening, men de lidt indviklede indbyrdes afhængigheder mellem sproget og programmeringssystemet betyder at nogle afsnit nødvendigvis må referere til materiale der forklares senere i dokumentet. Hvis du er nybegynder med PILS, bør du nok først læse det igennem og fatte så meget du nu kan, og så prøve lidt PILS programmering, og derefter læse dokumentet igen.

1.3  Nogle generelle egenskaber ved PILS

1.4  Byggestenene i datamodellen

Grundstenen i PILS datamodellen er noden, bestående af et nodenavn og et eller flere unikt navngivne attributter, kaldet ben. Nodenavnet og benenes navne kan være vilkårlige konstanter, herunder konstante noder. Benenes værdier er vilkårlige PILS data.

Nodenavnet og benenes navne gemmes i en kliche der er fælles for alle noder med netop den kombination af nodenavn og bennavne, uanset benenes rækkefølge.

Udover noder og klicheer har PILS lister, tal, tekststrenge samt nogle mere specialiserede typer. Tekststrengene er utf-8-kodede, tallene er flydende tal med fuld præcision (doubles, typisk 15-16 cifres nøjagtighed) hvoraf heltallene er et specialtilfælde, lister er følger der tælles fra 1 og behandles efter samlebåndsprincip når det er muligt: når en liste sendes igennem en serie operationer, sker det ved at ét element ad gangen sendes igennem samtlige operationer.

1.5  Konstanter og udtryk

Alt efter hvordan fortolkeren håndterer dem, kan alle PILS data klassificeres som konstanter eller udtryk. Både konstanter og udtryk er uforanderlige, men konstanter er kendetegnet ved at der ikke sker noget når de fortolkes, og det eneste der kan indpasses i en konstant pasform, er den selvsame konstant. Udtryk kan derimod resultere i noget andet ved tolkning, og noget andet kan indpasses i et udtryk der står som pasform.

Konstanter har ingen identitet og er altid unikt repræsenterede. Når en konstant dannes, eftersøges den først i en global hashtabel.

Alle programmeringskonstruktioner repræsenteres af noder med særlige navne. Sådanne noder samt noder og lister der indeholder dem, er udtryk, alle andre data er konstanter. Udtryk har identitet og kan dermed lokaliseres i kildeteksterne af programmeringssystemet.

Fremmede objekter behandles som konstanter, selvom de – i modsætning til PILS noder og lister – kan have tilstand.

1.6  Regler, pasformer og objekter

PILS objekter opbygges af regelsæt – lister af regler af form  {pasform|handling. Når et regelsæt tolkes, bindes det til den aktuelle kontekst og danner et objekt der kan udføre et kald ved at prøve de regler hvor kaldet kan indpasses i pasformen. For at et kald skal lykkes, skal handlingen tage ansvar, typisk med ansvarstageren  :ok .

Regelsæt kan oprettes dynamisk ved simpel konstruktion af noder af en bestemt form – de kompileres og indekseres automatisk og straks. Bundne regelsæt knyttes sammen i bundter og håndteres under et.

Ved indpasning i pasformer udnyttes konstanternes unikke repræsentation; tit kan komplekse strukturer genkendes eller afvises ud fra simple adressesammenligninger. Der er en omkostning ved at oprette dem, men det opvejes som regel af den hurtige genkendelse, undtagen for talknusning, som sinkes af de indpakninger og  opslag der er nødvendige for at indpasse tallene i en datamodel der er konstrueret til genkendelse og søgning.

1.7  Fejlhåndtering

PILS har et finkornet fejlhåndteringssystem der vistnok ikke findes tilsvarende andetsteds.

I sprog med dynamiske typer kan det være svært at finde fejlkilder, fordi mange fejl udløses på uventede steder i koden, langt væk fra de programmeringsfejl som forårsager dem.

PILS afhjælper dette problem med ansvarsfralæggelse – en regel kan lægge ansvaret for en operation på det udtryk som har kaldt reglen. Det gøres med ansvarsfralæggerne  :prøv  og  :kræv .

Mekanismen har en vis lighed med det der går under navnet structured exception handling, men PILS ansvarshåndtering er mere finkornet og bruger leksikalsk binding.

1.8  Programmeringssystemet

PILS projekter lagres i PILS biblioteksfiler – flade utf-8-tekstfiler der  indeholder en liste over andre brugte biblioteker, efterfulgt af en liste navngivne PILS moduler. Modulnavnene er lister af PILS navne, der vises som et træ af programmeringssystemet.

Når en PILS fil åbnes, oprettes der en programstrop, der sammenfletter filen med de biblioteker den bruger, samt konfigurationsfiler og de biblioteker der udgør programmeringssystemet.

PILS fortolkeren har bindinger til juce-biblioteket. Bindingerne bruges gennem et platformsbibliotek der skjuler visse platformsspecifikke detaljer og i øvrigt muliggør at programmeringen kan ske med danske termer.

1.9  Installation og kørsel på MS Windows

PILS leveres som et installationsprogram der er genereret med InnoSetup – installationsbrugerfladen vil være velkendt for de fleste brugere. PILS fortolkeren er statisk linket mod runtime-bibliotekerne i Visual C++ 2008 Express, og kræver ingen specielle biblioteker på maskinen. En afinstalleringsfunktion er inkluderet.

Den indeværende version af PILS er udviklet og testet på en dansk Windows XP.

2  Syntaks og datamodel

2.1  Konstantsyntaks

2.1.1  Tal

Tal skrives som vant, med foranstillet  -  for negative tal, Et eventuelt decimaltegnet skal stå mellem to cifre, både  ,  (komma) og  .  (punktum) kan bruges. Heltal kan skrives hexadecimalt som i C.

2.1.2  Tekststrenge

Tekststrenge kan skrives som vilkårlige tegnfølger afgrænset af  "  (anførselstegn, ASCII kode 34), disse skal skrives dobbelt hvis de forekommer i teksten.

            "Dette er en tekststreng med et anførselstegn """

Efter det afsluttende  "  kan følge en skraber – en sekvens af tocifrede hexadecimale bytes, dobbeltskrevne anførselstegn og skrabetegn.

Skrabetegnene er:

            =  for LF (Linjeskift, standard i Unix og PILS)
            <  for CR (Macintosh linjeskift)
            >  for TAB
            ~  eller  *  for NULL
            /  for CR+LF (DOS/Windows linjeskift)

Eksempel:

            "Denne tekststreng slutter med et linjeskift"=

Efter en skraber kan følge mere tekst.

            "Dette er en tekst"="på to linjer"=

Tekststrenge kan deles over flere linjer med  -  (delestreg).

            "Dette er en tekststreng på en enkelt linje "-
            "som er fordelt på to linjer i kildeteksten."

Kodningen er altid utf-8, dvs. æ, ø og å repræsenteres som to-byte-sekvenser.

Tal og tekststrenge kan bruges direkte som navne for noder og ben.

2.1.3  Tidsstempler, dateringer, varigheder og farver

PILS har tre indbyggede typer til håndtering af tid.

Tidsstempler lagres som GMT, men skrives i lokal tid med tidszoneangivelse:

            2008-12-06T19:00:05.161+01:00

Minutter, sekunder og millisekunder kan udelades, ligesom minutterne kan udelades i tidszoneangivelsen. GMT kan betegnes med  +00:00 ,  +00  eller  Z .

Dateringer er abstrakte tidsangivelser bestående af dato og klokkeslæt, men uden tilknytning til en tidszone. De skrives som tidsstempler, blot uden tidszoneangivelsen.

Varigheder skrives som:

            1001d5h20m4.567s

for 1001 døgn, 5 timer, 20 minutter, 4 sekunder og 567 millisekunder. Der er ingen understøttelse for uger, måneder og år. Dele af angivelsen kan udelades; f.eks. kan et kvarter skrives:

            15m

Varigheder kan være negative:

            -3h20m

står for minus tre timer og 20 minutter.

Decimaltegnet er altid  .  i tidsangivelser, og bruges kun til at skille sekunder fra millisekunder.

Farver skrives som  #  efterfulgt af RGB (rød-grøn-blå) værdier samt en evt. alfa-værdi der angiver graden af uigennemsigtighed, hver med to hexadecimale cifre, som i HTML. Hvis alfa-værdien ikke angives, sættes den til 0xff.

            #ffff00            gul
            #ffff00ff            gul, med angivet alfa-værdi

For nærværende understøttes kun 8 bits farvekanaler.

2.1.4  Konstantklammer [ ... ]

Sammensatte konstanter (klicheer, noder og lister) sættes gerne i konstantklammer  [ ] . For klicheer er klammerne påkrævede, for noder og lister kan de udelades når sammenhængen udelukker andre læsningsmuligheder. Indenfor konstantklammer gælder forsimplede syntaksregler: de konstruktioner der er nævnt i afsnittet om udtryk er ikke gyldige, navne og operatorer læses som simple konstanter. Eksempel:

            2 + 2    er et udtryk der giver værdien  4

            [2 + 2]    er en liste af tre konstanter  ,  [+]  og  2

2.1.5  Klicheer, noder og lister

Klicheer:  [nodenavn|ben-navn|ben-navn ...]

Nodekonstanter:  node-navn: .ben-navn ben-værdi ...

Listekonstanter:  element, ...    idet  ,  efter det sidste element kan udelades

Den tomme liste  []  kan udelades når det ikke muliggør fejllæsninger. Indenfor konstantklammer betegner  ?  den tomme liste.

I konstante noder kan ben-værdierne udelades når de er identiske med benets navn.

Noderne er grådige:  når som helst en node begynder, strækker den sig så langt det er muligt. I konsekvens af dette er der sjældent brug for parenteser ved indlejrede kontrolstrukturer.

Indenfor konstantklammer kan listekonstanter med to eller flere elementer skrives på kortform uden kommaer. Lister på kortform kan indlægges i kommaadskilte lister, som i denne matrix:

            [1 0 0, 0 1 0, 0 0 1]

Man bør undgå at skrive lister af tekststrenge på kortform, da det kan lede til forvirrende syntaks. Skriv:

            s *=* ["skidt", "godt"]

fremfor

            s *=* ["skidt" "godt"]

2.1.6  Haler og forben

En hale er et ben hvis navn er den tomme liste  [] . PILS fortolkeren sammenkæder mange strukturer ved halerne. Halen kan skrives hvor man ønsker det – umiddelbart efter nodehovedet:

            node: hale

eller senere, med brug af  ;  (semikolon)

            node: .navn værdi; hale

Halen kan efterfølges af flere ben.

Halen skal adskilles fra det foranstående  :  eller  ;  af mellemrum.

Et forben er et ben med samme navn som noden. Dette har ingen speciel betydning i PILS undtagen i ordvælger-noder som beskrevet i det efterfølgende, men hvis forbenet sættes først, kan navnet udelades:

            besked: . "Halløj"    er det samme som    besked: .besked "Halløj"

2.1.7  Ord og sprog

PILS datamodellen giver stor frihed hvad navne angår: enhver konstant, herunder konstante noder og lister, kan bruges som navn på en node eller et ben, selvom det almindeligste er at bruge  navneklicheer som navne.

En navnekliche er en kliche af to tekststrenge  [ordklassenøgle|navnestreng] , som typisk frembringes ved at parse et navn med et eventuelt ordklasse-præfix, via en sprognode.

En sprognode oversætter præfixerne til ordvælger-nøgler, og kan oversætte bestemte navnestrenge til bestemte konstanter for et givet præfix, hvorved PILS tilpasses til nøgleord på forskellige sprog. Sprogobjekter bruges også i forbindelse med brugergrænseflader, til oversættelse af navne og meddelelser.

Alle de indbyggede navne der genkendes af PILS fortolkerkernen, har  "pils.org/ns/sne" som ordklassenøgle;  sne  er et akronym for Scandinavian Nerd-English, nemlig den slags engelsk vi bruger her til lands når vi gør os kloge på ting vi ikke ved hvad hedder på dansk.

Ideen om lokaltilpassede – eller flersprogede – programmeringssprog har været lagt på is siden der gik ged i makroerne fra lokaliserede tekstbehandlings- og regnearkprogrammer en gang for længe siden; men PILS er grundlæggende designet til flersproget brug, og det fungerer uden de store problemer.

2.1.8  Ordklasser og sprognoder

Parsning styres altid af en sprognode:

            [sprog: sprogdefinitionsnode]

hvor sprogdefinitionsnode er en nodekonstant hvis navn er en tekststreng, og hvis tekststreng-benævnte ben har ordklasse-noder som værdier. Benenes navne bruges som ordklasse-præfixer. Der skal være et forben, som gælder for navne uden præfix..

En ordklasse-node er en nodekonstant hvis navn bruges som ordklassenøgle for navne der ikke oversættes. De af ordklassenodens ben der har tekststrenge som navne, definerer oversættelser. Hvis nodenavnet er  , tillades kun oversatte navne; dette er nyttigt for kontrollerede ordklasser.

Sprogdefinitionsnodens hale er en skrive-kontrolstreng på to bytes, hvoraf den første opfattes som flag der styrer brugen af forskellige syntaktiske konventioner ved udskrift, mens den sidste byte  bruges som decimaltegn og bør være  "."  eller  "," .

Skrivekontrolflagene er:

0x01
            Kontroltegn i tekststrenge skrives som kontrolsekvenser
            (beskytter mod dos/unix linjeskift og nul-tegnkoder)
0x02
            alle ikke-ASCII-tegn skrives som kontrolsekvenser
            (muliggør PILS udskrift som ren ASCII)
0x04
            data der ikke overholder utf-8-reglerne, tillades i udskriften
            (normalt vil sådanne data blive udskrevet som kontrolsekvenser)
            (dette sparer plads når binære data skrives som tekststrenge)
0x10
            alle navne skrives som klicheer med tekststrenge
            (omstændeligt, men uafhængigt af sprog)
0x20
            sukker-fri, udtryk og regelsæt skrives med den grundlæggende nodesyntaks
            (instruktivt – viser hvordan PILS parser dine udtryk)

Alle kombinationer er tilladt; 0x02 har forrang for 0x04. Kontrolsekvens-flagene gælder både for navne og tekststrenge; hvis et navn indeholder tegn der skal skrives som kontrolsekvenser, skrives det som tekststreng.

Parseren påvirkes ikke af skrivekontrolstrengen; både  "."  og  ","  kan bruges som decimaltegn, uanset hvad der er angives i skrivekontrolstrengen.

PILS starter op med en sprognode omtrent som vist her (gengivet på engelsk):

            [ language:
              "system":  ! standard-ordklassepræfix
              ."system" ["pils.org/ns/sne":]  ! standard-ordklassenøgle
              ."pils-configuration"  ! hjælpe-ordklasse
                [-:  ! kun de følgende oversatte navne tillades i denne ordklasse
                  ."platform". "juce"  ! værdier som bruges af opstartsprocessen
                  ."system". "Win32"  ! indsæt selv dit foretrukne OS her
                        ! (andre indstillinger er udeladt her for kortheds skyld)
                ]
              ;
              ""01"."  ! skrivekontrolflag 0x01, punktum som decimaltegn
            ]

I dette særlige tilfælde oversættes navne som  pils-configuration:platform  til tekststrenge der  indgår i styringen af opstartsprocessen og sørger for indkobling af de relevante biblioteker.

2.1.9  Sprogindstilling og internationalisering

PILS kan indstilles til dansk sprogbrug ved at kopiere biblioteket  <lib>/pils/danish/pils/config.pils  til brugerens Application Data -mappe. Dette biblioteket medtages så i alle kørende programmer, hvorved visse funktioner, specielt  sig  og  siger  (på engelsk  say  og  saying )  omstilles til at bruge det danske sprogobjekt der er defineret i  <lib>/danish/pils/danish .

For nærværende understøttes kun dansk og engelsk.. Andre sprog – såsom som fransk – kan defineres ved at tilføje disse filer til systemet:

            <lib>/pils/french/pils/config.pils

som kontrollerer sprogvalget, og

            <lib>/pils/french/pils/french.pils

som definerer sprognoden. En simpel fransk sprognode kunne se sådan ud:

            [ language:
              "fr":
              ."fr"
              [ "pils.org/ns/fr":
                ."bon" good
                ."un" one
                ."di" say
                ."blanc" white
              ]
              ."anglais" ["pils.org/ns/sne":]
              ;
              ""01","
            ]

Dette vil oversætte navnet  un  til  one  (i omstændelig notation:  ["pils.org/ns/sne"|"one"] ),  mens  vin  vil blive læst som  ["pils.org/ns/fr"|"vin"]  idet der ikke er defineret en oversættelse.

Hvis en programmør skriver dette i et fransksproget modul:

            di [un bon vin blanc]

og vi udfører det med dansk sprogindstilling, vil resultatet blive:

            One god vin hvid

idet ordene ved indlæsning søges oversat til engelsk, og derefter søges udskrevet på dansk – ordet  vin  udskrives ganske vist på fransk eftersom der ikke er defineret nogen oversættelse for det, men med lidt god vilje forstår vi det jo alligevel.

Der kan defineres simple regler til forbedring af oversættelsen – det danske sprogbibliotek har således en simpel regel der ud fra sammenhængen forsøger at afgøre om det engelske ord no skal oversættes til nej eller ingen, hvilket er ret væsentligt for forståeligheden af meddelelser.

Den danske sprognode dækker så vidt muligt alle ord der bruges i PILS sproget og programmeringssystemet. Den danske terminologi er i øvrigt den originale; den engelske terminologi der bruges internt i fortolkeren er for de fleste ords vedkommende oversat fra dansk.

Bemærk at også de sprogneutrale operatorer defineres som oversættelser; ellers ville de få en dansk ordvælger-nøgle og ikke blive genkendt som indbyggede operatorer.

İstanbul express -metoden – der beskrives i afsnittet om tekstoperationer – viser hvordan oversættelser kan bruges til at tilpasse konverteringerne mellem store og små bogstaver til tyrkisk, hvor prikken over i'et bevares når det skrives med stort.

2.1.10  Regulære navne

Vilkårlige tekststrenge kan bruges som navnestrenge, men for det meste bruges regulære navne, der skrives uden anførselstegn.

Et regulært navn er en sekvens af bogstaver, operatortegn  * / \ ^ # $ % & , additivtegn  + - , sammenligningsoperatortegn  < > = ~ , cifre  0 1 2 3 4 5 6 7 8 9  og  .  (punktum), idet ' @ ` _ og alle tegn uden for ASCII-tegnsættet behandles som bogstaver (herunder æ, ø og å der kodes som utf-8 sekvenser), med de følgende undtagelser:

Grundlæggende er alt tilladt hvis ikke det kan misforstås af parseren.

Navne der slutter med et operatortegn, additivtegn eller et sammenligningsoperatortegn, klassificeres som henholdsvis operatorer, additiv-operatorer eller sammenligningsoperatorer. Indenfor konstantklammer  []  og for nodenavne og nodebenenes navne er dette uden betydning; operatorerne kan uden videre bruges som navne på noder og nodeben. Tildelingssymbolet  :=  er dannet ved at  bruge sammenligningsoperatoren  =  som navn på et ben.

Et præfix er et regulært navn og et  :  uden mellemrum. Præfixet slås op i ordvælger-noden, og værdien bruges til at oversætte det efterfølgende navn eller kombinere det med en ordvælger-nøgle.

Når der ikke bruges præfix , eller når nul-præfixet  ?:  bruges foran en tekststreng, bruges sprognodens standard-ordvælger.

Et PILS udtryk kan ikke definere ordvælger-nøgler lokalt som det er tilfældet med XML; de eneste tilladte præfixer er dem der defineres af sprognoden. Det er dog muligt at læse og skrive navne der tilhører andre ordvælgers, med kliche-notation:

            ["ordklassenøgle"|"navnestreng"]

2.1.11  Apostroffen

En enkelt apostrof  '  læses som en gentagelse af det sidst læste navn der ikke var en operator eller sammenligningsoperator.

            { ok | :ok ' + 1 }

er det samme som

            { ok | :ok ok + 1 }

eller

            { ok | :' ' + 1 }

Hvis en operator eller sammenligningsoperator afsluttes med  , læses den som om den ikke var en operator. Det afsluttende  '  indgår ikke i navnet.

Yderligere  -er kan tilføjes sådanne navne; den sidste ignoreres og resten læses som et almindeligt navn. Tilsvarende for navne der udelukkende består af apostroffer. Derimod medtages afsluttende apostroffer i navnet hvis sidste tegn inden den eller de afsluttende apostroffer ikke er et operatortegn af en slags.

2.1.12  Forkortelser for sprognoder og ordklasse-nøgler

Som en ekstra bekvemmelighed læses  {}  som en forkortelse for den sprognode der parses efter.

Ordklassenøglen for et præfix kan angives som:  præfix:?  eller, for sprognodens standard-ordklasse,  ?:?

2.2  Udtryk

2.2.1  Nodeudtryk

Generelt tolkes noder ved at benenes værdier tolkes og samles til en tilsvarende node (for nodekonstanter vil dette altid resultere i den selvsame node, og derfor sparer fortolkeren arbejdet).

Noder med navnene  []  (den tomme liste) eller  [|handling]  tolkes anderledes og skrives med en særlig notation:

            ;navn værdi ...    for    []: .navn værdi ...
            ;: værdi ...    for    []: værdi ...
            :navn ...    for    [|handling]: .navn ...
            : værdi ...    for    [|handling]: værdi ...

Noder med tomt navn  []  bruges til lokale bindinger:

            ;navn værdi; ...

Handlingsnoderne bruges til kontrolstrukturer:

            :hvis betingelse; udtryk .ellers ellers-udtryk

2.2.2  Navne- og frasekald

Et navn der står alene

            navn

læses som:

            :kald [navn]

I pasformer vil dette binde navnet til den tilsvarende værdi. I udtryk vil den aktuelle kontekst blive gennemsøgt efter bindinger eller regler for navnet.

Hvis navnet efterfølges af en konstant, et regelsæt eller et udtryk i parenteser, dannes en node – dette kaldes en frase.

            a 3    læses som    :kald [a: 3]

            b {x|y}    læses som    :kald b: {x|y}

Fraser lan have navngivne ben

            linje (.fra a .til b)    er det samme som:    :kald linje: .fra a .til b

Forben og skjulte benværdier kan bruges:

            besked (.)    er det samme som:    :kald besked: .besked besked

Konstantklammer kan bruges til at danne fraser:

            besked [. "Hallo"]    er det same som:    :kald [besked: .besked "Hallo"]

I pasformer virker konstantfraserne ganske som navne, men udtryksfraserne behandles som almindelige noder, hvilket sjældent giver mening. Ved tolkning tolkes udtryksfraserne før kaldet udføres.

2.2.3  Objektkald

To enheder efter hinanden læses som et objektkald:

            (a) (b)    er det same som    :hvem a .kald b
            eller, for at pinde det helt ud:    :hvem (:kald [a]) .kald :kald [b]

Hvis den anden enhed er et navn eller en frase, læses det som et metodekald:

            a b     er det samme som    :hvem a .kald [b]
            eller, pindet ud:    :hvem (:kald [a]) .kald [b]

Objektkald sammenknyttes fra venstre:

            a b c    er det samme som    (a b) c

I pasformer er objektkald i almindelighed meningsløse. Nogle metodekald er reservet til typetjek og forskellige andre formål. Pasformer bør bruge objektkald i omsvøb som  : (a) (b)  eller, for nu at vise det samme med en mere omstændelig syntaks,  ::hvem a .kald b .

2.2.4  Kald, fejl og svipsere

Et kald kan afsluttes på en af følgende måder:

  1. Kaldet lykkes og resulterer i en værdi.

    2 + 2    giver    4

  2. Kaldet svipser fordi det hverken kan behandles af de indbyggede operationer eller af reglerne i objektet eller konteksten.

    2 + "to"    svipser, forudsat ingen regler behandler kaldet.

  3. Kaldet fejler, idet en indbygget operation eller en angiven regel detekterer at noget er galt.

    {} læs "[shit}"    fejler med værdien    [fejl: . ?:"Mangler konstant" .start 5 .slut 5]

Fejl signalerer at noget er galt med programmet eller de data det arbejder på, og vil som regel skulle rapporteres til brugeren, hvorimod svipsere tit er et led i en normal procedure og kan ignoreres.

Når en  :kald  node tolkes, sker dette:

  1. kald -benet tolkes.

  2. Den aktuelle kontekst gennemsøges efter regler eller bindinger der kan behandle kaldet.

  3. Hvis ingen regler eller bindinger kan behandle kaldet, signaleres en svipser.

Når en  :hvem .kald  node tolkes, sker dette:

  1. Hvis kaldet er en indbygget operation, overlades kontrollen til denne. Hvis den indbyggede operation ikke kan håndtere kaldet, simulerer den de følgende skridt 2 og 3 idet de dele af udtrykket der allerede er tolket, genbruges – så alting er som hvis den indbyggede operation ikke eksisterede. Hvis en samlebåndsoperation som  enhver  eller  fold  svipser, er denne rekonstruktion umulig, og svipseren behandles som i 6.

  2. kald -benet tolkes, det kaldes argumentet

  3. hvem -benet tolkes, dette er objektet

  4. objektet gennemsøges efter regler eller bindinger der kan behandle argumentet

  5. Hvis det fejler, fejler udtrykket. Hvis det svipser, oprettes en  :hvem .kald -node, der derefter behandles som et kald i den aktuelle kontekst. Dette giver mulighed for at supplere de indbyggede operationer med lokalt definerede regler.

  6. Hvis dette fejler eller svipser og udtrykket blev tolket som handlingen i en  :prøv  eller  :kræv  ansvarsunddragelse, behandles fejlen eller svipseren af denne.

  7. Hvis dette ikke er tilfældet, prøves et  :fejl -kald i den aktuelle kontekst. For svipsere konstrueres en fejlværdi, for fejl bruges den angivne fejlværdi.

  8. Hvis :fejl -kaldet svipser, returneres fejlværdien og udførelsen fortsætter.

Bemærk at argumentet tolkes før objektet. Denne konvention er valgt fordi den falder i hak med fortolkerens optimeringsstrategier, specielt samlebåndsoperationer på lister.

2.2.5  Frasebryder-punktummet

Et punktum i fri luft kan bruges til at skille et navn fra en efterfølgende konstant, regelsæt eller parentes, for at undgå at det læses som en frase.

            a . 3

læses som:

            :hvem (:kald [a]) .kald 3

(Hvis a  resulterer i en liste, vil udtrykket udvælge det 3. element.)

Et friluftspunktum kan også bruges til at skille et navn eller en frase fra et foregående element, for at angive at navnet eller frasen skal indlægges i sin egen  :kald -node.

            a . b

er det samme som

            (a) (b)

eller:

            :hvem (:kald [a]) .kald :kald [b]

(Hvis  a  og  b  giver tekststrenge, vil udtrykket sammenstykke dem.)

2.2.6  Operatorpræcedens

Når en operator indleder en sekvens eller følger efter en anden operator eller sammenligningsoperator, læses den som foranstillet operator.

            + n

læses som:

            :hvem n .kald [+]

Når en operator følger efter en operand, opfattes den som en midtstillet operator.

            a * b

læses som:

            :hvem a .kald *: b

Operatorer der ender på  * / \ ^ # $ % &  har samme høje præcedens som sekvenser.

Additive operatorer der ender på  +  eller  - , har mellemste præcedens.

Sammenligningsoperatorer der ender på  = ,  < ,  >  eller  ~ , har lav præcedens.

Indenfor samme præcedens samles udtryk fra venstre mod højre.

2.3  Regler, pasformer og tildelinger

2.3.1  Regelsæt

PILS objekter opbygges af bundne regelsæt. Et regelsæt som

            { pasform2 | handling2 }
            { pasform1 | handling1 }

repræsenteres af en node

            :regelsæt (;pas pasform2 .handling handling2), (;pas pasform1 .handling handling1)

Når et regelsæt tolkes i en given kontekst, bindes det til konteksten, i form af en node:

              :regelsæt .hvor

Når et kald udføres på et sådant bundet regelsæt, prøves reglerne nedefra og op, idet en intern indekseringsmekanisme gør at kun de relevante regler prøves. Prøvning af en regel sker ved at kaldet søges indpasset i reglens pasform, og hvis dette lykkes, tolkes handlingen med de bindinger der blev etableret af indpasningen.

For at en regel skal have virkning, skal ansvaret behandles, typisk ved ansvarstageren  :ok , men ansvarshenviserne  :prøv  og  :kræv  muliggør mere detaljeret kontrol. Hvis handlingen ikke tager ansvaret, fortsætter søgningen til næste regel. Hvis ingen regel håndterer ansvaret for kaldet, svipser det.

Reglerne kan udtrække information om kaldet med implicitte bindere, hvoraf  :hvem  er den vigtigste og betegner det objekt hvori reglen er fundet.

2.3.2  Pasformer

Pasformer er data med en struktur svarende til strukturen af de data der skal indpasses. Enhver konstant er en pasform der passer til sig selv og intet andet. Lister passer til lister af same længde og passende elementer. Nodeudtryk passer generelt til noder med same kliche og passende ben, men visse noder har speciel betydning i pasformer.

Variable skrevet som navne, repræsenteret af noder af form  :kald [navn] ,  passer til hvad som helst og bindes dertil. Hvis en variabel findes flere steder i en pasform, skal værdierne på de tilsvarende pladser i kaldet være identiske.

Jokeren – skrevet som  ?  og repræsenteret som  :kald []  – passer til hvad som helst uden forbehold.

Andre særlige noder giver mulighed for aliasser, simple typetjek, sammenligninger med talkonstanter, søgebetingelser og længdeudtræk for tekst og lister, udtrækning af specifikke pladser i en liste af ikke angivet længde, simple oversættelser af værdier, og delvist angivne nodeformer med konstanter til udfyldning af manglende ben.

De særlige nodeformer kan bruges som almindelige noder hvis de lægges i omsvøb:

            : særlig-node

2.3.3  Tildelinger

Selvom PILS i hovedsagen er et rent funktionelt sprog, findes der tildelingsssætninger som bruges i forskellige listebehandlingsoperationer og til at sætte attributter på fremmede objekter..

Den ser sådan ud::

            modtager := værdi
            modtager := værdi; hale

Ved tolkning af tildelinger tolkes værdi først, hvorefter  modtager  tolkes med en speciel form for kald der søger efter tildelingsregler eller tildelingsfølsomme indbyggede operationer.

Hvis der er hale på tildelingen, bortkastes resultatet af tildelingskaldet, og hale tolkes og afleveres. Formen med hale svarer til:

            (modtager := værdi) og: hale

Tildelingsregler har denne form:

            { modtager := værdi | handling }

Disse regler håndteres særskilt i regelsættene. Generelle regler som  {x|...}  kan ikke udløses af tildelingerne, og tildelingsreglerne udløses ikke af normale kald, heller ikke hvis kaldet har samme form som en tildeling..

PILS parseren genkender ikke tildelingerne som sådan; de indlæses og repræsenteres som sekvenser der slutter med en grådig node, og repræsenteres som:

            :hvem (modtager) .kald (:= værdi)
            :hvem (modtager) .kald (:= værdi; hale)

2.4  Syntaktisk sukker

De følgende koventioner gælder kun i udtryk der ikke er i konstantklammer [] .

2.4.1  Skjulte ben

Når værdien af et navngivet ben er et kald af benets navn, kan værdien udelades.

Dette bruges i pasformer såvel som i almindelige udtryk, i det meget almindelige tilfælde hvor et bens navn bindes til dets værdi, dvs. for navngivne parametre.

Dette gælder også for forbenene, men ikke for halerne.

            besked: .    er det samme som    besked: .besked besked

Et skjult ben bruges tit med  :ok -ansvarstageren i simple udtræksregler. En nodes hale kan udtrækkes med denne regel:

            {* ;: hale|:ok hale}

eller kortere:

            {* ;: ok|:ok}    hvilket er det samme som    {* ;: ok|:ok ok}

2.4.2  Prikkede pasformer

En regels pasform kan indledes med et punktum, hvilket gør at det følgende navn eller frase ikke indlægges i et kald. Det letter skrivningen af parameterløse operationer:

            { .navn | ... }

er nemmere at skrive end

            { [navn] | ... }

2.4.3  Halekrøller

Eb sekvens af form

            afslutning .(uafsluttet-udtryk)

læses som:

            uafsluttet-udtryk afslutning

hvor  afslutning læses med præcedens som en sekvens, der gerne består af en lang række operationer. Når uafsluttet-udtryk parses og  )  nås, indsættes afslutning som en enhed.

            a b .(c d:)    er det samme som:    c d: a b

En kuperet form findes for etbenede handlingsnoder, omsvøb og dobbelt-omsvøb:

            udtryk .:navn    er det samme som    :navn udtryk

            udtryk .::    er det samme som    :: udtryk

Ansvarshenviseren  :kræv  og listebyggeren  :liste  skrives gerne på denne form:

            udtryk .:kræv    er det samme som    :kræv udtryk

            (... liste := ...) .:liste    er det samme som    :liste (... liste := ...)

Halekrøller kan bruges til at give lange og komplekse udtryk en slags fortællende form: man kan skrive et udtryk og teste det, og derefter indlejre det i en node eller kontrolstruktur ved at sætte en halekrølle i enden, uden at ændre det allerede skrevne.

Halekrøllen er dels inspireret af vildsvinene ved Moesgård, dels af denne konstruktion der er ganske almindelig i talt dansk selvom visse skolelærere anser det for noget griseri:

            Ting små grisebasser elsker at rode rundt i

Strengt taget skulle ting stå sidst, men talesproget tillader at vi trækker det vigtige frem, og det  samme gælder til en vis grad PILS.

2.4.4  Kommentarer

PILS understøtter 3 former for kommentarer:

            ! kommentar (linjen ud, til første CR eller LF)

            !! kommentar (kan strække sig over flere linjer) !!

            :- kommentardata; udtryk

!  og  !! -kommentarerne behandles som mellemrum ved parsning;  :-  kommentarer parses som handlingsnoder og gør det muligt at indlægge kommentardata i udtryk og pasformer.

De svarer løseligt til // kommentar,  /*kommentar*/  og  #pragma  i C++.

2.4.5  Nummererede gengangere

Tit bruges de samme delstrukturer mange gange i en sammensat datastuktur. Nummererede gengangere er indført for at spare plads når sådanne strukturer gemmes som tekst. De bruges generelt ikke i PILS kildetekster.

Tekststrenge, regelsæt samt udtryk i kantede eller runde parenteser kan nummereres med et positivt heltal der skrives umiddelbart foran, og derefter refereres med det samme heltal efterfulgt af  .  (punktum).

            1"fut", 1., 1.    er det samme som    "fut", "fut", "fut"

            123(x) + 123.    er det samme som    x + x

3  Indbyggede operationer

De fleste indbyggede operationer er udformet som objektkald der får særbehandling af fortolkeren. De repræsenteres af noder af form   :hvem .kald .

De allerfleste af disse operationer virker kun når de udføres direkte, ikke gennem konstruktioner som  (argument) kald (objekt) . Dog virker udtræk af listeelementer ved nummer samt sprogobjekternes metoder også ved indirekte kald.

Bemærk forskellen: Dette er et direkte kald:

            {.kliche|:ok [k]} kliche    giver    [[|handling]|hvor|regelsæt]

Den indbyggede operation  kliche  udtrækker klicheen af den node der udgør det bundne regelsæt.

Dette er et indirekte kald og udløser  .kliche -metoden i regelsættet:

            [kliche] kald {.kliche|:ok [k]}    giver    [k]

3.1  Talberegninger

Alle talberegninger udføres med flydende tal, de såkaldte doubles (typisk 64 bits med 52 bit binær mantisse, men det kan afhænge af implementationen). Resultatet af alle beregninger prøvekonverteres til et 32 bit heltal med fortegn og tilbage til flydende tal. Hvis dette resultaterne i det samme flydende tal, genkendes tallet som heltal – det er ikke muligt i PILS at skelne mellem det flydende tal 3,0 og heltallet 3. Både heltal og ikke-heltal pakkes ind i unikke objekter; heltallene fra  0  til  216-1 har fast placering.

Halløjet med fortolkning, indpakning og opslag i konstanttabellen gør at talbehandling er forholdsvis langsom i PILS. Dette er et valg der er truffet i designet: hurtig søgning og genkendelse af data af blandet struktur anses for vigtigere end hurtig talbehandling.

Uendeligheder, tal der ikke er tal osv. understøttes ikke af PILS, og håndteringen af dem er ikke testet særlig grundigt..

Operationer på to tal er

            +    addition
            -    subtraction
            *    multiplication
            /    floating point division (even når supplied with integer argumenterne)
            \    integer division
            %    modulo

Bemærk: alle disse operatorer har samme præcedens:  1 + 2 * 3  giver  9 , ikke  7 .

Foranstillet  -  (minus) gør som forventet:   ;x 7; - x    returns  -7 .

            abs  giver den absolute værdi

            afrund  giver den nærmeste heltallige værdi, idet eksakte halve rundes væk fra 0

            afkort  giver den nærmeste heltallige i retning mod 0

Operationerne  afrund  og  afkort  kan bruges med enhedsangivelse:

            x afrund 10

afrunder x til nærmeste hele 10'er.

Disse efterstillede operatorer er lånt fra  C++  (altid med double-argumenter):

            sin cos tan asin acos atan kvr log exp
            (kvr er den danske udgave af  sqrt,  kvadratrod eller square root.)

Sammenligningsoperatorerne  = <> < <= > >=  lykkes og giver det første argument hvis sammeligningen er sand, men fejler hvis den er falsk; PILS har ingen særlig type til sandhedsværdier.

PILS fosøger at skrive tal så genparsning vil resultere i nøjagtig samme tal, men det virker desværre ikke altid for tal med store eksponenter.

Formateringsstrenge understøttes ikke i PILS.

3.2  Operationer på tidsstempler og varigheder

Almindelige regneoperationer kan bruges på tidsstempler og varigheder i disse kombinationer:

            tidsstempel + varighed
            datering + varighed
            varighed + tidsstempel
            varighed + datering
            varighed + varighed

            tidsstempel - tidsstempel
            tidsstempel - varighed
            datering - datering
            datering - varighed
            varighed - varighed

            varighed * tal
            tal * varighed

            varighed / tal
            varighed / varighed

            varighed \ varighed

            tidsstempel afrund varighed
            datering afrund varighed
            varighed afrund varighed

            tidsstempel afkort varighed
            datering afkort varighed
            varighed afkort varighed

To værdier af samme type kan sammenlignes med  = <> < <= > >=

            tidsstempel sammenligningsoperator tidsstempel
            datering sammenligningsoperator datering
            varighed sammenligningsoperator  varighed

Systemfunktionerne  nu  og  tidsstempel  henter begge den aktuelle systemtid, men  tidsstempel  opjusterer værdien hvis det er nødvendigt for at undgå at give samme værdi flere gange. Denne opjustering har ingen indflydelse på nu -funktionen.

3.3  Operationer på tekststrenge

3.3.1  Konverteringer

s utf-8    konverterer mellem utf-8-kodede tekststrenge og lister af unicode-tegnværdier som heltal.

s utf-16    konverterer mellem utf-16-kodede tekststrenge med byte-order-mark og tegnværdi-lister

s utf-16le    do, little-endian utf-16 uden byte-order-mark

s utf-16be    do, big-endian

s bytes    konverterer mellem tekststrenge og lister af byte-værdier 0 – 255.

Dette sammenstykker 2 tekststrenge:

            (s) (t)

Dette sammenstykker en liste af tekststrenge ss  med tekststrengen t som adskillelse:

            ss splejs (t)

Dette splitter en sammenstykket tekststreng s i en liste, idet der klippes hvor t forekommer.

            s split (t)

Erstatningsoperatoren

            s *=* ss

forudsætter at ss er en liste med et lige antal tekststrenge, der opfattes som erstatningspar. For hver plads i s gennemløbes parrene i ss i den orden de står, og hvis et af dem passer, udføres erstatningen og søgningen fortsætter efter den erstattede tekst.

Start/slut-erstatningsoperatoren

            s (<)$*=* ss

erstatter starten hhv. slutningen af s og udfører aldrig mere end en erstatning.

Tekststrenge kan sammenlignes med operatorerne  = <> < <= > >= .  Som altid tester  =  og  <>  for identitet; den unikke repræsentation af konstanter sikrer at ens tekststrenge er identiske. De andre operatorer sammenligner byte-vis, uden hensyn til alfabetiseringsregler. Sammenligninger der ikke skelner mellem små og store bogstaver, må foretages ved at bruge  småt -operationen på begge operander først. Sortering efter nationale alfabetiseringsregler kan ske ved at bruge  orden -operationen med en funktion der genererer kollateringsnøgler – dvs. tekststrenge der ved rå sorteringer havner i den orden der ønskes. For at lave dansk alfabetisering skal der således bruges en kollateringsfunktion der oversætter æ, ø og å til stigende værdier – hvis der sorteres uden kollateringsfunktion, bliver rækkefølgen å, æ, ø, idet tegnkoderne ligger i denne rækkefølge.

3.3.2  Store og små bogstaver – med İstanbul-ekspressen

En tekststreng kan omsættes til store eller små bogstaver med operatorerne  stort  og  småt :

            s stort
            s småt

Denne operation skifter første tegn i teksten til stort, mens resten ikke ændres:

            s titel

Disse operations er udføres af kode der er genereret ud fra unicode-tabellerne og arbejder direkte på utf-8, og vil fungere med de fleste sprog.

Men desværre er tyrkerne ikke begejstrede for detteher:

            "istanbul" titel    giver    "Istanbul"

Operationen må suppleres med en erstatningsoperation for at give det korrekte resultat:

            "istanbul" $*=* ["i", "İ", "ı", "I"] titel    giver    "İstanbul"

İstanbul-ekspressen er et fif der gør det lidt lettere for tyrkerne.

Modulet  [===,]   som er tilgængeligt fra alle normale PILS moduler, definerer disse erstatninger for de indbyggede operationer, idet skifteoperationerne kombineres med passende erstatninger:

            { tekst . ([stort] // erstat) | :prøv tekst *=* erstat .:kræv stort }
            { tekst . ([småt] // erstat) | :prøv tekst *=* erstat .:kræv småt }
            { tekst . ([titel] // erstat) | :prøv tekst $*=* erstat .:kræv titel }

Ved at erstatte den sædvanlige standard-ordvælger i sproget med en version der oversætter  stort ,  småt  og  titel  som følger:

            {} **=** ;[[?:?]:]
            [ [?:?]:
              ."stort" [stort|"i", "İ", "ı", "I"]
              ."småt" [småt|"I", "ı", "İ", "I"]
              ."titel" [titel|"i", "İ", "ı", "I"]
            ]

og bruge dette sprog til at parse vores testudtryk, får vi:

            "istanbul" titel    giver    "İstanbul"

Således kan omskiftningen mellem små og store bogstaver konfigureres i sprogobjektet.

3.3.3  Tekstsplittere

Tekstsplitteren er en udvidet  split -operation, konstrueret til hjælp for leksikalsk analyse af diveres programmeringssprog, men ganske brugbar til tekstanalyse i det hele taget. Tekstsplitteren er et alternativ til såkaldte regular expressions, der bruges til tilsvarende formål i mange andre programmeringssystemer.

En tekstsplitter er en nodekonstant der angiver en grammatik. Halen er en prioriteret liste af mål (på nørdsk hedder det nonterminaler, og de der er nævnt i halen er mulige startsymboler); de tilsvarende ben i noden beskriver veje til målene (på nørdsk kaldet produktioner). Vejene består dels af mål, dels af tekststumper (på nørdsk terminaler) samt nogle kontrol-instruktioner. Mål kan bruges rekursivt, også de mål der ikke findes i den prioriterede liste.

En vej kan bestå af flere alternative spor, der hver kan bestå af flere skridt og kontrolposter efter hinanden. Kontrolposter er skridt der efterprøver en betingelse uden at rykke frem.

            s split
            [ mål, ...
              .mål vej
              ...
            ]

Dette udtryk vil gennemløbe tekststrengen s og levere en liste af par (mål, tekststump, hvor målene er de overordnede mål i splitterens hale, og tekststumperne er de udsnit af s der passer til målene. Når ingen mål kan nås fra en position i tekststrengen, dannes et par  (, enkeltbyte-tekststump) idet der rykkes en byte frem.

Tomme veje afvises på topniveau og af gentagerne  [*: ...]  og  [+: ...]  for at undgå at analysen kører i ring, men i andre sammenhænge er de tilladt.

Gyldige skridt er:

            mål – henviser til et mål der skal indgå i sporet
            1 – joker, går en enkelt byte frem
            "a-z" – en tekststreng på 3 bytes, passerer en byte der skal ligge i området
            enhver anden tekststreng – skal passe præcist
            [*: vej]så mange gennemløb som muligt af vej, eventuelt ingen
            [+: vej] – så mange gennemløb som muligt, mindst ét
            [=*: vej] – ryk frem indtil vej er farbar, og tag den
            [^*: vej] – ryk frem indtil vej er farbar, uden at tage den

Gyldige kontrolposter er:

            [-: vej] – den angivne vej må ikke ikke være farbar
            [/: mål] – det sidste hovedmål skal være det angivne
            [/: mål, ...] – ...eller et af dem.  $  angiver at indeværende hovedmål er det første.

Bemærk at lister bruges i to niveauer, med forskellig betydning:

            spor, ... – et af sporene skal være farbart
            Et spor kan bestå af flere skridt der skal tages efter hinanden

For tydelighedens skyld bør de spor der udgør en vej, adskilles af kommaer, mens spor der består af flere skridt, skrives på kort listeform, uden kommaer.

Hvor der kun er et enkelt spor som består af flere skridt, skal sporet afsluttes med et komma for at gøre det til en liste af spor, ellers vil hvert skridt blive opfattet som et spor – jf. .hexadecimal  herunder.

Denne tekstsplitter genkender hexadecimale tal:

            s split
            [ hexadecimal,
              .hexadecimal "0x" [+: hexdigit],
              .hexdigit "0-9", "A-F", "a-f"
            ]

Dette splitter s i enkelte utf-8-tegn (gyldige) og klumper af ikke-utf-8-konforme data:

            s split
            [ gyldig ugyldig
              .gyldig ""00"-"7f, ""c0"-"df x,  ""e0"-"ef x x, ""f0"-"f7 x x x
              .x "80"-"bf
              .ugyldig [^*: gyldig]
            ]

Instruksen  -:  kan bruges til test som disse:

            [-: 1]     slutningen af teksten
            [-: -: vej]    snydekig fremad, vej skal være farbar men følges ikke

3.3.4  Fælles operationer for tekststrenge og lister

Operationerne i dette afsnit virker på tekststrenge såvel som lister. Tekststrenge bearbejdes byte for byte, så visse operationer kan give uventede resultater med utf-8 bytesekvenser.

Tekststrengs- og listehhåndtering er optimeret så oprettelse af mellemresultater undgås når det er muligt. Disse optimeringer fungerer ikke når mellemresultater bindes til variable – hvis du har hastighedsproblemer med disse operationer, så brug sammenkædede operationer når det er muligt.

I det følgende betegner s og t tekststrenge eller lister, n et ikke-negativt heltal, og op en operator. Et  (<)  foran en operator – som   (<)+#  – betyder at operator  +#  har en dobbeltgænger  <+#  der arbejder i modsat retning, idet positioner regnes fra tekststrengens eller listens slutning.

s . n    – n'te byte/element af s idet der tællles fra 1, svipser hvis ikke  1 <= n <= s antal .

s antal    giver antallet af elementer i s. ( s utf-8 antal  tæller tegnene i en tekststreng.)

s antal (t)    antal ikke-overlappende forekomster af t i s.

s vend    – s bagfra. Brug  s utf-8 vend utf-8  for at vende en tekst tegnvis.

s (<)+# n    – de første n  elementer af s; svipser hvis  n > s antal .

s (<)-# n    – s uden de første n elementer; svipser hvis  n > s antal .

s (<)++# n    – de første n elementer af s, eller hele s hvis  n > s antal .
For lister udføres  ++#  på samlebånd; så vidt muligt beregnes kun de første n elementer af s.

s (<)--# n    – s uden de første n elementerne; "" hvis  n > s antal .

I det følgende angiver  (<)(+#/-#)op  at op har 2 fætre
            s +#op t    =     s +# (s op t)
            s -#op t    =     s -# (s op t)
og alle de tre har bagvendte dobbeltgængere  <op  <+#op  <+#op

Disse kan uden videre bruges med utf-8 multibyte-tegn:

s (<)(+#/-#)=* t    – længden af s til og med første forekomst af t, eller  0  hvis t ikke findes i s.

s (<)(+#/-#)^* t    –  længden af s indtil første forekomst af t, eller  s antal  hvis t ikke findes.

s (<)(+#/-#)$* t    – længden af t hvis s begynder med t, ellers  .

Disse kan give uventede resultater hvis t indeholder utf-8 multibyte-tegn.

s (<)(+#/-#)#* t    – længden af sammenfaldende start af s og t.

s (<)(+#/-#)~* t    – længden af s til med sidste element af en spredt forekomst af t, eller  0 .

s (<)(+#/-#)+* t    – antal begyndende elementer i s der også findes et sted i t.

s (<)(+#/-#)-* t    – antal begyndende elementerne i s der ikke findes i t.

Som eksempel på hvordan de kombinerede operatorer virker, er her et udtryk der giver navnedelen af et filnavn idet stien og filtypen klippes fra, hvor både  \  og  /  opfattes som sti-separatorer:

            fn <+#-* "\/" <-#=* "."

<+#-* operatoren er sikker at bruge selvom filnavnene kan indeholde  utf-8 multibyte-tegn, idet  \  og  /   er ASCII-tegn og disse aldrig forekommer i utf-8 multibyte-tegn.

3.4  Listeoperationer

De følgende afsnit beskriver operationer der er specifike for lister.

3.4.1  Simple listeoperationer

De indbyggede listeoperationer i PILS virker efter samlebåndsprincippet: i stedet for at konstruere mellemresultater som lister sendes elementerne videre enkeltvis til efterfølgende listeoperationer. Efter den sidste listeoperation opsamles elementerne i en intern struktur, og listen konstrueres når alle elementer er behandlet.

I det følgende er m og n heltal, s og t er lister hvis ikke andet er nævnt.

listevis  og  enkeltvis  er bekvemmelighedsoperationer til situationer hvor der valgfrit kan bruges et enkelt element eller en liste, typisk når der skal angives en eller flere parametre til en funktion.

            s listevis    er det samme som    s kald {e|:ok e,} {& ok|:ok}

Hvis s er a liste, ryger den direkte igennem, ellers indlægges den som eneste element i en liste.

            s enkeltvis    er det samme som    s prøv {ok,|:ok}

Det modsatte af  listevis : hvis s er en liste med et enkelt element, udtages det, alle andre værdier videregives bare..

            s & t  sammenstykker  s listevis  og  t listevis .

            s først (e)    indskyder elementet e foran elementerne i s,  til brug for  fold -operation

Operationerne  op  og   ned  danner lister af stigende og faldende heltal:

            m op (n)    m, m + 1, m + 2 ... n  eller  []  hvis  n < m
            m ned (n)    m, m - 1, ... n  eller  []  hvis  n > m
            n op    er det same som    1 op (n)
            n ned    er det same som    n ned 1

Lister kan splittes op i mindre lister af en given længde:

            s split (n)
            s split 2    splitter s i par.

En liste af lister kan samles til en enkelt liste:

            s splejs

Eventuelle elementer af s som ikke er lister, medtages i den splejsede liste.

3.4.2  Opbygning, filtrering og foldning af lister

Lister kan opbygges med listebyggeren:

            :liste ... liste := værdi ... ...
            :liste [mærkat]; ... liste [mærkat] := værdi ...

Dette danner en liste af alle de tildelte værdier. Den afmærkede form giver kontrol over indlejrede listebyggere.

Listebyggere skrives tit som halekrøller: (... liste := værdi ... ...) .:liste

Lister kan filteres med de følgende operationer. Filtrene er typisk regelsæt der ikke behøver parenteser.

            s hver (filter)

filter -funktion prøves på alle elementer af s efter tur. Resultaterne sendes videres som en liste, idet svipsere springes over.

            s undtagen (filter)

Som hver, men videresender kun de elementer hvor filteret svipser. Resultaterne fra filteret bortkastes.

            s benene (tildelings-filter)

Som hver, men for hvert element e på position n (talt fra 1 som altid) prøves tildelingen  n := e  i filteret, i stedet for bare e.

            s enhver (filter)

Som  hver , men kræver at filteret behandler alle elementerne.

            s find (filter)

Som  a hver (filter) 1 men hurtigere – giver det første filterede element, svipser hvis ingen elementer slipper igennem filteret.

Denne operation fjerner dubletter fra en liste, så kun første forekomst af hver værdi slipper igennem.

            s forskellig

Eller baseret på en nøglefunktion:

            s forskellig (filter)

Filteret prøves på alle elementer i s. Hver gang det giver en konstant der ikke er brugt før, sendes  det originale element igennem. Hvis filteret giver en node  :navn navn; værdi  og  navn  er en ubrugt konstant, sendes  værdi  videre i stedet for det originale element.

Denne operation folder en liste sammen i en tilstand, som returneres.

            s fold (tildelings-filter)

Listen skal have mindst et element. Det første element bruges som starttilstand, typisk angivet som:

            s først (starttilstand) fold (tildelings-filter)

For alle efterfølgende elementerne e skal assign-filter behandle tildelingen  {tilstand := e|...} , og tilstanden sættes til resutatet heraf. Når alle elementer er behandlet, returneres sluttilstanden.

Foldning kan kombineres med en listebygger hvis man har brug for filtrering med tilstand, som vist i dette eksempel:

            ["Rene", han, "John", "Peter", hun, "Jane", "Susan"]
            først [ukendt] fold
            { køn := $ navn | :ok liste := køn, navn; køn }
            { ? := / køn | :ok køn }
            .:liste

 Resultatet er:

            [ukendt "Rene", han "John", han "Peter", hun "Jane", hun "Susan"]

$  og  /  er typetjek, som forklaret i kapitlet om pasformer.)

3.4.3  Sortering, sammentælling, gruppering og omformning af lister

En liste kan sorteres efter nøgler:

            s orden (nøgle-funktion)
            s orden [ok]    (simpel sortering med triviel nøglefunktion)

Det kræves at nøglefunktion virker på alle elementerne i s. Resultatet er kopi af listen, sorteret efter nøglerne. Tal og tidsstempler sammenlignes numerisk, tekststrengene sammenlignes byte for byte uden hensyn til alfabetiseringsregler. Alfabetisk sortering opnås ved at bruge en nøglefunktion der genererer kollateringsnøgler. Sortering efter flere nøgler kan udføres ved at lade nøglefunktionen danne lister; første element i listen er det mest betydende.

Udtræk af et enkelt element ud fra en nøgles størrelse kan ske med:

            s mindst (nøgle-funktion)
            s størst (nøgle-funktion)

Simpelt udtræk af det mindste eller største element kan ske med:

            s mindst
            s størst

Dette eksempel udtrækker den længste tekststreng i en liste::

            s størst {? $ ok|:ok}

Hvis den største nøgle findes for flere elementer, udtrækkes det første.

Tallister kan sammentælles med:

            s sum

Gruppering af data efter nøgler:

            s grupperne (nøgle-funktion)

nøgle-funktion prøves på alle listeelementerne; den bør enten give et konstant navn eller en node  :navn navn; værdi  hvor  navn  er en konstant nøgle, eller svipse; alt andet får operationen til at fejle.

Operationen danner en node som for hver forskellig nøgle har et ben med nøglen som navn og en liste af de elementer der gav denne nøgle som værdi; men for de elementer hvor nøglefunktionen giver en  :navn navn; værdi -node, indlægges værdi i stedet for det originale element.

            grupperne: .navn værdi-liste ...

Hvis der ingen nøgler dannes, giver operationen den tomme liste  [] .

Dette eksempel:

            15 op grupperne {n|:ok {} skriv (n) antal}{13|?}

grupperer heltallene fra 1 til 15 efter antal cifre, idet 13 udelades. Resultatet er:

            [grupperne: .2 10 11 12 14 15 .1 1 2 3 4 5 6 7 8 9]

For hver nøgleværdi opregnes værdierne i samme orden som i den original liste, men nøglernes orden er vilkårlig, som det altid er tilfælde med benene i en node.

Hvis der kun er brug for en enkelt repræsentant for hver nøgleværdi:

            s første (nøgle-funktion)
            s sidste (nøgle-funktion)

virker som  grupperne , men opsamler kun det første/sidste element for hver nøgleværdi, i stedet for en liste af alle elementerne.

            s eneste (nøgle-funktion)

som  første ,  men fejler hvis en nøgleværdi forekommer mere end én gang

            s foldene (kombineret-nøglefunktion-og-tildelingsfilter)

som  første ,  men når en brugt nøgle kommer igen, kræves der at den den ny værdi kan foldes med filterets tildelingsregler, og resultatet heraf huskes nu som nøglens værdi. Hele udtrykket svipser hvis en foldning svipser.

Her er et eksempel der tæller en vareliste sammen:

            [æble 1, appelsin 2, banan 3, æble 4]
            foldene {slags, antal|:ok :navn slags; antal} {a := b|:prøv a + b}

giver

            [foldene: .æble 5 .banan 3 .appelsin 2]

idet værdier for æble  foldes af tildelingsreglen som lægger dem sammen.

Operationen  tværs  vender en liste af lister af ens længder på den anden led:

            [en 1, to 2, tre 3] tværs    giver    [en to tre, 1 2 3]

3.5  Operationer på noder og klicheer

Noder med normale navne kan opbygges ved simpel tolkning:

            hovsa: 1 + 2    giver nodekonstanten    [hovsa: 3]

Handlings- og bindingsnoder opbygges på lignende vis med omsvøb:

            : 4 + 5 + 6

 er det same som

            ::hvem (:hvem 4 .kald +: 5) .kald +: 6

og giver  9 + 6 .

            :: 4 + 5 + 6    giver    : 15    – det dobbelte omsvøb genererer et enkelt omsvøb.

Tolkning af et regelsæt i omsvøb resulterer i et nyt regelsæt hvor alle pasformer og handlinger fra det oprindelige regelsæt er tolket.

En nodes ben kan behandles efter tur:

            node benene (tildelings-filter)

For hvert ben forsøges tildelingen navn := værdi  i filteret; resultaterne videregives som liste idet svipsere ignoreres.

En node kan kopieres uden et navngivet ben:

            node uden (ben-navn)

Et ben kan tilføjes eller erstattes med en angivet værdi:

            node flet (ben-name, ben-værdi)

To noder kan flettes sammen:

            a flet (b)

Benene i b har forrang for ben i a med samme navnen. Nodenavnet fra b bruges hvis det ikke er  [] ; i så fald bruges nodenavnet fra a.

Hvis et af argumenterne til  flet  er  [] , er resultatet det andet argument, undtagen i dette tilfælde:

            [] flet (ikke-tom-liste)

som svipser hvis ikke listen er et pair  (navn, værdi)  hvor navn er en konstant. I dette tilfælde resulterer operationen i en node  pyt: .navn værdi

Denne konvention gør det nemt at bygge noder med  flet,  ud fra den tomme liste.

Noder eller klicheer kan udstyres med et andet navn:

            a hoved (h)

De etbenede klicheer der bruges som navne i PILS, kan opbygges med operationen

            ordklassenøgle // navnestreng

Operationen virker med alle konstanter, men bruges mest med tekststrenge.

3.5.1  Dyb søgning og erstatning med  **=**

Dyb erstatning i noder og lister kan ske med:

            s **=** filter

Først får filter et forsøg på at behandle s i et enkelt kald. Hvis det lykkes, returneres resultatet uden videre behandling. Hvis det svipser og s er en node eller en liste, behandles benene, idet først tildelingen  n := værdi  hvor n er benets navn eller nummer, og derefter værdi prøves i  filter; hvis begge forsøg svipser og værdi er en node eller liste, kaldes videre ud i denne. Hvor filterkaldene lykkes og resulterer i ændrede værdier, genereres nye noder eller lister hvis resultatet af operationen skal bruges.

Dette eksempel nulstiller alle tal undtagen pris-ben som fordoubles. Bemærk at tildelingen prøves først, uanset rækkefølgen af reglerne.

            s **=** {[pris] := # ok|:ok . * 2} {#?|:ok 0}

Hvis operationen bruges i en sammenhæng hvor resultatet ikke bruges (typisk en listeygger), vælger PILS fortolkeren automatisk en hurtigere version der ikke genererer den omformede node.

Dette eksempel bygger en liste af alle nodenavnene i s:

            s **=** {navn * ?|liste := navn} .:liste

Tip: hvis filteret kun består af tildelingsregler, kan det ikke behandle hele s i ét hug. Hvis den øverste regel (som er den der prøves sidst) har formen

            { ? := ok | :ok }

virker søgningen kun i et niveau.

3.6  Diverse kontrolstrukturer

3.6.1  Tolkning og citat

PILS udtryk kan tolkes (en ekstra gang) med operatoren  ---  der kan bruges både mellemstillet og foranstillet:

            --- e    (kan også skrives  e ---. ) tolker værdien af e i de aktuelle bindinger.

            e --- c    tolker værdien af e i bindingerne angivet ved .

Udtryk kan citeres med  :citat -sætningen:

            :citat e    eller, skrevet med halekrølle:    e .:citat

Resultatet er e som det står, uden tolkning.

Bemærk: :citat -sætningen bruges sjældent i PILS. Der er ingen grund til at bruge den på konstante noder og lister.

3.6.2  Erklæring af lokale bindinger og regler

Navne kan bindes til værdierne lokalt:

            ;navn værdi; hale

værdi is tolkes i den aktuelle kontekst k, og hale tolkes i konteksten  ;navn vk  hvor v er den tolkede værdi.

Flere navne kan bindes med én bindingsnode:

            ;navn1 værdi1 .navn2 værdi2 ...; hale

Alle værdierne tolkes i den aktuelle kontekst k  i vilkårlig orden. (Fremtidige versioner af PILS fortolkeren vil måske tolke dem samtidigt.)

Alle konstanter undtagen  []  kan bindes som navne.

Et objekt – typisk angivet ved et regelsæt, her kaldet reglerne – kan indflettes i konteksten:

            :brug reglerne;
            udtryk

eller

            udtryk
            ===
            reglerne

De to former har same virkning: reglerne tolkes i den aktuelle kontekst og kombineres med denne til en kontekst hvori udtryk tolkes.

3.6.3  Betingede udtryk

Et betinget udtryk har denne generelle form:

            :hvis betingelse; udtryk .ellers ellers-udtryk

hvor  .ellers -delen kan udelades:

            :hvis betingelse; udtryk    svarer til    :hvis betingelse; udtryk .ellers []

Hvis  betingelse  lykkes, ignoreres værdien og  udtryk  tolkes. Hvis  betingelse  fejler, ignoreres fejlen og  ellers-udtryk  tolkes.

Betingelser kan kombineres med  og  og  eller .

            :hvis (vare pris < maxpris) og ((vare kvalitet = [god]) eller (vare kvalitet = [fremragende])); køb (vare)

3.6.4  Eksplicitte kald

Som alternativ til formen  (objekt) (argument)  kan objektkald angives med  kald -operationen:

            (argument) kald (objekt)

Operationen anvender  objekt  på  argument .

Dette adskiller sig fra  (objekt) (argument)  på følgende måde:

I den aktuelle version af PILS fortolkeren er tolkningsrækkefølgen i begge tilfælde den omvendte af den tekstlige rækkefølge – i  (objekt) (argument) -formen tolkes argument først, i det eksplicitte kald tolkes  objekt  først.

3.6.5  Prøvelser og gentagelser

Et kald kan forsøges med  prøv -operationen:

            (argument) prøv (filter)

Det virker som kald -operationen, men hvis kaldet svipser, videregives argumentet som det er.

Gentagelse kan udføres med  gentag  operation:

            (argument) gentag (filter)

Dette virker som  prøv -operation, men  filter anvendes igen og igen indtil det svipser, hvorpå den sidste værdi returneres.

Dette eksempel lister heltallene fra 1 til 10  (vi glemmer lige at  10 op  er en nemmere måde.):

            1 gentag {n <= 10|:ok liste := n; n + 1} .:liste

Bearbejdning af en konstant så længe det ændrer den:

            (argument) igen (filter)

argument tolkes og skal give en konstant, ellers fejler operation. Derpå anvendes filter igen og igen, hvilket skal resultere i konstanter, ellers fejler operationen. Når resultatet er det samme som den sidste værdi, returneres det.

            1 igen {x|:ok 1 / x + 1}

svarer til:

            1 gentag {x|;x' 1 / x + 1; :hvis x' <> x; :ok x'}

og giver  1.618033988749895  på en x86 (operationen vil måske fejle på maskiner hvor flydende tal håndteres anderledes).

Som beskyttelse mod uendelige løkker har  igen -operatoren et simpelt indbygget tjek mod cykliske sekvenser: ved iteration nummer 64, 128, 256, 512, 1024... opsamles værdien og sammenlignes med de the følgende værdier. Hvis den kommer igen efter mindst én mellemliggende værdi, antages det at der er tale om en cyklus, og operationen fejler. I eksempler som ovenstående kan afrundingsfejl bevirke at værdien ender med at skifte mellemto eller flere værdierne – uden tjekket mod cykliske sekvenser ville det resultere i en uendelig løkke.

3.6.6  Genkald

Rekursiv analyse med hukommelse understøttes af  genkald -operationen:

            (startkonstant) genkald (analyseregler)

Som kald -operationen, men  :hvem -binderen i analyseregler henviser til et omsvøb der husker både igangværende og afsluttede kald; kun konstanter tillades som kald. Hvis et allerede afsluttet kald gentages, afleveres den gamle værdi. Hvis et igangværende kald direkte eller indirekte gentager sig selv, svipser det inderste kald.

3.6.7  Udgange

:udgang -sætningen ligner  :liste -sætningen:

            :udgang ... udgang := værdi ... ...
            :udgang [mærkat]; ... udgang [mærkat] := værdi ...

Ligesom  :liste -sætningen skrives  :udgang -sætningen gerne med halekrølle:

            (... udgang := værdi ... ...) .:udgang

Hvis tildelingen udføres, afleveres værdi straks som resultat af  :udgang -sætningen.

3.6.8  Udtryksfølger

To udtryk kan kombineres med operatorerne  og ,  eller  og  pyt :

            e1 og: e2

e1 tolkes. Hvis det lykkes, bortkastes resultat og e2 tolkes. Hvis det fejler eller svipser, gælder dette for hele udtrykket, og e2 ignoreres.

            e1 eller: e2

e1 tolkes og returneres hvis det lykkes. Hvis e1 fejler eller svipser, tolkes e2.

            e1 pyt: e2

e1 tolkes og resultatet ignoreres, uanset om det fejler, svipser eller lykkes. Derefter tolkes e2.

3.7  Sammesatte objekter

3.7.1  Omkald

Omkald - noder af form

            kald: argument

har særlig betydning når de bruges som objekter: de udskifter argumentet med objekt. Brugen af det kan illustreres med dette udtryk:

            liste hver {emne|:prøv emne pris}

Udtrykket giver en liste af priserne fra de listeemner der har en pris. Men en nemmere og hurtigere måde er:

            liste hver [kald: pris]

Dette virker som følger: hvert emne fra liste forsøges behandlet af objektet  [kald: pris] , som håndterer dem ved at prøve at kalde theres  pris -metoder.

3.7.2  Bundter

To objekter a og b kan kombineres til et bredt bundt med  +++ -operatoren:

            base +++ udvidelse

Når det udvidende bundt kaldes, kaldes  udvidelse  først. Hvis det svipser, kaldes base.

Bred bundtning svarer til subklasser i traditionel objektorienteret programmering, men bundtning er dynamisk og sker direkte på objekter, idet PILS ikke har klasser.

Hvis  udvidelse er  [] , giver  +++ -operationen simpelthen  base .

Et smalt bundt kan oprettes med  -> -operatoren:

            a -> b

Når dette bundt kaldes, kaldes a først, og hvis det lykkes, kaldes b på resultatet.

Smalle bundter kan ikke behandle tildelinger.

Bemærk:  -> -operator har præcedens som sammenligningsoperatorne og samles fra højre. Præcedensen afledes af det sidste tegn i operatoren.

3.8  Styring af  :hvem -binderen

Normalt vil  :hvem -binderen binde til hele bundtet hvis den bruges i et objekt i et bundt, således at bundtets enkelte objekter kan kalde metoder på det samede bundt, svarende til virtuelle kald i traditionel objektorientering. Et objekt kan isoleres ved at indlægge det i en  hvem: -node:

            hvem: objekt

Det bevirker at  :hvem-bindere i objekt bindes til objekt alene, uanset om det indgår i bundtning.

Til brug for programmeringssystemets indpakning af platformspecifikke objekter findes en udvidet form:

            hvem: . hvem-binding; objekt

hvor hvem-binding specifikt angiver værdien af  :hvem -bindinger i objekt.

3.9  Ikke-filtre

Bundter af form

            ikke: objekt

svipser for kald der kan behandles af objekt. Hvis dette derimod svipser, afleveres kaldet uændret.

4  Regler og pasformer

4.1  Pasformer

Pasformer bruges i regler; de angiver en struktur som data skal indpasses i. I det følgende gennemgås betydningen af forskellige PILS udtryk når de bruges i pasformer.

4.1.1  Konstanter, variable og jokere

En konstant kan stå for den selvsame konstant og intet andet.

En variabel – typisk skrevet som et regulært navn, repræsenteret ved en node  :kald [navn]  er en stedfortræder der kan stå for hvad som helst og binder det til navnet. Hvis et navn bindes flere gange i samme pasform, skal værdierne være identiske, og der genereres kun én binding.

Jokeren  ?  eller i omstændelig notation:  :kald []  står for hvad som helst og ignorerer værdien.

For alle andre konstanter end  []  behandles  :kald konstant  som en variabel.

OBS: denne regel:

            { fac (0) | :ok 1 }

gør ikke hvad det umiddelbart ser ud til. Frasen  fac (0)  læses som  :kald [fac: 0]  der kan stå for hvad som hels t og binder navnet  [fac: 0]  til værdien.

4.1.2  Lister, noder og omsvøb

En liste som pasform står for en liste af samme længde, med elementer der passer til pasformens elementer.

En node som pasform står for en node med samme nodenavn og ben med samme navne og værdier der passer til pasformens tilsvarende ben – undtagen i de tilfælde der er angivet i de følgende afsnit. Som altid er benenes rækkefølge irrelevant.

For konstante noder og lister er kravet om samme struktur og passende værdier ensbetydende med at der skal være tale om den eksakt samme konstant. Det testes med en simpel og hurtig adressesammenligning, uanset konstantens størrelse og art.

Når handlingsnoder bruges i pasformer og skal tages for pålydende – altså, dække over en tilsvarende handlingsnode – skal de sættes i omsvøb.

En pasform i omsvøb:

             : pasform

er det samme som hvis  pasform  stod der direkte, bort set fra at eventuelle særlige regler for node-pasformer ignoreres. Dette muliggør angivelse af nodeformer som ellers ville have speciel betydning. Det følger heraf at et dobbelt omsvøb i pasformen

            :: x

står for et enkelt omsvøb i data – det yderste omsvøb betyder at det inderste omsvøb behandles som en almindelig node og står for en node af samme struktur, altså et omsvøb.

4.1.3  Typetjek

Disse operationer angiver typetjek; s er typisk en variabel eller  .

            # s    tal
            % s    heltal
            + s    heltal > 0, det samme som  % s > 0 , se nedenstående
            s antal   heltal >= 0, det samme som  % s >= 0

            s tid    – tidsstempel
            s varighed    – varighed
            s datering    – datering

            $ s    tekststreng
            +$ s    ikke-tom tekststreng, det samme som  s $ (? > 0) , se nedenstående
            ++$ s    tekststreng med 2 eller flere bytes, det samme som  s $ (? > 1)

            & s    liste
            +& s    ikke-tom liste, det samme som  s & (? > 0)
            ++& s    liste af length > 1, det samme som  s & (? > 1)

            * s    node
            / s    kliche
            = s    – vilkårlig konstant
            s benene    – node eller liste

Konstant-typetjekket  =  kan kombineres med andre typetjek:

            = & s    liste konstant

Platformsbindingerne kan definere yderligere typetjek for systemobjekter.

4.1.4  Søgninger

Søgeoperatorerne    (<)=*    (<)^*    (<)$*    (<)#*    (<)~*    (<)+* t    (<)-*   kan bruges på direkte angivne tekststrenge og listekonstanter. Det resulterende heltal kan ikke udtrækkes men skal være > 0 .

Dette står for en tekststreng der begynder med  "http://"  og slutter med  "/" :

            s $* "http://" <$* "/"

Dette står for en liste der skal indeholde navnet [sprog] :

            s =* [sprog,]

(Bemærk kommaet; søgeoperationerne bruger lister, ikke enkelte elementer.)

4.1.5  Navnesplittere

Klicheer med enkelt attribut som  [ordklassenøgle|navnestreng]  kan splittes i en pasform som:

            ordklassenøgle // navnestreng

idet  ordklassenøgle  og  navnestreng  kan stå for vilkårlige konstanter, men typisk er det tekststrenge.

Denne pasform står for et navn i standard-ordklassen, idet  navn  bindes til hele navnet og  navnestreng  til navnestrengen.

            navn = ?:? // namestring

Opsplitning af klicheer med flere ben understøttes ikke; det er fravalgt fordi benenes udefinerede rækkefølge ville give diffuse regler.

4.1.6  Aliasser

Pasformer som  alias = pasform   kræver at data passer til både alias og pasform. Typisk er alias en variabel der bindes til en node eller liste som analyseres yderligere af pasform.

4.1.7  Sammenligninger

En værdi kan forudsættes forskellig fra en given konstant:

            s <> konstant

eller sammenlignes med talkonstanter:

            s op k

hvor k er en talkonstant og op en af operatorerne  < > <= >=

Denne pasform står for et heltal i intervallet 10 – 20:

            % x >= 10 <= 20

4.1.8  Udtræk af længder fra lister og tekststrenge

Længden af lister og tekststrenge kan udtrækkes med disse udgaver af typetjekkene:

            liste & længde

            tekststreng $ længde

Specifikke krav kan stilles til længden ved at bruge sammenligninger. Denne pasform står for en tekst med mellem 5 og 10 bytes.

            tekststreng $ (5 <= længde <= 10)

4.1.9  Udtrækning af listeelementer

Specifikke listeelementerne kan udtrækkes ved indeksering med heltal.. Positive heltal udtrækker elementer talt fra starten af listen, som altid tælles der fra  1 .  0  udtrækker det sidste element, -1  det næstsidste osv..

Dette binder 3. element fra en liste til variablen  e , idet resten af listen ignoreres:

            (e) 3

Dette står for en liste  q  med identisk start- og slutelement e :

            q = (e) 1 = (e) 0

4.1.10  Ufuldstændige nodeangivelser og pytter

Når navnløse noder (hvilket er det samme som noder med nodenavnet  [] ) bruges som pasformer uden omsvøb, er nodens navn uspecificeret.

            ;x

står for en node med et  x -ben og ingen andre ben, idet  x  bindes til benets værdi.

Nodens navn kan udtrækkes:

            h ;x

Dette er som  ;x  men  h  is bindes til nodenavnet.

Brug af node-typetjekkeren  *  med en nodepasform åbner for noder med flere ben end de angivne:

            * punkt: .x .y    kan stå for noder som   punkt: .x .y .z   osv.

Pytter åbner for noder der mangler de angivne ben, idet pyttens konstanter bruges i stedet for de manglende ben:

            (navn: .ben ...) pyt [.ben ...]

Dette kan kombineres med  *  -typetjekkeren for også at tillade ekstra ben:

            (* navn: .ben ...) pyt [.ben ...]

I forbindelse med pytter behandles nodenavnet  []  på lige fod med andre navne. Hvis alle de angivne ben er dækket af pytten, tillades selve nodenavnet som stedfortræder for en benløs node (idet benløse noder ikke eksisterer i PILS).

Pytter i pasformer er indført for at muliggøre automatisk udfyldning af manglende parametre.

4.1.11  Konstantudskiftere

Konstantudskiftere  udfører simple udskiftninger før indpasning i den berørte pasform. Der er to varianter:

            pasform kald [.nøgle erstatning ...]

Nøglerne er de eneste mulige værdier, og den tilsvarende erstatning indpasses i pasform.

            pasform prøv [.nøgle erstatning ...]

Nøglerne udskiftes, men andre værdier passerer uændret igennem.

Konstantoversættere er blandt andet indført for at håndtere oversættelse mellem talkoder og symbolske navne ved systeminterfacing.

4.1.12  Systemorienterede udtrækkere

De fulde navne kan udtrækkes af fil- og mappeobjekter:

            fil fil: filnavn

            mappe mappe: sti

PILS udvidelser til systemobjekter kan udtrækkes:

            systemobjekt når: pils-udvidelse

4.1.13  Om PILS systemets håndtering af pasformer

PILS er generelt et fortolket sprog, men alle pasformer kompileres til en mellemform kaldet quicksteps, som udføres betydelig hurtigere end fortolkede udtryk. Quicksteps opdaterer aldrig referencetællere, opretter aldrig objekter og søger aldrig i konteksten undervejs. Først når en indpasning er lykkedes, oprettes eventuelle bindinger og referencetællere opdateres.

Kompileringen er hurtig og sker automatisk når som helst læses af parseren eller genereres ved tolkning af PILS udtryk.

Regelsøgning er optimeret ved indeksering af reglerne i et regelsæt. For at en pasform kan indekseres, skal yderste niveau være en fuldt specificeret liste eller node, eller en konstant.

Aliasser påvirker ikke indekseringen, så regler som  { alias = indekserbar-pasform | ... } er indekserbare.

Tildelingsregler indekseres efter tildelingens venstreside og holdes adskilt fra de øvrige regler.

Indekseringen er usynlig for programmøren, når der ses bort fra udførelseshastigheden. Ved opbygning af store regelsæt, såsom tilstandsmaskiner for parsere, bør reglerne så vidt muligt være indekserbare, og ikke-indekserbare regler bør samles i klumper, helst øverst i regelsættet.

4.2  Regelhandlingerne

Når en regelhandling påbegyndes, er reglen uskyldig: det er ikke afgjort om reglen vil lykkes, fejle eller svipse. De ansvarsrelaterede konstruktioner der beskrives i det følgende, er kun gyldige i reglens uskyldige tilstand; hvis de bruges efter at ansvaret er taget, fejler de.

4.2.1  Ansvarsforskydere

PILS har fem ansvarsforskydere:

            :ok hale
            :prøv udtryk
            :kræv udtryk    (skrives gerne som  udtryk .:kræv )
            :muligvis udtryk
            :fejl udtryk

Ansvarsforskydernes leksikalske binding og deres tilknytning til specifikke operationer frem for  kodeblokke muliggør en mere finkornet fejlhåndtering end try-catch -konstruktionen der bruges i mange udbredte sprog.

Ansvarstageren  :ok  er den almindeligste måde at få et resultat ud af en regel på. Ansvaret for kaldet tages, og  udtryk  tolkes og afleveres. Eventuelle ansvarsforskydere i udtryk fejler; fortolkeren har bortkastet den information der muliggjorde forskydning af ansvaret.

Konstruktionen svarer til return-sætningen i C og lignende sprog, men med garanti for haleudfladning.

Implementationsnote: Når  :ok -ansvarstageren er den første og eneste handling bort set fra eventuelle forudgående bindere, bortoptimerer kompileren stakoperationerne til håndtering af ansvar. Hvis halen er en konstant, en variabel der er bundet af reglen, eller en simpel node- eller listekonstruktion baseret alene på konstanter og reglens egne variable, kompileres reglen helt igennem, og bindingsnoden bortoptimeres.

Ansvarsforskyderen  :prøv  forsøger at videregive ansvaret til det øverste niveau i udtryk, uden selv at tage ansvar. Hvis det øverste niveau i  udtryk  lykkes eller fejler, sker det samme for det originale kald, med haleudfladning hele vejen. Hvis det svipser, returnerer  :prøv -konstruktionen den tomme liste. Reglen vil så svipse, hvis ikke en anden konstruktion tager ansvaret.

I modsætning til try -sætningen i C++ og lignende sprog gælder  :prøv  ikke for deludtryk der fejler  eller svipser.

Ansvarsfralæggeren  :kræv  bruges til delresultater der er nødvendige men ikke tilstrækkelige for at reglen kan lykkes.

Hvis den yderste operation lykkes, gives den som resultat af   :kræv -sætningen og indgår i det omgivende udtryk. Hvis den fejler, videregives til det originale kald. En svipser får en eventuel igangværende  :prøv -sætning for same regel til at svipse og aflevere den tomme liste. Hvis ingen  :prøv -sætning er aktiv, svipser reglen.

Hvis et krævet  udtryk har deludtryk der kan fejle eller svipse, kan de dækkes af indlejrede  :kræv -ansvarsfralæggere. Halekrølle-formen  udtryk .:kræv  er praktisk til dette brug.

Den forsigtige ansvarstager  :muligvis  tolker og afleverer  udtryk , men tillader i modsætning til  :ok  indlejrede ansvarsforskydere, typisk  :kræv , der har samme virkning som i en  :prøv -ansvarsforskyder.

Ansvarsfralæggeren  :fejl  får det originale kald til at fejle med den angivne værdi.

4.2.2  Bindere

Disse bindere:

            :selv hale
            :hvem hale
            :hvor hale
            :hvad hale

binder deres navne til data omkring kaldet, som følger.

selv  binder til det bundne regelsæt hvor reglen optræder, og kun dette regelsæt.

hvem  binder til det kaldte objekt, som kan være et bundt hvori det bundne regelsæt indgår. Hvis regelsættet ikke er bundtet, er  hvem  det same som  selv .

For simple kald uden angivelse af objekt vil  hvem  binde til den kontekst hvorfra kaldet blev foretaget, dvs. det samme som  hvor .

hvor  binder til konteksten for det originale kald.

hvad  binder til det udtryk der udløste kaldet.

Binderne  selv  og  hvem  bruges typisk når et objekt skal kalde sine egne metoder. Brug af  hvem  svarer i denne sammenhæng omtrent til OO sprogenes virtuelle kald.

Binderne  hvor  og  hvad  bruges til at stedfæste fejl. Når ansvaret for et kald sendes videre til et andet kald med  :prøv  eller  :kræv , vil bindere i viderekaldet opføre sig som følger:

            :hvor    og    :hvad    binder til det original kald.

            :selv    og    :hvem    binder til viderekaldet.

4.2.3  Mærkater

Bindere og ansvarsforskydere findes i mærkede versioner til brug i indlejrede regler.

De mærkede bindere

            :selv [mærkat]; hale
            :hvem [mærkat]; hale
            :hvor [mærkat]; hale
            :hvad [mærkat]; hale

binder  navn [mærkat]  i stedet for  navn .

De mærkede ansvarsforskydere

            :ok [mærkat]; hale
            :prøv [mærkat]; hale
            :kræv [mærkat]; udtryk
            :tak [mærkat]; udtryk
            :fejl [mærkat]; udtryk

bruges med en mærkat:

            :mærkat [mærkat]; hale

og tillader en indre regel at agere på vegne af en ydre regel:

            { ...
            | ... :mærkat [mærkat]; ...
              ... { ... | ... :ok [mærkat];  ... }
            }

Generelt bruges mærkater sjældent, men PILS redigeringen bruger dem til håndtering af syntaksfejl.

4.3  Udpegning af fejlkilder med ansvarsforskydere

Den finkornede håndtering af  ansvar gør det nemmere at sætte fingeren på en fejlkilde. Et af de største problemer ved løst typede sprog viser sig i situationer som den der her skitseres med et simpelt eksempel:

            ! Annas program forsøger at fordoble et tal ved hjælp af Bennys fordobler-bibliotek.
            dobbelt "MMVII"
            ===
            ! Bennys bibliotek bruger Børges bibliotek:
            { dobbelt: x | :ok gang (x  .med 2) }
            ===
             ! Børges biblioteksfunktion forventer talværdier, ikke tekststrenge med romertal:
            { gang: x .med | :ok x * med }

Når Anna kører sit program, vil operationen  x * med  svipse og udløse en fejlmeddelelse i Børges bibliotek, hvad der godt kan virke forvirrende på Anne, eftersom hun ikke aner at hun bruger Børges bibliotek igennem Bennys.

Halfdan Rasmussen har beskrevet det rammende:

            Bennys bukser brændte,
            Børge råbte: åh!
            Børge havde nemlig
            Bennys bukser på.

Traditionelt vil man i en sådan situation granske stakdump eller skridte igennem koden for at finde fejlkilden. Men i PILS kan man gennem hensigtsmæssig brug af ansvarsbegrebet typisk straks slå ned på fejlkinden.

Med ansvarsforskyderne  :prøv  og  :kræv  kan en biblioteksfunktion udføre specifikke operationer på vegne af kaldet af funktionen, så kaldet får skylden hvis noget går galt.

Når Børge bliver træt af brok fra folk der bruger hans biblioteksfunktion med forkerte parametre, laver han måske en revideret udgave der forholder sig pessimistisk til argumenterne og ikke stoler på at de har de forventede operationer. Anna vil nu have denne situation:

            dobbelt "MMVII"  ! Anna's application
            ===
            { dobbelt: x | :ok gang (x .med 2) } ! Bennys bibliotek
            ===
            { gang: x .med | :prøv x * med } ! Børges reviderede bibliotek

Nu får operationen  gang (x .med 2)  i Bennys bibliotek skylden. Benny kunne så revidere sit bibliotek, så Anna får denne situtation:

            dobbelt "MMVII"  ! Anna's application
            ===
            { dobbelt: x | :prøv gang (x .med 2) } ! Bennys reviderede bibliotek
            ===
            { gang: x .med | :prøv x * med } ! Børges reviderede bibliotek

Nu skydes skylden straks på kaldet  dobbelt "MMVII"  – og vi får udpeget den rygende tændstik i Annas hånd. Mysteriet er opklaret.

Ovenstående lille eksempel er kun til illustration af konceptet; til simple beregninger af den art vil et typetjek i pasformen være tilstrækkeligt. Men i situationer hvor parametrene er objekter der formodes at understøtte visse metoder, er typetjekkene ikke til megen hjælp, idet PILS ikke har klasser eller interfaces der kan testes mod. I stedet kan metodekaldene foretages med ansvarsfralæggelse, så det kald der har leveret parametrene, får skylden hvis disse ikke understøtter de forventede metoder.

PILS er indrettet så denne teknik stort set ikke kræver ekstra skrivearbejde.

5  Systemnære faciliteter

5.1  Supplerende konstanttyper

PILS kernen understøtter udvidelse af datamodellen med konstanttyper, som kan behandles af parseren ved udvidelser af denne.

Når parseren møder en startkrølparentes  { , spoler den frem idet der holdes styr på kommentarer, tekststrenge og indlejrede parenteser. Hvis den finder en slutkrølparentes  }  uden en mellemliggende bjælke  | , prøves parserudvidelserne på de mellemliggende tegn.

De ekstra typer levers af platformsbindingerne og kan være forskellige for forskellige platforme.

5.2  Tilstande i en omskiftelig verden

Normalt er PILS data tilstandsløse og uforanderlige, men nogle få tilstandsbærende objekter er indført i sproget for at håndtere grænseflader til en foranderlig verden. De er udformet så risikoen for resurselækager er lille, ved specielle forholdsregler omkring de konstruktioner der gør det muligt at opbygge cirkulære strukturer.

5.2.1  Kanaler, lyttere og kontakter

En kanal er en nodekonstant af form

            [kanal: nøgle]

hvor nøgle er en vilkårlig konstant. Operationen

            kanal lyt (lytter)

opretter en kontakt, der i hele sin levetid forbinder lytter med kanal og ikke har andre funktioner. Den eneste måde kontakten kan afbrydes på, er at tabe alle referencer til den så den fjernes af PILS afrydningen.

Når en kanal udsættes for et kald, videresendes det til kanalens lyttere efter tur, idet de sidst indsatte lyttere prøves først, indtil en af dem tager ansvaret, som hvis lyttterne havde været bundtet med  +++ -operatoren, bort set fra at  :hvem  binder til kontakten.

Som alle konstanter er kanaler unikke. Hvis et udtryk som (kanal: nøgle)  udføres flere gange for samme nøgle, genbruges kanalen. PILS programmeringssystemet bruger kanaler til at holde redigeringer af PILS filer og moduler unikke, ved at bruge fil- og modulnavne i kanalnøgler.

Kanaler er trådsikre.

5.2.2  Udefrastyrede huskere

Når PILS bruges med et vinduesbaseret brugerfladesystem, vil vinduernes levetid generelt være styret af brugeren eller platformen, ikke af PILS. Sådanne objekter kaldes udefrastyrede, og PILS håndterer dem gennem indpakningsobjekter der håndteres som konstanter, med metoder som defineres af platformsbindingerne.

Huskere er udefrastyrede objekter der kan have tilknyttede PILS objekter og herigennem kan referere cirkulært til hinanden.

PILS bindingerne bruger platformens hændelses-håndtering (på nørdsk: event handling) til at  holde styr på hvornår udefrastyrede objekter bliver nedlagt – når det sker, blænder PILS indpakningsobjektet og frigiver eventuelle tilknyttede data hvis objektet er en husker.

Data kan tilknyttes en huskekage og hentes igen:

            huskekage husk . nøgle := værdi;
            ...
            huskekage husk . nøgle

Værdierne kan overskrives, men nøglerne kan ikke slettes når de først er oprettet. Værdierne kan kun udtrækkes hvis man har de tilsvarende nøgler; der er ingen mulighed for systematisk gennemløb af huskekager.

5.2.3  Stropper

Noder af form  [strop: nøgle]  kan bruges som en slags kanaler, men de kan kun have én lytter, og kun når de er hægtet på en eller flere udefrastyrede objekter. Hvis de udefrastyrede objekter nedlægges, taber stropperne straks hukommelsen.

Stropper bruges af programmeringsystemet til programstropper der hægtes på alle programmets vinduer. Nå alle de vinduer der hører til et program, taber programstroppen hukommelsen og frigiver dermed programmets resurser.

Ved opstart af programmer hæftes stropperne midlertidigt på universalnøglem-

5.3  Simple filoperationer

Fil- og mappeobjekter giver adgang til simple operations på filer og filsystemer.

En mappe er det der på nørdsk hedder et directory.

Læsning og skrivning er kun understøttet som afsluttede operationer på hele filer. Fil- og mappeobjekter er ret beset kun indpakninger af navnene, med adgangsbillet filsystemet; konceptet åbne filer findes ikke i PILS.

Fil- og mappenavnene angives med fuld sti, med  /  som skilletegn på alle systemer, også MS Windows. Det sædvanlige MS Windows-skilletegn  \  bruges ikke i PILS filnavne.

Mappenavne har altid et afsluttende  / .

Funktionerne  fil  og  mappe  er metoder på universalnøglen og virker ikke uden den. Ved at skjule universalnøglen kan der konstrueres sandkasser hvor fremmed kode kan udføres med begrænset adgang til filsystemet.

5.3.1  Filer

            fil (filnavn)
            datafil (filnavn)

opretter et filobjekt –  fil  er den indbyggede funktion,  datafil  er en indpakning der bruges til dataafhængige moduler.

Filnavnet kan udtrækkes ned

            fil navn

Filen kan læses og skrives i et hug:

            fil tekst
            fil tekst := tekst

Læsning og skrivning sker som rå bytes – eventuelle konverteringer udføres som særskilte operationer:

            fil tekst bytes utf-8

indlæser en ANSI-kodet fil som en utf-8-kodet tekststreng.

            fil tekst := tekst utf-8 bytes

skriver en utf-8-kodet tekststreng som en ANSI-kodet fil.

Filer kan manipuleres med disse operationer:

            fil (filnavn) kopiér (nyt-filnavn)
            fil (filnavn) flyt (nyt-filnavn)
            fil (filnavn) slet

Resultat af  kopiér  og  flyt  er filobjektet for nyt-filnavn.

Eksistensen af filer kan testes med

            fil ok

som giver fil hvis filen findes, og ellers svipser

            fil læsbar    giver  1  hvis læsning er mulig og tilladt, ellers  0 .
            fil skrivbar    tilsvarende for skrivning

            fil antal    giver filens størrelse i bytes

            fil tidsstempel    læser tidsstemplet for ændring af filen
            fil tidsstempel (tid)   sætter  tidsstemplet for ændring af filen

Der er ingen specialiserede operationer til opsplitning af filnavne i dele; dette gøres nemt med de almindelige operationer på tekststrenge:

            fn <-#-* "/"    giver mappens navn
            fn <+#-* "/"    giver filnavnet uden mappe
            fn <+#-* "/" <-#=* "."    giver filnavnet uden mappe og type
            fn <+#-* "/" <+#=* "." --# 1    giver filens type
            fn <-# (fil navn <+#-* "/" <=* ".")    giver filnavnet uden type
            fn <+#-* "/" <-#=* "."    giver filnavnet uden mappe og type

5.3.2  Mapper

            mappe (mappe-navn)

opretter et mappeobjekt. mappe-navn skal afsluttes med skilletegnet  /  og kan udtrækkes med:

            mappe navn

Filer og undermapper kan udtrækkes:

            mappe filerne    giver en liste af filobjekter
            mappe mapperne    giver en liste af mappeobjekter

Filtrering kan udføres som separat skridt. – dette eksempel finder filer af type  .pils :

            mappe filerne hver {ok fil (? <$* ".pils")|:ok}

Dyb søgning i en mappe efter filer kan udføres med en rekursiv regel:

            mappe kald {m|:hvem :ok m filerne & (m mapperne enhver (hvem) splejs)}

Tomme mapper kan oprettes og slettes:

            mappe opret
            mappe slet

5.3.3  Filer og mapper i pasformer

Filtering af filer og mapper understøttes ved brugen af  fil  og  mappe  som operatorer i pasformer.

            fil fil (fn)   dækker over en fil idet fn dækker over navnet

            mappe mappe (mn)   dækker over en mappe idet mn dækker over navnet

Dette eksempel lister alle  .pils  filer i brugerens dokumentmappe med tidsstempler, idet mapper af navn backup  og deres undermapper udelades

            mappe (platform sti dokumenterne) kald
            { m mappe (?) | :hvem :ok m filerne & (m mapperne hver (hvem) splejs) }
            { ? mappe (? <$* "/backup/") | ? }
            hver { fil fil (filnavn <$* ".pils") | :ok filnavn, fil tidsstempel }

5.3.4  Zipfiler

Zipfiler kan læses med:

            fil (zipfilnavn) zip

som giver en liste af lister  (sti, data, tidsstempel)  for alle pakkede filer i zipfilen.

En zipfil kan oprettes med:

            fil (zipfilenavn) zip := posterne

Bemærk: skrivning af zipfiler er p.t. ikke implementeret i jucePILS.

Ligesom ved almindelige filnavne bruges  /  som skilletegn, selvom zip-formatet bruger  \  internt.

Der er ingen understøttelse for ændringer eller tilføjelser til zipfilerne, udtrækning af enkelte filer eller læsning/skrivning af andre arkivformater.

Bemærk: OpenOffice-pakken lagrer sine dokumenter i zip-format. HTML-udgaven af PILS dokumentationen er skrevet med OpenOffice Writer og konverteret til HTML af et PILS script.

5.4  Tråde, knuder og efternølere

Med arbejdstråde kan man undgå at PILS brugergrænsefladen låser under langvarige beregninger, og  udnytte multiprocessorsystemer til ad udføre beregninger parallelt.

PILS tråde og knuder er beregnet til dette og har ikke den finkornede synkroniseringsmekanik der kræves til proceskontrol. Til gengæld er man fri for at bekymre sig om låsninger der går i hårdknude (på nørdsk: deadlocks), og det er umuligt at kalde objekter fra en forkert tråd.

Biblioteket  lib/pils/english/compute/compute.pils  tilbyder a simpel indpakning af brugen af en enkelt arbedstråd til parsning af filer eller lignende.

5.4.1  Opretttelse af en arbejdstråd

            :tråd udtryk

Denne konstruktion kan kun bruges i hovedtråden. En arbejdstråd oprettes og startes, og  []  afleveres som resultat. Arbejdstråden tolker udtryk, bortkaster resultatet og afsluttes.

Systemkald og brugergrænsefladeoperationer kan ikke udføres direkte fra arbejdstråde. Men en arbejdstråd kan låne hovedtråden ved at kalde en knude.

5.4.2  Knudekald

Den eneste form for synkronisering der findes i PILS er knudekaldet, dvs. kald af metoder på et objekt indpakket i en knude:

            (knude: objekt) metode

Knuden omdirigerer alle kald af objektet fra arbejdstråde til hovedtråden, som behandler dem når den ikke er optaget af andre opgaver. Den kaldende arbejdstråd blokeres indtil hovedtråden har udført kaldet.

Der er ingen mulighed for at sætte en arbejdstråd i pausetilstand for at vække den senere.

Når kaldet lykkes, fejler eller svipser, sættes arbejdstråden i en tilstand der afspeljer dette, og genstartes. Trådskiftet er generelt gennemsigtigt, men haleudfladning og samlebåndsoperationer på lister holdes inden for den enkelte tråd, og ansvarstagere, listebyggere og udgange fejler hvis man forsøger at bruge dem over trådgrænser.

Generelt bør arbejdstråde udføre selvstændige beregninger det meste af tiden, og kun lejlighedsvis bruge knuder for at tilgå systemet, opdatere brugergrænsefladen og udveksle information. Hvis en arbejdstråd bliver hængende i et knudekald, vil PILS systemet være låst.

I hovedtråden behandles knudekald som alle andre kald, uden nogen form for synkronisering.

Det PILS modul der viser fejlmeldinger, er indlagt i en knude, så når en arbejdstråd udløser en fejlmelding, vil den blive vist i hovedtråden. Arbejdstråden kører videre efter at fejlmeldingen er udløst.

5.4.3  Efternølere

Under opbygning af brugerflader er det sommetider nødvendigt at sætte operationer i kø til senere udførelse, typisk efter at det vindue der er under konstruktion, er vist og opmålt.

            :senere udtryk

udtryk lægges i kø idet den aktuelle kontekst vedhæftes, og  []  afleveres straks som resultat. Når hovedtråden er ledig, tolkes udtryk i den vedhæftede kontekst, og det hele bortkastes.

Efternølere kan dannes både fra hovedtråden og arbejdstrådene; de udføres altid i hovedtråden.

6  PILS programmer

Et PILS program er en samling moduler, indeholdt i en eller flere biblioteksfiler. De moduler og biblioteker der er involveret i kørslen af et program, samles i en programstrop.

Programstropperne administreres af biblioteket  pils/english/system/system.pils  der ved opstart findes ud fra den eksekverbare fils mappe og dens omgivende mapper, hvorefter modulet  [pils system boot]  parses og kaldes.

De moduler i systembiblioteket der administrerer programstropperne, bruger selv en forenklet form for referencer til hinanden. De bør ikke ændres hvis man ikke ved hvad man gør – hvis de går i kludder, trækker de PILS fejlmeldingssystemet med sig, og man må ty til primitive og besværlige metoder for at finde ud af hvad der går galt.

6.1  Moduler

De grundlæggende byggeklodser i PILS programmeringssystemet kaldes moduler. Generelt er et module et navngivet PILS udtryk, som typisk resulterer i et bundet regelsæt, som er det der ses udefra..

            { ... | } ! synlige regler
            ...
            ===
            private definitioner

I biblioteksfilerne lagres hvert modul sammen med et modulhoved der indeholder modulets navn og nogle attributter, hvoraf  .tidsstempel  angiver hvornår modulet sidst  blev redigeret, og  .sprog  angiver et PILS sprogobjekt som bruges til parsning af modulets tekst.

Modulerne er det der på nørdsk hedder singletons, på programbasis: når flere dokumenter er åbne samtidig, har de hver sin programstrop med egne eksemplarer af modulerne. Desuden dannes der ekstra eksemplarer som kan eksistere sideløbende med de gamle hvis modulerne redigeres mens de er i brug.

6.1.1  Modulnavne og referencer

Et modulnavn er en liste af PILS navne. PILS redigeringen præsenterer dem som et træ, idet fælles begyndelser slås sammen.

Et modul kan ikke umiddelbart kalde sine egne metoder. Hvis en regel har brug for at kalde andre regler i det same regelsæt eller bundt, må det ske gennem binderne  :hvem  eller  :selv .

Moduler kan referere til hinanden ved hele modulnavnet:

            @@ [spil bræt skak]    refererer altid til    [spil bræt skak]

Eller ved relative navne. I et modul ved navn  [spil bræt]  vil

            @@ skak

referere til det første eksisterende af modulerne  [spil bræt skak] ,  [spil skak]  eller  [skak,] , mens

            @@ . [skak brikker]

tilsvarende refererer til  [spil bræt skak brikker] ,  [spil skak brikker] eller  [skak brikker]

Absolutte modulreferencer håndteres af en funktionsregel  {@@: modulnavn|...}  mens relative modulreferencer håndteres af et hjælpeobjekt ved navn  @@ .

Både absolutte og relative modulreferencer kan bruge beregnede modulnavne.

Moduler bruges typisk via en metode hvis navn er sidste led i modulnavnet. Hjælpeobjektet  @  gør at man slipper for at skrive navnet to gange:

            @ bræt (...)    er det samme som    @@ bræt bræt (...)

            @ bræt    er det samme som    @@ bræt bræt

Længere referencer kan angives som her:

            @ [bræt] dam (...)    er det samme som    @@ . [bræt dam] dam (...)

            @ [spil bræt] dam (...)    er det samme som    @@ . [spil bræt dam] dam (...)

6.1.2  Fælles funktionalitet i === -moduler

Funktionalitet som er almindeligt brugt i en applikation, kan defineres som regler i et modul hvis sidste navneled er  === . Reglerne kan bruges direkte fra alle andre moduler i den pågældende gren af modultræet.

Systembiblioteket  system.pils  indeholder et modul  [===,]  med funktionalitet der kan bruges fra alle PILS moduler.

6.1.3  Modulreferencer og ændringer i kørende programmer

Modulreferencer er som udgangspunkt dynamiske: hvis et modul ændres mens det er i brug, vil referencer til modulet herefter resultere i en instans af den nyeste version.

Modulreferencer der er udført før rettelsen blev gemt, vil fortsat referere til den gamle version.

Denne forskel er væsentlig når man eksperimenterer med ændringer i en kørende applikation: den funktionalitet der tilgås gennem dynamiske modulreferencer, ændres øjeblikkelig når modulerne redigeres, mens funktionalitet der ligger i objekter som blev oprettet ved applikationens start, kræver en genstart hvis en ændret udgave skal testes.

Fremgangsmåden med genstart for hver ændring er stadig almindeligt udbredt når der arbejdes med kompilerede sprog, men der er i almindelighed ingen grund til at gøre det i PILS.

6.1.4  Instantiering af moduler

Første gang et modul refereres i et kørende program, vil programmeringssystem instantiere det ved at parse og tolke udtrykket. Instansen huskes i programstroppen og vil blive genbrugt næste gang modulet refereres, hvis det ikke er redigeret i mellemtiden.

Når flere PILS programmer kører, har de hver deres egen programstrop og adskilt instantiering.

Når et modul ændres ved redigering, fjernes det fra instanshukommelsen i alle berørte programstropper, sammen med eventuelle andre modulinstanser som refererede modulet under instantiering. De gamle instanser er stadig brugbare, men nye referencer vil oprette nye instanser.

6.1.5  Datafiler og dataafhængige moduler

Hvis et modul under instantiering bruger data fra tekstfiler, XML-filer, regneark eller lignende, kan det være nødvendigt at geninstantiere modulet når filerne er ændrede, for at afspejle de ændrede data i PILS programmet. Det understøttes med funktionerne

            datafil (filnavn)

som opretter et filobjekt og desuden registrerer modulets afhængighed af den tidsstemplede fil hvis det sker under instantiering af modulet, og

            datafiltjek

som tjekker tidsstemplerne på samtlige datafiler der er registreret i programmet, og fjerner de modulinstanser fra instanshukommelsen der afhænger af filer hvis tidsstempel er ændret.

6.1.6  Modul- og programattributter via »dette«

Navnet  dette  kan bruges til udtræk af forskellige egenskaber ved det aktuelle modul eller program.

            dette program filnavn
            dette program sti
            dette program sprog

henter filnavn, sti og sprog af det program der har instantieret modulet, mens

            dette modul navn
            dette modul tekst
            dette modul udtryk
            dette modul sprog
            dette modul bibliotek

henter modulets attributter.

Fejlfinderen er afhængig af denne konvention, navnet  dette  (engelsk:  this ) bør ikke bruges til andre formål.

6.1.7  Sprogmoduler

Hvert modul har en tilknyttet sprogattribut som angiver et sprognavn, som kan bestå af flere led.

Modulets sprog findes ved at  [pils sprog]  sættes foran sprognavnet, og dette opfattes som en relativ modulreference, svarende til udtrykket

            @@ . ([pils sprog] & sprognavn)

For at processen ikke skal gå i ring, henviser sprognavnet  [system]  altid det sprogobjekt der blev brugt til opstart af PILS.

Typisk er sprogmoduler PILS sprogobjekter, eventuelt med nationale oversættelser af de ord der bruges i PILS systemet, eller med program- eller biblioteksspecifike ordvalgspræfixer som  j:  der bruges i bindingerne til  juce -biblioteket, eller eller til håndtering af  XML-baserede formater såsom OpenOffice dokumenter.

Alternativt kan man definere sin egen parser i et sprogmodul. Et simpelt eksempel er sproget  tekst , der afleverer modulteksten uden at parse den, hvilket er nyttigt hvis man vil gemme tekstdata i PILS biblioteker.

PILS redigeringen stiller de relevante sprogmoduler til rådighed i menuen.

6.2  Biblioteker

Et bibliotek er en fil der indeholder moduler og eventuelt references til andre biblioteker. Når et bibliotek indlæses, indlæses de biblioteker det refererer til også, hvis det ikke allerede er sket.

Et PILS program er et bibliotek der bruges som applikation.

6.3  Programstropper

Når brugeren åbner et program, indlæses det sammen med de biblioteker det bruger. Alle bibliotekerne sammenflettes så i et programbibliotek der indeholder alle moduler fra alle bibliotekerne.

Et programbilioteks levetid styres af programstroppen, som hægtes på alle de hovedvinduer der oprettes fra programmet. Programmet frigives når det sidste af dets vinduer nedlægges.

Når et bibliotek bruges som en del af et programbibliotek, rettes dets modulreferencer mod hele programbibliokteket.

Fire biblioteker – systembiblioteket, redigeringen,  platformsbiblioteket og et brugerkonfigurationsbibliotek – skal være indlæst for at PILS systemet fungerer. Systembiblioteket, redigeringen og platformsbiblioteket findes ud fra relative stier fra den eksekverbare fil, mens brugerkonfigurationsbiblioteket vælges ved opstart, ud fra computerens sprogindstillinger.

Hvis din computer er sat til dansk sprog, vil konfigurations- og sprogfilerne for dansk blive indlæst hvis de eksisterer, og  sig -funktionen – der bør bruges til alle meddelelser og navne i brugergrænsefladen af PILS programmerne – vil så bruge dette sprog og forsøge at præsentere meddelelser på noget der ligner dansk.

Indtil videre er kun dansk og engelsk understøttet.

6.4  Kommandolinjer og programinstanstjek

Ved opstart forsøger PILS fortolkeren at kontakte en eventuelt allerede kørende instans og videregive kommandolinjen til denne. Kun hvis det ikke lykkes, starter fortolkeren i en ny instans.

Det første fortolkeren gør er at danner PILS et objekt der skal kunne behandle kommandolinjerne. Dette objekt defineres af modulet  [pils system start]  i baduljeflikkeren.

Dette kompliceres en smule af at baduljerne oprettes og nedlægges løbende, så kanalen  [kanal: pils: kommandolinje]  bruges til at sende forespørgsler til en aktiv badulje med en passende instans af modulet  [pils system start] .

MS Windows-installationspakken definerer operationerne open og edit for PILS filtypen i registreringsdatabasen; de kendes fra hinanden ved at edit-kommandoen har  -edit  indskudt i kommandolinjen. Dette opfattes af en regel i  [pils system start]  og bevirker at redigeringen åbnes, uanset om der er defineret en anden handling i  [pils kør start] .

7  PILS redigering

PILS programmer er oprettes, redigeres og testes med PILS redigering – en simple tekstredigering med faneblade for de enkelte moduler og faciliteter til søgning og navigation i PILS bibliotekerne samt kald af testfunktioner.

Et PILS redigeringsvindue begrænser sig altid til et enkelt bibliotek. Hvis flere bibliotekern skal åbnes for redigering, bruges flere vinduer i hver sin badulje.

Redigeringen findes i PILS biblioteket  editor.pils  og kører i baduljen for det bibliotek der redigeres. Bibliotekerne kan ændre redigeringens opførsel ved at overskrive dens moduler.

Når et modul gemmes, fjernes de moduler fra instanshukommelserne der direkte eller indirekte refererede modulet under instantiering..

Bemærk: ændring af redigeringens moduler bør udføres med forsigtighed. Hvis et ødelagt redigeringsmodul gør at redigeringen ikke virker, kan redigeringen jo ikke bruges til at reparere skaden med, og man er henvist til enten at bruge en anden tekstredigering til reparation af fejlen, eller genetablere en tidligere udgave af PILS redigeringen.

7.1  Oprettelse af et PILS program

Opret en tom fil med type  .pils , åbn den og vælg det sprog du vil bruge.

Filen

             lib/pils/sprog/pils/ny.pils

bliver så kopieret oven i den tomme fil og åbnet.

Det sprog du vælger, bliver programmets sprog. Det bruges til at gemme og vise modulnavnene og vil blive valgt som sprog når du opretter ny moduler. Du kan skifte filsproget senere.

Bemærk: Brugergrænsefladens sprog styres af computerens sprogindstilling, ikke af programmets sprog. Hvis du skriver dine programmer med engelske indstillinger og kører dem på et system med danske sprogindstillinger, eller omvendt, vil PILS forsøget at oversætte brugerfladen.

7.2  Åbning af et eksisterende program

Ved dobbeltklik på en PILS fil kaldes funktionen  åbn  i modulet  [pils kør kommando åbn] . Hvis modulet ikke er overskrevet, vil det åbne filen for redigering.

Kommandoen  Rediger  kalder tilsvarende funktionen  rediger  i modulet  [pils kør kommando rediger] , der ligeledes åbner filen for redigering.

Under opbygning af et PILS program vil man typisk starte programmet ved at åbne redigeringen og køre en testfunktion. Når programmet skal afpudses og eventuelt offentliggøres, kan man så oprette et modul  [pils kør kommando åbn]  og definere en regel:

            { .åbn | ... }

Redigeringen kan stadig åbnes med Rediger-kommandoen.

På kommandolinjen bruges det engelske ord  -edit  (med bindestreg foran) til at differentiere Rediger-kommandoen fra  Åbn, som er standardkommandoen.

7.3  Redigering af moduler

Når du åbner redigeringen, vil det sidst ændrede modul i biblioteksfilen blive vist i et faneblad.

7.3.1  Simple redigeringsoperationer

Redigeringen som sådan er simpel – de sædvanlige tastaturgenveje kan bruges:

            Ctrl-C    kopiér

            Ctrl-X    klip

            Ctrl-V    indsæt

            Ctrl-Z    fortryd

            Ctrl-Y    gendan

            Ctrl-S    gem

Der gemmes altid til filen med det samme. Du kan ikke teste et modul uden at gemme det.

Forsøg på at gemme moduler med syntaksfejl afvises, og og fejlen markeres i teksten.

Tip: Hvis du vil gemme et modul med syntaksfejl, så sæt sproget til  tekst  som beskrevet herunder.

7.3.2  Navigering i modultræet

            Ctrl-M    viser programmets moduler som et træ.

Et modul åbnes ved at aktivere flasken i træet med dobbeltklik eller Enter -tasten. Brug  Esc -tasten til at lukke træet.

7.3.3  Oprettelse, flytning og sletning af moduler

Undermoduler kan oprettes med:

            Ctrl-N    opretter et undermodul til det aktuelle modul.

Moduler i roden kan oprettes ved at oprette et undermodul til et eksisterende modul og flytte det ned ved at bruge Modul-menuen:

            Modul->flyt->ned    flytter modulet med undermodules mod roden

            Modul->flyt->op->nabo    flytter modulet med undermodules op under nabomodul

            Ctrl-K    kopierer modulet med undermoduler til et andet navn.

            Ctrl-R    omdøber modulet og undermodulerne.

            Modul->slet    nedlægger modulet hvis det er tomt og ikke har undermodules.

            Modul->flyt->bibliotek->bibliotek    flytter til et andet bibliotek, med undermoduler.

Tip: Det er med vilje gjort lidt besværligt at nedlægge moduler, så man ikke mister kode hvis man rammer en forkert tast. Hvis du skal slette mange moduler som led i en oprydning, så opret et bibliotek og brug det som skraldespand: indstil dit bibliotek til at bruge skraldespanden, flyt så de uønskede moduler derover, fjern skraldespanden fra brugslisten og slet den.

7.3.4  Ændring af sprog for et enkelt modul

Vælg det ønskede sprog fra  Sprog -menuen, og gem modulet med  Ctrl-S .

Menupunktet system  refererer altid til det sprogobjekt der bruges til opstart af PILS. De andre punkter refererer til sprogmoduler.

Et sprog kan tilføjes til menuen ved at oprette et modul

            pils sprog navn

og skrive et sprogobjekt eller et udtryk der giver et sprogobjekt eller et objekt med en  læs -metode, som sprogene  tekst  og  tekstliste  der defineres af moduler i redigeringsbiblioteket –  tekst  giver simpelthen modulteksten, mens  tekstliste  opdeler den i linjer.

Sprogmoduler kan sættes på en gren i modultræet:

            job j pils sprog mit-sprog

definerer et sprog der kun kan bruges i moduler hvis navn begynder med  job . Hvis et moduls sprog er sat til et sprog der er specifik for en gren af modultræet, bør modulet ikke flyttes uden for grenen, da det vil gøre sprogindstillingen ugyldig.

juce bindingsbiblioteket – der pakker  juce-bindingerne ind – bruger et sådant specifikt sprog som knytter præfixet  j:  til den ordklasse der bruges til klasser og metoder i juce.

7.3.5  Ændring af programsproget

Programmets sprogindstilling kan ændres hvis man ikke er tilfreds med det oprindeligt valgte sprog. Først skal et passende sprogbibliotek tilføjes til listen af brugte biblioteker:

            <lib>/sprog/pils/sprog

Derefter bruges menupunktet  Fil->indstillingerne->sprog-> sprog  til at sætte programmets sprog.

Dette udløser en rekonstruktion af redigeringsvinduet for at sikre at modulnavne vises korrekt.

Ændring af programmets sprog påvirker ikke sprogvalget for moduler der allerede er oprettet.

7.4  Brug af PILS biblioteker

Et program eller bibliotek kan indstilles til at bruge andre PILS bibliotekerne med menupunktet

            Bibliotekerne->brug

Biblioteker kan indsættes i og fjernes fra listen med  Insert  og  Delete -tasterne. Insert -tasten  åbner en filvalgsdialog, og det valgte filnavn forkortes idet filtypen  .pils  udelades og begyndelsen af filstien erstattes af et af de følgende indledninger hvis muligt:

            <lib>/    – mappen  lib/pils/  i PILS installationen

            <doc>/    – brugerens dokumentmappe

            <.>/    – det nærværende biblioteks mappe

            <..>/    – den mappe der omgiver det  nærværende biblioteks mappe

Denne omskrivning bidrager til at opretholde referencerne når PILS programmer flyttes.

7.4.1  Søgning på tværs af biblioteker

Hvis du arbejder med mange biblioteker, vil du lejlighedsvis få brug for at søge på tværs af biblioktekerne for at finde en stump kode du har glemt hvor er.

            Ctrl-D    

Åbner et detektivpanel med to søgestriber – en høj og en lav – og et modultræ. Ud over de velkendte  Kun hele ord  og  Forskel på store og små bogstaver understøtter detektivpanelet en Strukturel søgemåde, som læser søgeteksten som et PILS udtryk og bruger den som en pasform Det gør det muligt at søge efter konstruktioner af en bestemt form, uden hensyn til hvordan de er skrevet.

Mens søgeteksten skrives eller redigeres, opdateres modultræet løbende, idet alle moduler med træffere samt vejene ud til dem markeres.

Når et modul med træffere vælges i træet, vises en liste med linjenumre for træfferne. Når linjenumrene markeres, markeres træfferne automatisk i redigeringsvinduer for de respektive biblioteker.

7.4.2  Om biblioteksfilernes opbygning

Bibliotekerne har filtypen  .pils  og er utf-8 tekstfiler med CR+LF (DOS/Windows-konventionen) som linjeskift. Om nødvendigt kan de redigeres with MS Windows Notepad eller lignende. Programstrophåndteringen ignorerer de ekstra bytes (BOM) som Windows-applicationer som Notepad indskyder forrest i utf-8-filer.)

Et PILS program eller bibliotek består af et eventuelt program-hoved og en række moduler, adskilt af markører:

             bibliotekshoved><:modulhoved><;modultekst><:modulhoved><;modultekst ...

idet enhver anden forekomst af  ><  kodes som  ><.  (med tilføjet punktum) for at undgå forveksling med markørerne. Deres orden har ingen betydning, men de sorteres dem ved rå sammenligninger af modulhovederne udskrevet som tekst, hvilket er bekvemt hvis der arbejdes med rå tekstredigering eller versionsstyringssystemer.

Modulerne består af et modulhoved og en tekst. Modulhovedet er en nodekonstant  [modul: .  ...]  skrevet i programfilens sprog med modulnavnet i forbenet og derudover ben for modulets attributter, som  sprog  og  tidsstempel . Modulnavnet er en liste af 1 eller flere PILS navne.

Bibliotekshovedet indeholder generel information om filen og skrives på engelsk. Et eventuelt  language -ben angiver programfilens sprog, og et  use -ben angiver referencer til andre biblioteker.

Når et bibliotek indlæses, lagres modulerne som ben i en node, idet benenes navne er modulnavnene mens deres værdier er modulhovederne med modulets tekst som forben i stedet for modulnavnet. Bibliotekshovedet lagres i halebenet.

7.5  Testregler

7.5.1  Lokale testfunktioner

Regler af form

            { .navn | :ok testudtryk }

kan aktiveres gennem et moduls Test -menu når modulet er i gemt/uændret tilstand. Aktivering af menupunktet kalder det pågældende navn, og hvis kaldet lykkes, vises resultatet i et  Resultat -vindue. Hvis resultate ikke skal vises, kan ansvarstageren udelades:

            { .navn | testudtryk }

En eventuel kviktest-funktion

            { .test | :ok testudtryk }

kan aktiveres med tastaturgenvejen

            Ctrl+T

som først gemmer modulet hvis det ikke allerede er gemt, og derefter kalder kvikttestfunktionen hvis den findes.

7.5.2  Testmoduler

Testmoduler definerer testfunktioner der kan bruges fra alle moduler i en gren af modultræet.

Lad os antage at du arbejder med et modul ved navn  [a b c] . Når du gemmer modulet eller trykker  Ctrl-T, afsøges disse moduler for test funktioner i den angivne rækkefølge:

            [a b c]
            [a b c test]
            [a b test]
            [a test]
            [test,]

Hver testfunktion bindes til det første modul hvor den findes.

Testfunktioner i modulet  [test,]  kan bruges hvorsomhelst. Men hvis man har mange testfunktioner, bør de lægges ud i de grene af modul træet hvor de er relevante, af hensyn til overskueligheden af testmenuerne.

7.5.3  Testprojekter

Hvis man skal teste et bibliotek der bruges af forskellige programmer,kan det være praktisk at indlægge et program i biblioteksbaduljen, så dette programs moduler er tilgængelige fra bibliotekets testfunktioner – men ikke fra andre programmer der bruger biblioteket. Et sådant program kaldes et testprojekt.

Åbn bibliotekets  Brug bibliotekerne -panel, tilføj testprogrammet, og markér det med  Ctrl-T . Testprogrammet vil nu blive medtaget i bibliotekets egen badulje, men ikke i de baduljer der bruger biblioteket.

8  Brugerflader med PILS

Brugerfladeprogrammering foregår gennem et PILS bibliotek  <pils>/english/system/juce/juce  som bygger på PILS bindinger til et underliggende system, som for nærværende er Juce, se http://www.rawmaterialsoftware.com . PILS bindingerne er genereret af et PILS program – plumming-generator.pils – og dækker det meste af Juce.

Det relevante PILS bibliotek medtages automatisk i alle PILS baduljer og tilgås gennem  platform  som er et alias for  @ [platform juce entry] , indgangene til biblioteket er undermoduler af dette modul.

8.1  Vinduer og ruder

            platform vindue [test] tekstrude tekst "Der var engang " udvalg (-1, -1) udpeg

Ovenstående udtryk opretter et vindue med overskriften  Test , udfylder det med en tekstrude med teksten "Der var engang " og sætter markøren i slutningen så man kan skrive videre på historien.

Brugerflader opbygges typisk ved at oprette et vindue og udstyre det med ruder der oprettes med metoder på vinduet eller mellemliggende ruder. Ruderne oprettes fra moduler  [platform juce parent xxx] , som indirekte er tilgængelige som metoder på vinduer samt de ruder hvor under-ruder giver mening.

Udvalget af ruder der understøttes af PILS og deres grænseflade er stadig under forandring. På langt sigt skal de fleste af dem gerne erstattes af ruder der er implementeret i PILS, baseret på PILS grafer. Derfor er der ingen udførlig dokumentation for dem, og hvis man har brug for at finde ud af hvilke metoder de understøtter, må man undersøge de relevante moduler i bindingsbiblioteket – og finde deres danske navne via den danske PILS sprognode hvis man programmerer i dansk PILS.

Bemærk at Juce biblioteket giver mulighed for at oprette kontroller uafhængigt af underliggende kontroller og vinduer, og omplacere dem – mens PILS ruder bør behandles som tilhørende det vindue eller den rude hvorfra de blev oprettet, og ikke bør omplaceres. Denne begrænsning er indført for at sikre korrekt frigivelse; Juce biblioteket er ikke konstrueret med bindinger til dynamiske sprog for øje og har ikke de bagindgange der er nødvendige for en finkornet styring af binderne. Derfor må PILS bruge en grovere metode: når en rude løsrives fra det underliggende vindue, frigiver PILS den automatisk..

8.2  Hændelser og udbyggere

For at reagere på brugerens gøren og laden skal brugerfladens objekter udbygges med PILS regler med operationen  når .

            ;vindue platform vindue [test];
            vindue tekstrude
            når {.ændret|:hvem :hvis hvem tekst <$* "dages ende."; vindue luk}
            tekst "Der var engang "
            udvalg (-1, -1) udpeg

Denne version lukker automatisk vinduet i det øjeblik teksten afsluttes med "dages ende." . Bemærk at udbygningen af tekstruden sættes på straks efter oprettelsen og refererer til ruden gennem  :hvem ‑binderen. For at sikre mod cirkulære referencer tillader PILS ikke at man udbygger objekter som der findes flere referencer til. Dette vil resultere i en fejl:

            ;tekstrude vindue tekstrude;
            tekstrude når {.ændret|:hvis tekstrude tekst <$* "dages ende."; vindue luk}

Her vil  når ‑operation fejle og nægte at udbygge ruden, idet dette ville resultere i en cirkulær reference.

9  PILS Grafer

PILS har indbygget et letvægts-vektorgrafikformat hvis faciliteter p.t. afspejler Juce-bibliotekets grundlæggende grafiklag.

— This HTML is generated by PILS from PILS sproget.odt