&kommander;s nya tolk

Michal Rudolf

    mrudolf@kdewebdev.org

Eric Laffoon

    eric@kdewebdev.org

2005-2008 Michal Rudolf Eric Laffoon
&FDLNotice;
Stefan Asserhäll

    stefan.asserhall@comhem.se

Översättare

# Dokumentation av ny tolk

Den nya tolken introducerades i &kommander; med version 1.2, utgiven tillsammans med KDE 3.4. Detta dokumentet gavs ursprungligen ut för att visa alla funktioner i den nya tolken. Den nya tolken är förval från och med &kommander; 1.3, utgiven med KDE 3.5.9, utom för program med huvudfönster skapade i &Qt; Designer. Eftersom den nya tolken har så mycket rikare möjligheter, saknar den gamla tolkens begränsning av nästling och lägger till så många nya funktioner, rekommenderar vi starkt att den används.

&kommander; själv beskrivs inte här. Se övriga dokument för att ta reda på vad &kommander; är till för, hur man skapar dialogrutor och hur grafiska komponenter hanteras under körning.

## Gammal tolk

Här jämför vi de två tolkarna. Även om vi förordar den nya för nästan alla syften, stöds den gamla fortfarande och är användbar, i synnerhet vid arbete med andra skriptspråk.

### Gammal tolk

Den gamla tolken var i själva verket en makrotolk. Bara strängar som börjande med @ kändes igen, tolkades lokalt och expanderades.

    @LineEdit1.setText(@ListBox.selection)

Alla underliggande funktioner (lokala variabler, uttryck, filhantering) var nödvändigt att göra i ett annat skriptspråk, som Bash. Även om avsikten med &kommander; är att stödja alla andra skriptspråk, vilket för närvarande är i viss mån möjligt, fanns det behov av ett snabbt, inbyggt skriptspråk som var garanterat flyttbart. Det största problemet med den gamla tolken är att &kommander; specialvärden utvärderas innan koden skickas till skriptspråket, vilket gör det omöjligt att använda dem i snurror och villkor.

Utvecklarna ansåg att Bash var långsamt och inte vänligt för nya användare, och den gamla tolken var ursprungligen Bash med DCOP-anrop. Paradoxalt nog orsakade det faktum att &kommander; är språkneutralt ett behov av att kunna göra mer än bara använda funktioner internt.

### Ny tolk

Den nya tolken är en fullständig tolk. Den tolkar hela skriptet, inte bara funktionerna. Eftersom vårt intresse är interaktion med det grafiska användargränssnittet, inte att sprida skriptspråk, gjorde vi vissa kompromisser. Resultatet är att &kommander;s skriptspråk bör vara användbart för de flesta grundläggande uppgifter, samt naturligt och enkelt att använda. Det finns också en funktionsbläddrare, som hjälper till att foga samman satser. Funktionsbläddraren är avsedd att göra &kommander; tillgängligt för fullständiga noviser i programmering. Det liknar vad man finner i Kspread för att hjälpa till att välja en funktion och fylla i parametrarna.

Om du vill använda utökade funktioner som finns i andra språk, kan du infoga dem i &kommander;-skriptobjekt inledda med teckenföljden #!. I dessa skript hjälper funktionsbläddraren till att infoga referenser till grafiska komponenter. Kom bara ihåg när funktionen används, att tolken utför en genomläsning för den gamla tolkens funktioner och en genomläsning för skriptet. Om du försöker ändra något i en grafisk komponent och läser den i mitten av ett skript, kanske du inte får vad du förväntar dig.

    #!/usr/bin/php

Följande funktionslista kommer från version 1.2

- lokala och globala variabler och associativa fält

- numeriska uttryck

- stränghantering

- diverse strukturella kommandon: if, while, for, foreach

- de flesta funktionerna från den gamla tolken

- direkt hantering av grafiska komponenter

- många ytterligare funktioner

- rimlig körningshastighet

- mottagning av parametrar från signaler i skriptslots

Denna listan är från version 1.3

- skicka parametrar och ta emot dem med skriptkörningsanrop

- returnera ett värde från ett skript

- skapa grafiska komponenter i farten

- ansluta signaler och slots i farten

- använda variabelalias för namn på en grafisk komponent

- enkla funktioner för indexerade fält

- direkt åtkomst av slots i en grafisk komponent

### Anropa den nya tolken

