====== OIL neboli skriptovací jazyk ORBISu ======
===== Úvod =====
Vlastní slovo OIL neznamená olej, jak by se mohl mnohý anglofon domnívat, ale zkratku z **Orbis Interactive Language**. Je to jazyk jednoduchý, vlastně to skoro ani není jazyk a už vůbec není interativní.
OIL je **interpret**. To znamená, že zdrojový kód se předem nekompiluje, ale provádí se postupně řádek po řádku. Což zároveň znamená, že na případné chyby se přijde až k nim jazyk dojde a pokusí se je provést.
===== Základní informace =====
Základem příkazového řádku OILu je pohyb po objektovém modelu ORBISu. Ten se odehrává **relativně** vůči **aktivnímu elementu**, kterým je, pokud není jinak řečeno **WORLD**. Na rozdíl od jiných jazyků je jediným akceptovatelným operátorem v řádku "**=**", tedy přiřazení, běžné operátory jsou samozřejmě také k dispozici, ale jen v rámci výrazu, který je vždy v samostatném bloku.
Změnu aktivního elementu je možné provést nastavením systémové proměnné ''**ACTIVEELEMENT**''. každý řádek má výsledek kterým je buď hodnota, nebo seznam hodnot. Cesta zápisu může být i **absolutní**, pak řádek začíná slovem **World**.\\
Příkazový řádek tedy v souhrnu disponovat těmito jevy:
* definice proměnné stylem '' Jméno: typ '' např. W: TElement
* Cesta k elementu ObjecModel.WORK
... zápis vrátí číselnou adresu elementu WORK v Objektovém modelu světa
* Cesta k vlastnosti elementu ObjecModel.WORK.Count
...Vrátí počet elementů WORK
* Výraz { 2 * 3}
...Výsledkem je 6
* Funkce
ObjecModel.WORK.CreateElement(TOwnerEntity, Karel)
ObjecModel.WORK.CreateElement(TOwnerEntity, Bohdan)
...pod WORK vytvoří Entity Karel a Bohdan a poslední řídek vrátí adresu Bohdana
* Přiřazení
ObjecModel.WORK.Karel.Name = 'Rudolf'
...Změní hodnotu vlastnosti Name, tedy přejmenuje Karla na Rudolfa
* Seznam ObjecModel.WORK.Element.Name
...Vrací seznam jmen všech elementů pod WORK, výsledkem bude ''Rudolf, Bohdan''
* a kombinace těchto stavů:
W: TElement // definice proměnné W
i: integer // a celočíselné proměnné i
S: string // textové S
Bobo: TElement // a nakonec objektového Boba
W = ÓbjecModel.WORK // do W přiřadíme element WORK
W.CreateElement(TOwnerEntity, Karel) // ve WORK vytvoříme entitu Karel
// to samé provedeme s Bohdanem ale jinak
Bobo = Create(TOwnerEntity, Bohdan) // do proměnné Bobo přiřadíme vytvořeného Bohdana
W.NextElement = Bobo // a vložíme ho do WORK aby se stal součásti modelu
W.Karel.Name = 'Rudolf' // Karla přejmenujeme na Rudolfa
i = {W.Count * 2} // do i přijde dvojnásobná hodnota počtu elementů WORK, tedy 4
S = cat(" Dvojnásobný počet elementů ve WORK je ", i)
// v S je text Dvojnásobný počet elementů ve WORK je 4
===== Syntaxe =====
==== Základní syntaxe ====
OIL **není case sensitiv**, tedy nezáleží na velikosti písmen. Všechny **konstantní hodnoty** jsou chápány jako **text** a tedy uzavřeny v **apostrofech** nebo **uvozovkách** (toto pravidlo neplatí ve výrazech). Rozdíl je jen v tom, že u apostrofů se automaticky mažou vnější mezery, u uvozovek ne.\\
''S: string = ' 12 ''' nastaví proměnnou S na %%"12"%% \\
zatímco\\
''S: string = %%" 12 "%%'' nastaví proměnnou S na %%" 12 "%% \\
Cesta k elementu, resp. k jeho vlastnosti je sekvence jmen elementů v modelu oddělaná tečkami. \\
''ObjectModel.Karel.Count''\\
Výrazy jsou (až na vyjímky) ohraničené složenými závorkami **{}**. \\ Těmi vyjímkami je automaticky chápaný výraz v kostrukcích jako např. **IF //výra//z THEN**, **WHILE //výraz//** a podobně.
==== kombinace skriptů ====
Cestu k souboru tak jak je uváděna v následujících řádcích je možné zapisovat relativně ... pokud leží pod adresářem, ze kterého je spuštěna řídící aplikace (pak začíná přímo názvem prvního podřazeného adresáře). Je samozřejmě možný i absolutní zápis, začínající označením disku s dvojtečkou a lomítkem ... c:\
=== Volání jiného skriptu ===
Z příkazového řádku OILu lze volat jiný skript pomocí funkce CALL \\
''CALL(Led\Review.oil)'' tohle např. způsobí přezobrazení hlavního mapového okna aplikace\\
parametr cesty se zde uvádí bez apostrofů či uvozovek. Což zároveň znamená, že se dá použít jen pevná hodnota a ne proměnná či jiný parametr. \\
Pokud vyvstane tato potřeba, lze to vyřešit vlastností hlavního nástroje: \\
s: string = 'LED\Review.oil'
MainTool.ExecuteScript=S
Je potřeba si uvědomit, že volaný skript nic neví o proměnných aktuálního OILu a ani aktuální OIL nevidí proměnné volaného skriptu, takže je-li potřeba předat nějaké parametry, musí se to udělat jinou cestou.
=== Vložení skriptu do aktuálního ===
Funkce **INCLUDE** umožňuje vložit jeden skript do jiného a tím i používat knihovnu často používaných funkcí a proměnných bez opakování v každém OILu.\\ ---
Syntaxe je \\
''INCLUDE('Cesta ke skriptu')''\\
na rozdíl od funce CALL je parametr cesty uveden v apostrofech nebo uvozovkách, umožňuje tedy aplikaci proměnné. \\
Příklad: \\
Include('LED\ProtoUtils.oil')
CheckProto()
M.NextText="Ahoj světe!"
vloží OIL pro práci s oknem protokolu, funkce CHeckProto ho otevře a poslední řádek vypíše text.
===== Komentáře =====
Komentáře, neboli poznámky lze tvořit několika způsoby:
* '';'' středník komentuje celou řádku, je-li uveden na začátku
* ''%%//%%'' dvojité lomítko ... kdekoliv, komentuje text od něj až do konce řádku
* ''%%*%%'' hvězdička (asterisk) ...blokový komentář, komentuje více řádků až do příštího výskytu, je-li uvedena na začátcích řádku (jako první znak)
===== Proměnné =====
==== Definice ====
Proměnné je možné definovat kdekoliv v kódu, každopádně před jejím prvním použitím. Jejich název by neměl obsahovat mezery a doporučoval bych i šetřit s diakritikou a použitím jiných záhadných znaků. Existuje jen jedna vyjímka při které se proměnná, neexistuje-li, vytváří automaticky a to je v hlavičce cyklu **for**\\
Definice proměnné se provádí následujícím zápisem:
jméno_proměnné: typ
// takže například
S: string // textová proměnná
D: float // desetinné číslo
==== Typy ====
^ Název typu ^ Popis ^
| char | jednoznaková textová hodnota |
| string | text |
| boolean | logická proměnná true nebo false |
| integer | celočíselná hodnota |
| float | desetinné číslo |
| datetime | datum a čas |
| TElement | objekt, uvedená může být jakákoliv třída která je potomkem elementu, TElement platí obecně |
==== Použití proměnných ====
Proměnné lze používat jednoduše, případné přetypování probíhá automaticky, pokud je to možné.\\
Např.
S: string=" 3"
i: integer
i=S
\\
nastaví do S text obsahující 3 a následně vloží do i celočíselnou hodnotu 3.\\
Proměnná je zároveň i zakukleným objektem, který disponuje vlastnostmi umožňující delikátnější komunikaci s nimi, tedy číst i nastavovat pomocí nich hodnoty:
^ Vlastnost ^ Použití ^
| AsChar | pokusí se převést hodnotu proměnné jako znak |
| AsString | Vrací hodnotu jako text |
| AsBoolean | podobně |
| AsInteger | podobně |
| AsFloat | podobně |
| AsDatetime | podobně |
| Value | stejný typ jako sama proměnná |
Příklady:
F: float="0,04568156" // u desetinného čísla je jedno zda použijeme jako oddělovač tečku nebo čárku
F // hodnota F je nyní v exponenciálním tvaru, tedy 4.56815600000000E-0002
// protože není exponenciální podoba vhodná pro čitelné výpisy, použijeme raději konstrukci
F.Value // a ejhle, objeví se původní velmi lidské 0,04568156
nebo
CH: char="A" // v CH je nyní znak A
CH.AsInteger = {CH.AsInteger + 1} // pomocí výrazu zvýšíme celočíselný kód znaku o jedna a vrátíme do CH
CH // a výsledkem operace je písmeno B
===== Výrazy =====
Výrazy slouží k aritmetickým nebo porovnávacím operacím nad hodnotami představovanými objekty (elementy), jejich vlastnostmi nebo obecně proměnnými. Nepoužívají se striktně jenom ve skriptech, ale i mimo ně (např funkce hledání výrazem v LEDu).
==== Syntaxe výrazu ====
Výraz v rámci OILu je uzavřen složenými závorkami... ''**{..}**''. Výjimkou je hlavička ''**IF**'' a ''**WHILE**'' ... tam je samozřejmé, že se jedná o výraz a závorka se nepoužívá. Další vyjímkou je samostatné použití například v dialogu "Hledání ve vrstvách výrazem" v LEDu. Pak se ve výrazu nevyskytují žádné proměnné, ale spouští se na každý testovaný objekt a je možné v něm používat přímo vlastnosti testovaného objektu.\\
Výraz, stejně jako zbytek OILu není case senzitivní, takže velikost písmen nerozhoduje.\\
Další syntaktická pravidla jsou ale mnohem přísnější:
* Na rozdíl od OILu, kde všechny hodnoty jsou jako text a hodnoty uváděny v apostrofech nebo uvozovkách, v rámci výrazu je potřeba rozlišovat typy a **čísla** psát jako čísla a **text** jako text.
* **Text** (string) je v apostrofech, tedy 'Karel'
... uvozovky způsobí syntaktickou chybu
* **Desetinná čísla** (float) používají jako oddělovač tečku, tedy 14.156
* **Typová kontrola** ... existuje, takže pokud chceme provádět operace s hodnotami různých typů, musíme je pomocí adekvátních funkcí přetypovat
S: string = '3'
i: integer
i = {S + 2} // tohle by způsobilo chybu, nemůžem sčítat text a číslo, napíšeme tedy
i = {S.AsInteger + 2} // přetypování pomocí vlastnosti proměnné
// nebo
i = {ToInteger(S) + 2} // přetypování pomocí funkce
// Každopádně v i je teď hodnota 5
* **Závorky ()** umožňují oddělovat části výrazu a měnit tak prioritu jeho provádění, posloupnost vykonávání částí výrazu je jinak dané i prioritou operátorů která je v pořadí [^], [*,/,DIV], [+,-], [AND, OR]
{3 + 2 * 2} // zde je výsledek 7, nejprve se násobí, pak sčítá
{(3 + 2) * 2} // zatímco tady 10 - provede se součet v závorce a pak násobení
==== Proměnné a konstanty ====
Jak už možná bylo řečeno, ve výrazu je možné použít všechny proměnné definované kdekoliv skriptu, jen je zde třeba respektovat jejich typ. Specialitou je několik konstant existujících právě jen ve výrazu:
^ Konstanta ^ Použití ^
| NIL | používá se pro zjištění inicializace objektu např ''A = nil'' je pravdivé, pokud není vytvořen |
| PI | float ... Ludolfovo číslo, tedy 3.141... atd |
| NOW | datetime ...je vlastně funkce bez závorek, vracející momentální čas na úroveň vteřin |
| TRUE | boolean ...logická hodnota ANO, tedy pravda |
| FALSE | boolean ...logická hodnota NE, tedy nepravda (někdo by řekl lež) |
==== Operátory výrazu ====
Operátor je kouzelné slovo které provede operaci s jednou nebo více hodnotami
^ Operátor ^ Použití ^ Výsledek ^
| %%^%% | mocnina - operand a exponent tedy např ''2 %%^%% 3'' je 2 na třetí, čili 8 |float nebo integer |
| %%*%% | násobení např. ''2 %%*%% 3'' je 2 krát 3 tedy 6 |float nebo integer |
| / | dělení ... ''5 / 2'' tedy 5 děleno 2 je 2.5 |float |
| DIV | celočíselné dělení ...totéž jen s celými čísly, ne desetiny |integer |
| MOD | zbytek po celočíselném dělení ...''5 mod 3 = 2 10 mod 3 = 1'' |integer |
| = | "rovná se" ...True pokud jsou obě hodnoty stejné |boolean |
| > | "větší než" ...True pokud jsou je první hodnota větší než druhá |boolean |
| > | "menší než" ...True pokud jsou je první hodnota menší než druhá |boolean |
| >= | "větší nebo rovno" ...True pokud je první hodnota větší nebo rovna druhé |boolean |
| <= | "menší nebo rovno" ...True pokud je první hodnota menší nebo rovna druhé |boolean |
| <> | "různé" ...True pokud je první hodnota jiná než druhá |boolean |
| AND | "a zároveň" logický součin ...True pokud platí obě strany výrazu |boolean |
| OR | "nebo" logický součet ...True pokud platí alespoň jedna strana výrazu |boolean |
| NOT | logický zápor ...převrací logickou hodnotu výrazu za sebou True - false |boolean |
| IS | objekt vlevo je potomkem třídy vpravo zapsané jako text: ''E is 'TElement'''|boolean |
| CONTAINSTEXT | Text vlevo obsahuje text vpravo - velikost znaků nerozhoduje |boolean |
| STARTSTEXT | Text vlevo začíná textem vpravo - velikost znaků nerozhoduje |boolean |
| ENDSTEXT | Text vlevo končí textem vpravo - velikost znaků nerozhoduje |boolean |
| MATCH | Text vlevo odpovídá regulárnímu výrazu vpravo (taky text) |boolean |
Poslední operátor, **MATCH** porovnává textovou hodnotu s regulárním výrazem, více o regulárních výrazech jako o zajímavém prostředku pro analýzu textové hodnoty se můžete dozvědět třeba [[https://www.regularnivyrazy.info/regularni-vyrazy-zaklady.html|na této stránce v češtině]], Případně si lze regulární výraz poskládat a otestovat [[https://regex101.com/|na stránce regex101 v angličtině]].
==== Funkce výrazu ====
funkce je uvedena klíčovým slovem za nímž následuje v jednoduché závorce jeden, výjimečně dva parametry, přičemž prvním parametrem je měněná hodnota, druhým mohou být pravidla provedení.
^ Funkce ^ Použití ^ Výsledek ^
| CLASS() | Objekt na vstupu výrazu odpovídá třídě v závorce - trochu atypická funkce pracující se vstupním parametrem, nachází uplatnění hlavně v prohledávacích algoritmech |boolean |
| ROUND() | Převede float na integer zaokrouhlením: ''ROUND(4.65) = 5'' |integer |
| TRUNC() | Převede float na integer odříznutím desetinné části: ''TRUNC(4.65) = 4'' |integer |
| FRAC() | Vrátí desetinnou část: ''FRAC(54.156) = 0.156'' |float |
| ODD() | Testuje, zda je argument liché číslo |boolean |
| ABS() | Vrátí absolutní hodnotu parametru ''ABS(-12) = 12 '' |integer nebo float |
| EXP(x) | e na x kde e je základ přirozených logaritmů (eulerovo číslo) |float |
| LN(x) | vrátí přirozený logaritmus x |float |
| SQR(x) | vrátí druhou mocninu x |float |
| SQRT(x) | vrátí druhou odmocninu x |float |
| COS(x) | kosinus x |float |
| SIN(x) | sinus x |float |
| ARCTAN(x) | arkus tangens x |float |
| TOBOOLEAN()| Převede parametr na logickou hodnotu |boolean |
| TOINTEGER()| Převede parametr na integer |integer |
| TOFLOAT() | Převede parametr na float |datetime |
|TODATETIME()| Převede parametr na datetime, druhý parametr je v případě textu formát |datetime |
| TOSTRING() | Převede parametr na string, formátování opět může řídit druhý parametr |datetime |
=== Formátování ===
* **TODATETIME()** - jen pokud je první parametr text. Pokud druhý parametr s formátem neexistuje, pokusí se funkce převést buď jako ISO zápis (2025-04-15) nebo pomocí lokálního nastavení (typicky 15.4.2025). V případě jiného zápisu je možné definovat jak je datum zapsáno ... třeba pro '15/4/2025' je to 'dd/mm/yyyy'.
* **TOSTRING()** formátování má smysl jen pokud je první parametr datetime nebo float
* **datetime** //**d**-dny (dd číslo dddd název dne) **m**-měsíce (mm číslo mmmm název) **y**-rok **h**-hodiny **n**-minuty//
d: datetime='15.4.2025 1:58'
S={TOSTRING(d,'dddd dd.mmmm yyyy hh:nn')}
// S obsahuje 'úterý 15.duben 2025 01:58'
* **float** **0**-//číslice pokud je, jinak 0// **#**-//číslice, pokud je, jinak nic//** .**//-desetinný oddělovač//
f: float={pi}
S: string
S={TOSTRING(f,'000.00')} // S obsahuje '003,14'
S={TOSTRING(f,'#.000')} // S obsahuje '3,142'
===== Příkazy běhu programu =====
Příkazy běhu programu jsou ty, které ohraničují cyklické provádění kódu za nějaké podmínky, případně tyto cykly zkracují nebo z nich vyskakují.
==== FOR ====
příkaz **FOR** je uzavřen slovem **END** - provádí určený počet **cyklů** kódu, má 2 hlavní principy a 4 možné zápisy:
=== Syntaxe ===
* Automatický cyklus FOR v objektovém modelu - prochází všechny elementy dané entity:
include('LED\ProtoUtils.oil')
CheckProto()
for E in ObjectModel.Karel // první varianta prochází elementy Karla
M.NextText=E.Name // a vypisuje do textového protokolu jejich jména
end
// nebo
for E out ObjectModel.Karel // druhá varianta prochází vlastníky Karla (nadřazené objekty)
M.NextText=E.Name // a činí s nimi totéž
end
// jak je vidět, END uzavírá kód, který se v cyklu provádí
* Cyklus FOR pomocí celočíselné proměnné a rozsahu:
for i = 1 to 3 // první varianta provede 3 cykly 1 - 3
ShowMessage(i) // a v každém zobrazí dialog s hodnotou i
end
// nebo
for i = 3 downto 1 // druhá varianta provede 3 cykly sestupně, tedy 3 - 1
M.NextText=E.Name // a činí s nimi totéž
end
// Ani proměnná E, ani i nemusejí být předem definovány, vytvářejí se automaticky
==== WHILE ====
Příkaz **WHILE** provádí cyklicky kód uzavřený slovem **END**, **dokud platí** vstupní podmínka daná **výrazem**
=== Syntaxe ===
while
end
// příklad:
i: integer
i='1' // výchozí hodnota i je 1
while i < 10 // dokud platí že i je menší než 10
// libovolný kód
i={i + 1} // a zvýšíme hodnotu i o 1
end
// zvýšení hodnoty je nutné, jinak by vznikl nekonečný cyklus protože chudák i by nikdy nebylo větší než 10
==== REPEAT UNTIL ====
Příkaz **REPEAT** provádí cyklicky kód ukončený klíčovým slovem **UNTIL**, **dokud neplatí** podmínka daná **výrazem** za UNTIL.
=== Syntaxe ===
repeat
until
// příklad:
i: integer
i='1' // výchozí hodnota i je 1
repeat // opakujeme následující kód odsud
// libovolný kód
i={i + 1} // a zvýšíme hodnotu i o 1
until i > 10 // dokud i nepřesáhne hodnotu 10
// zvýšení hodnoty je nutné, jinak by vznikl nekonečný cyklus protože i by opět nikdy nebylo větší než 10
==== CONTINUE ====
Příkaz **CONTINUE** ukončí vykonávání zbytku stávajícího cyklu a přeskočí na další kolo.
for i = 1 to 5 // začínáme cykly 1 - 5
if ODD(i) then
continue // pokud je i liché, přeskočíme na další kolo
end
ShowMessage(i) // takže ve výsledku se zobrazí dialog jen dvakrát pro hodnoty 2 a 4
end
==== BREAK ====
Příkaz **BREAK** ukončí vykonávání cyklu a přeskočí na kód následující za ním.
for i = 1 to 5 // začínáme cykly 1 - 5
if i > 3 then
break // pokud je i větší než 3 ukončíme vykonávání cyklu
end
ShowMessage(i) // takže ve výsledku se zobrazí dialog jen třikrát pro hodnoty 1,2,3
end
// po volání break pokračuje OIL zde ... pokud tu je co k provedení ...
==== IF THEN ELSE ====
Příkaz **IF** umožňuje podmíněné provedení části kódu. Podmínku definuje logický výraz mezi klíčovými slovy **IF** a **END**. **ELSE** je nepovinné, případně odděluje část prováděnou při nesplnění podmínky.
=== Syntaxe ===
if then
end
// nebo
if then
else
end
// příklad:
for i = 1 to 10
if i mod 2 = 0 then // začátek podmíněné části - výraz zjišťuje jestli je i sudé
// // pokud ano, provádí se tato pasáž
else
// // a pokud ne, provede se kod2
end
end
==== EXIT ====
Příkaz exit ukončí vykonáváni skriptu. Konec, šmytec.
===== funkce =====
===== příklady =====