För att aktivera den nya tolken, ställ in egenskapen `useInternalParser` i dialogrutan till `true`. Man kan också aktivera den nya tolken i ett enskilt skript genom att skriva

    #!kommander

på första raden i skriptet. Observera också, att om du använder ett annat skriptspråk i ett skript med teckenföljden #!, aktiverar &kommander; automatiskt den gamla tolken för att kommunicera med dialogrutan.

    #!/bin/bash
    echo @Self.item(0)
    # returnerar första parametern som skickas till skriptet
    # echo $returvärde återgår till det anropande skriptet

## Funktioner i den nya tolken

### Typer

Varje värde har en av tre typer: sträng, heltal eller dubbelt flyttal. Typkonvertering är automatisk, och väljer den lämpligaste typen (om du till exempel adderar ett flyttal med ett heltal, blir resultatet ett flyttal). Om ett av värdena är en sträng, blir också resultatet det.

Ställen du kan råka ut för problem här är när ett numeriskt värde hämtas från en grafisk komponent och du försöker använda en matematisk funktion med det. Eftersom &kommander; använder `+` för att sammanfoga två textsträngar, kan det behandla `LineEdit1.text + 2` som `22` istället för `2`. Se konverteringsfunktionerna bland strängfunktioner för att undvika problem.

### Uttryck

Följande matematiska operatorer stöds: `+, -, *, mod,` . Vanliga parenteser stöds förstås också.

Alla sorters jämförelser stöds: `<`, `>`, `<=`, `>=`, `==`, `!=`. I stället för `!=` kan du också använda `<>`. Dessutom stöds de logiska operatorerna `and`, `or`, `not`, samt deras motsvarighet i C (`&&`, `||`, `!`).

För strängar kan operatorn `+` användas för sammanfogning av strängar.

Några exempel på giltiga uttryck:

    2+3
    -5 * (2 - 13 mod 3)
    "Listan har " + 12 + "objekt."

### Variabler

Variabler behöver inte deklareras. Så fort en variabel har används, anses den vara deklarerad. Typen hos en variabel känns igen automatiskt, och kan ändras senare.

Dessutom stöds associativa fält. De avbildar strängar till värden av godtycklig typ. För att deklarera ett sådant fält kan du helt enkelt lägga till några element i det, till exempel: `A["Quanta"] = "Webbeditor"`. Fält hanteras också av kommandot foreach och fältfunktioner.

Lokala och globala variabler stöds. Globala variabler markeras med ett inledande understreck. Alltså är `min_var` en lokal variabel, medan `_min_var` är global. Samma sak gäller för fält.

    a = 5
    b = 2 * 5 - (a + 1)
    c = "[Objekt " + b + "]"
    d["Min_nyckel"] = "Mitt_värde"
    d["Min_nyckel_2"] = 5

Att använda variabler för grafiska komponenter fungerar i stort sett som man kan förvänta sig. Det är användbart när grafiska komponenter läggs till i en tabell med en snurra.

    for i=0 to 10 do
      min_kombinationsruta = "ComboTable"+i
      createWidget(min_kombinationsruta, "ComboBox", "Form1")
    end

### Kommentarer

Du kan använda kommentarer i &kommander; med två traditionella kommentarformer från programspråk för radkommentarer. För användare som är noviser när det gäller programmering och som undrar vad traditionella former är, se nedan. Du kan kopiera och klistra in texten nedan i en initiering av en knapp eller dialogruta, och se hur kommentarer beter sig vid användning.

    // detta är en kommentar på en rad
    message_info("Hej allihop") // traditionellt första program
    // ovanstående kommentar ignoreras också - meddelanderutan gör det inte
    # detta är också en kommentar
    message_info("Detta meddelande visas")

Att använda följande flerraderskommentar fungerar inte, och gör att resten av komponentens körning misslyckas.

    /*
    Hej, det är meningen att detta ska vara en kommentar
    Ingenting i skriptet efter detta kommer att köras
    ANVÄND INTE DENNA KOMMENTARTYP I KOMMANDER!
    */

### Inbyggda globala variabler

&kommander; har några inbyggda globala variabler som kan vara praktiska.

- `_ARGS` - argumentsträngarna som skickas till dialogrutan när den visas.

- `_ARGCOUNT` - antal argument som skickades. De kan hämtas som `ARG1` till `ARGn` där n är det totala antalet argument som skickades.

- `_KDDIR` - katalogen där dialogrutan kördes. &kommander; använder normalt din hemkatalog, eller en ändrad katalog om tillfrågad om dess arbetskatalog. Det är användbart för att spara och läsa filer med &kommander;-filen.

- `_NAME` - det finns ingen anledning att använda denna, gör alltså inte det

- `_PID` - processidentifierare som den aktuella dialogrutan körs med - också tillgänglig som bara pid. Undvik användning av detta namn för dina egna variabler!

- `_VERSION` - praktiskt om du vill visa vilken version av &kommander; som kör

### Skicka argument i &kommander;

Du kan skicka argument via skriptparametrar, signaler och slots, kommandoradsparametrar och DCOP. Låt oss ta en titt på skript. Anropa skript på följande vis:

    resultat = Skriptobjekt1.execute("Hej allihop")
    debug(resultat)

Inne i skriptet kan det se ut så här:

    var = str_upper(Self.Item(0))
    return(var)

Nu får du ett returvärde i meddelandeloggen via standardfelutmatningen som är HEJ ALLIHOP.

Att ta emot en signal som är ansluten till en skriptslot fungerar på samma sätt. Self.Item(0) är parameter ett och så vidare. Du kan hämta antal argument som skickas via Skriptobjekt.count.

Kommandoradsparametrar tillåter namngivna eller namnlösa argument. Namnlösa ser ut som

    kmdr-executor mitt_program.kmdr 100 röd

Här blir _ARG1 = 100 och _ARG2 = röd. En konstighet är att när strängar med mellanslag skickas som argument måste de citeras. Används dialogkommandot blir saker och ting mer komplicerade, eftersom hela argumentsträngen måste skickas som en sträng, alltså inom citationstecken.

    dialog("min_dialog.kmdr", 100+" \"Hej allihop\"")

Det returnerar _ARG1 = 100 och _ARG2 = Hej allihop. Utan skyddade citationstecken hade du fått _ARG2 = Hej och _ARG3 = allihop. Att använda namngivna parametrar är rätt trevligt, och potentiellt mindre förvirrande.

    dialog("min_dialog.kmdr", "xantal=100 xcitat=Hej allihop")

Nu kan du komma åt dem med de globala variablerna _xantal och _xcitat.

DCOP kan vara komplicerat, vilket är orsaken till att vi rekommenderar att använda de verktyg vi utvecklar för att göra det möjligt att skapa DCOP för &kommander;-fjärrdialogrutor med något som liknar en funktionsbläddrare. Här är ett exempel på ett DCOP-anrop som skickas från en dialogruta öppnad av ett &kommander;-fönster. Eftersom den vet vem som skapat den, kan den skicka tillbaka information medan den är öppen, och fritt komma åt alla fönstrets funktioner med undantag av slots. Det kan naturligtvis göras internt med ett skript som kan anropas externt, så i praktiken finns det inga begränsningar av vad som är möjligt.

    dcop("kmdr-executor-"+parentPid, "KommanderIf", "setText(QString,QString)", "StatusBar8", "Hej")

Låt oss titta på detta en del i taget. Först av allt lägger vi till parentPid to "kmdr-executor-" eftersom vi inte antar att ett &kommander;-fönster gjorde anropet. Du skulle kunna använda det med Quanta, Kspread eller vad som helst. Därefter adresserar vi KommanderIf, som är ett bra gränssnitt för slutanvändare som har städats. Vi hoppas att fler program till sist börjar använda ett bra gränssnitt för integrering när KDE går från DCOP till DBUS i KDE4. Nästa parameter, "setText(QString,QString)", är viktig eftersom den anger prototyp för tillåtna parametrar. Annars skulle inte &kommander; kunna validera anropet. Utan definitionen av DCOP-anropet som används får du ett fel. Återstående parametrar är förstås de som skickas. Vi rekommenderar att du tittar på program med `kdcop` för att se hur det fungerar, och övar på att skicka DCOP-anrop från skalet för att få rätt syntax.

## Kommandon

Diverse strukturkommandon stöds. De kan nästlas fritt.

Det finns också tre särskilda kommandon: `exit`, `break` och `continue`. Det första avsluta skriptets körning och återgår. Det andra avslutar pågående block (while, for eller foreach), och det tredje avslutar bara pågående varv, och fortsätter från snurrans början.

### if

Kommandot `if` har följande syntax:

`if` villkor `then` kod `elseif` villkor `then` kod `else` kod `endif`

Både delarna `elseif` och `else` är valfria. Villkor är vilket uttryck som helst. Kod utförs om villkoret är sant. Det betyder:

- skilt från noll för heltal och flyttal

- inte tom för strängar

    if a * 2 > 7 then
       b = 1
    elseif a < 0 then
       b = 2
    elseif
       b = 0
    endif

### while

`while` villkor `do` kod `end`

Villkor beräknas om varje gång snurran utförs.

    while i < 15 do
      i = i + a
    end

### for

Kommandot `for` har följande syntax:

`for` variabel `=` startvärde `to` slutvärde `step` uttryck `do` kod `end`

Snurran utförs med början på startvärde och avslutas när variabelns värde är större än slutvärde. Om `step` anges, ökas variabelns värde med det angivna värdet istället för `1`.

    for i = 1 to 20 step 5 do
      a = a + 2 * i
    end

### foreach

Kommandot `foreach` har följande syntax:

`foreach` variabel `in` fält `do` kod `end`

Snurran utförs för varje värde i det givna fältet. För varje varv tilldelas variabeln nästa värde i fältet.

    summa = 0
    foreach i in mitt_fält do
      summa = summa + mitt_fält[i]
    end

## Funktioner

De flesta av den gamla tolkens funktioner stöds av den nya tolken. Dessutom har några nya funktioner lagts till.

### Strängfunktioner

Strängfunktionerna är samma som i den gamla tolken, den enda skillnaden är att deras namn inleds med `str_` istället för `@String.`

- `str_length(sträng)` - returnerar längden av sträng

- `str_contains(sträng, text)` - returnerar 1 om sträng innehåller text

- `str_find(sträng, text, start)` - returnerar positionen för den första förekomsten av text i sträng, det valfria värdet start anger sökningens början

- `str_find(sträng, text, start)` - returnerar positionen för den sista förekomsten av text i sträng, det valfria värdet start anger sökningens början

- `str_left(sträng, antal)` - returnerar första antal tecken av sträng

- `str_right(sträng, antal)` - returnerar sista antal tecken av sträng

- `str_right(sträng, start, antal)` - returnerar delsträngen av sträng med början på start som innehåller antal tecken (eller allt till strängens slut om den sista parametern inte anges)

- `str_remove(sträng, text)` - returnerar sträng med alla delsträngar som är lika med text borttagna

- `str_replace(sträng, text, text2)` - returnerar sträng med alla delsträngar som är lika med text ersatta med text2

- `str_lower(sträng)` - returnerar sträng konverterad till små bokstäver

- `str_upper(sträng)` - returnerar sträng konverterad till stora bokstäver

- `str_section(sträng, avdelare, start, slut)` - returnerar delsträngen som innehåller lämpliga delar av sträng bestämda av avdelare. Om inget slut anges, returneras en ensam del från start

- `str_args(sträng, ...)` - returnerar sträng med `%1`, `%2`, `%3` ersatta med efterföljande parametrar.

- `str_isnumber(sträng)` - returnerar 1 om sträng är ett giltigt tal

- `str_isempty(sträng)` - returnerar 1 om sträng är tom

- `str_toint(sträng, förval)` - returnerar sträng konverterad till ett heltal. Om konverteringen inte är möjlig, returneras det valfria värdet förval

- `str_todouble(sträng, förval)` - returnerar sträng konverterad till ett flyttal. Om konverteringen inte är möjlig, returneras det valfria värdet förval

### &kommander;-funktioner

De flesta &kommander;-funktioner stöds. Vissa (som `expr`) blev föråldrade av den nya tolken och är inte tillgängliga.

- `debug(sträng, ...)` - skriver ut alla parametrar på standardfelutmatningen

- `echo(sträng, ...)` - skriver ut alla parametrar på standardutmatningen

- `dcop(sträng, ...)` - anropar en DCOP-funktion

- `exec(sträng, skal)` - kör ett externt program (med användning av valfritt skal), blockerar körningen av aktuell dialogruta till programmet som skickas som parameter avslutas, returnerar programmets utmatning

- `i18n(sträng)` - markerar sträng för framtida översättning

- `env(sträng)` - returnerar värdet av en miljövariabel

- `readSetting(nyckel, förval)` - returnerar ett värde lagrat i inställningsfilen med angiven nyckel. Om ett sådant värde inte finns, returneras förval

- `writeSetting(nyckel, värde)` - skriver paret nyckel och värde i inställningsfilen

Nytt i &kommander; 1.3

- `execBackground(sträng, skal)` - kör ett externt program (med användning av valfritt skal) i bakgrunden, utan att blockera aktuell dialogruta, i motsats till funktionen `exec` ovan, returnerar den inte programmets utmatning.

- `return(värde)` - returnerar ett värde till anropande objekt (skript, knapp ...)

- `createWidget(komponentnamn, komponenttyp, skapare)` - skapar en ny grafisk komponent. Därefter kan du till exempel lägga till den i en tabell eller verktygslåda, och använda `min_komponent.show(true)` för att göra den synlig. Om du lägger till en ny grafisk komponent i formuläret, måste du ta hänsyn till layout. &kommander; skapar inte en layout i farten, eller justerar positioner bildpunkt för bildpunkt (i de flesta fall). Det är förvirrande till och med vid utveckling med C++. Vi rekommenderar att du använder en gruppruta och skapar en layout i dialogrutan för att få bäst kontroll.

- `connect(avsändare, signal, mottagare, slot)` - anslut en signal i en grafisk komponent till en slot. Se anslutningsdialogrutan och välj liknande komponenter för att se möjligheterna. Om en signal till exempel ser ut som `execute(const QString&)` är det exakt vad som måste finnas här inom citationstecken.

- `disconnect(avsändare, signal, mottagare, slot)` - ta bort anslutningen som anges ovan. Återigen är exakt riktig syntax helt nödvändig.

- `widgetExists(komponentnamn)` - kom ihåg att du nu kan använda ett variabelnamn för att ange en grafisk komponent . Använd det när grafiska komponenter som skapats ska kommas åt, för att försäkra dig om att de finns. Att anropa en grafisk komponent som inte finns orsakar naturligtvis ett fel.

### Fältfunktioner

De flesta fältfunktioner stöds. Vissa (som `value`) blev föråldrade av den nya tolken och är inte tillgängliga. Den enda skillnaden är att deras namn inleds med `array_` istället för `@Array.`

På grund av begränsningar i tolken, måste fältnamn för närvarande anges som strängar, till exempel `array_count("Mitt_fält")`.

- `array_clear(fält)` - tar bort alla element från fält

- `array_count(fält)` - returnerar antal element i fält

- `array_keys(fält)` - returnerar en sträng som innehåller nycklarna i fält åtskilda med radslut. Observera att om du importerade en skalär (nycklar utan värden) i ett fält med &kommander; skulle du inte kunna komma åt den med `array_values("mitt_fält")` som du kanske förväntade dig (eftersom den bara verkar ha värden) utan skulle istället behöva använda `array_keys("mitt_fält")`. Du kanske finner att ett bättre val i detta fall är att använda de nya indexerade fälten som beskrivs nedan.

- `array_values(fält)` - returnerar en sträng som innehåller värden i fält åtskilda med radslut

- `array_tostring(fält)` - returnerar en sträng som innehåller hela fält som rader med par av nyckel och värden åtskilda med tabulatortecken

- `array_fromstring(fält, sträng)` - läser fält från sträng (oftast skapad av funktionen `array_tostring`)

- `array_remove(fält, nyckel)` - tar bort objektet med nyckel från fält

Här är ett exempel på fälthantering:

    array_fromstring("mitt_fält", "1\tA\nandra\tB\n3\tC")
    foreach key in mitt_fält do
      debug("mitt_fält[" + key + "]= " + mitt_fält[key])
    end

Det skriver ut följande på standardfelutmatningen. Det syns att det inte finns någon garanti om elementens ordning i fältet, samt att nycklarna är strängar, inte tal.

    mitt_fält[1]= A
    mitt_fält[3]= C
    mitt_fält[andra]= B

Ett annat exempel på fält utan nycklar:

    array_fromstring("mitt_fält", "A\nB\nC")
    foreach nyckel in mitt_fält do
      debug(nyckel)
    end
    debug("Fältelement:\n" + array_keys("mitt_fält"))

Det ger resultatet:

    A
    B
    C
    Fältelement:
    A
    B
    C

Nytt i &kommander; 1.3

- `array_indexedFromString(fält, sträng, avdelare)` - Det här kompenserar för att &kommander; inte har indexerade fält. Den skapar ett fält med ett nollbaserat sekvensiellt index. Kom ihåg att använda citationstecken för fältnamnet och eventuella strängar som inte representeras av en variabel. Argumentet avdelare är valfritt och har det förvalda värdet "\t" (tabulator), och används för att åtskilja fält vid läsning och skrivning av tabeller, fält eller grafiska detaljkomponenter. Kom ihåg att fältindex inte själv tvingar att några regler följs, det är precis som om du skapade det med en for-snurra, bara bekvämare.

- `array_indexedInsertElements(fält, nyckel, sträng, avdelare)` - Funktionen ingår i uppsättningen med funktioner för indexerade fält, och gör det möjligt att infoga element i fältet och samtidigt behålla ett index som är sekventiellt, i en följd, och unikt. Ange indexnyckel att börja, textsträng och hur den skiljs åt. Elementen läggs till och alla efterföljande indexnummer skiftas med antalet tillagda element.

- `array_indexedRemoveElements(fält, nyckel, antal)` - Det här gör det möjligt att ta bort element från ett indexerat fält och undvika indexhål. Ange nyckel att börja med och valfritt hur många element som ska tas bort. Förvalt antal är ett. Slutresultatet blir ett omindexerat fält utan de borttagna elementen.

- `array_indexedToString(fält, avdelare)` - Det här gör det möjligt att konvertera ett indexerat fält tillbaka till en sträng, vilket är särskilt användbart för grafiska detaljkomponenter. Om du till exempel visar resultatet av en databasfråga i Trädkomponent1 med sex kolumner, kan du använda `Trädkomponent1.selection` för att hämta den markerade raden. Den är avdelad med tabulatortecken, och du skulle kunna titta på det femte elementet genom att använda `str_section(Trädkomponent1.selection, "\t", 4)` (kom ihåg att den är nollbaserad). Det är bra för att läsa ett värde, men om du vill ändra det märker du att du måste göra en hel del ytterligare arbete. Efter strängen har delats, måste du sätta ihop den igen med `val1+"\t"+val2...`. Genom att använda indexerade fält kan du redigera det femte elementet på följande sätt:

      idx = Trädkomponent1.currentItem
      array_indexedFromString("z", Trädkomponent1.selection)
      z[4] = "nytt värde"
      Trädkomponent1.removeItem(idx)
      Trädkomponent1.insertItem(array_indexedToString("z"), idx)

  Observera att bara två korta rader har lagts till för att åstadkomma det. Det är mycket välkommet vid databasanvändning.

### Filfunktioner

Alla filfunktioner stöds. Den enda skillnaden är att deras namn inleds med `file_` istället för `@File.`

- `file_read(namn)` - returnerar innehållet i filen namn

- `file_write(namn, ...)` - skriver alla argument till filen namn

- `file_append(namn, ...)` - lägger till alla argument sist i filen namn

### Inmatningsfunktioner

Dessa funktioner visar en dialogruta som låter användaren mata in ett värde. De kan kommas åt genom att använda `@Input.` i den gamla tolken. För de flesta funktioner är alla parametrar valfria, undantagen är `input_text` som kräver 2 parametrar och `input_value` som kräver 5 parametrar.

- `input_color(rubrik, förval)` - returnerar färg på formatet #RRGGBB

- `input_text(rubrik, beteckning, förval)` - returnerar text inmatad av användaren

- `input_value(rubrik, beteckning, förval, min, max, steg)` - returnerar värden som matas in av användaren

- `input_direktory(startkatalog, filter)` - returnerar en katalog som valts av användaren

- `input_openfile(rubrik, beteckning, förval)` - returnerar en befintlig fil som matas in av användaren

- `input_savefile(rubrik, beteckning, förval)` - returnerar en fil som matas in av användaren (om filen redan finns, krävs en bekräftelse)

- `input_openfiles(rubrik, beteckning, förval)` - returnerar en sträng med befintliga filer som matas in av användaren åtskilda med radslut

### Meddelandefunktioner

Dessa funktioner visar meddelanden för användaren, eller ber användaren bekräfta en åtgärd. Använd istället `@Message.` i den gamla tolken.

- `message_info(text, rubrik)` - visar informationstext

- `message_error(text, rubrik)` - visar feltext

- `message_warning(text, rubrik, knapp1, knapp2, knapp3)` - visar en fråga med en varning och upp till tre knappar. Numret på den valda knappen returneras. Om inga knappnamn anges, visas `Ja` och `Nej`

- `message_question(text, rubrik, knapp1, knapp2, knapp3)` - visar en fråga och upp till tre knappar. Numret på den valda knappen returneras. Om inga knappnamn anges, visas `Ja` och `Nej`