Jazyk Go a vyhodnocování výrazů v době běhu aplikace

16. 9. 2021
Doba čtení: 17 minut

Sdílet

 Autor: Go lang
Často se setkáme s požadavkem na umožnění zápisu výrazů do vstupních formulářových polí, pro použití výrazů v konfiguračních souborech atd. Lze to řešit vložením interpretru plnohodnotného jazyka, ale mnohdy stačí doménově specifický jazyk (DSL).

Obsah

1. Programovací jazyk Go a skriptovací jazyky

2. Doménově specifické jazyky

3. Obecné skriptovací jazyky versus doménově specifické jazyky (DSL)

4. Projekt Gval

5. Příklad typu „Hello world!“

6. Aritmetický výraz

7. Priority operátorů

8. Parametry ve výrazech

9. Předání parametrů pro vyhodnocení výrazu

10. Předání parametrů, které se ve výrazu nepoužijí

11. Logické operátory

12. Ternární operátor

13. Přístup k prvkům pole

14. Přístup k prvkům datové struktury

15. Indexy pole předané v parametru

16. Jména prvků datové struktury předaná v parametru

17. Zkrácený přístup k prvkům datové struktury s využitím tečkové notace

18. Obsah dalšího článku

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Programovací jazyk Go a skriptovací jazyky

V seriálu o programovacím jazyce Go jsme se již několikrát zmínili o možnosti zkombinování tohoto staticky typovaného a překládaného (kompilovaného) programovacího jazyka s nějakým interpretrem, typicky s interpretrem vhodného vyššího programovacího jazyka. Díky této kombinaci je například možné „skriptovat“ aplikaci naprogramovanou v Go, rozšiřovat tuto aplikaci o další logiku, upravovat či definovat nová „business“ pravidla atd. Většinou je tato možnost rozšiřování možností aplikace s využitím skriptů výhodná pro všechny spolupracující strany – firma produkující daný software vlastně zadarmo získá další aplikační oblast, zákazník či uživatel si takto může aplikaci upravit ke svým potřebám a popř. na úpravách může spolupracovat i další společnost/vývojáři nezávislí na autorech původního software (dlouholetým příkladem z praxe jsou například CADy a jejich nadstavby).

Prozatím jsme se v praktické části seriálu o Go věnovali způsobům vestavění interpretru programovacího jazyka Lua do aplikace naprogramované v Go. Kombinace Lua+Go, resp. obecněji řečeno Lua+staticky typovaný překládaný jazyk je velmi běžná a vlastně i logická, protože právě jazyk Lua je navržen s ohledem na jeho relativně snadnou „vložitelnost“ (embed) do větších aplikací. Důvodem, proč jsou některé hry, například Escape from Monkey Island, Grim Fandango, Fish Fillets, Neverwinter Nights či MDK2 z menší či větší části naprogramované právě v Lue, spočívá v tom, že kombinace nízkoúrovňového a skriptovacího jazyka umožňuje soustředit se při vývoji na podstatné věci – herní engine vytvořit co nejefektivnější s využitím všech možností nízkoúrovňového jazyka a naopak herní scénář a logiku hry naskriptovat s co největším urychlením cyklu oprava–překlad–spuštění (viz též odkazy na konci dnešního článku).

Poznámka: ve skutečnosti existuje hned několik projektů, které programátorům používajícím programovací jazyk Go zpřístupňují interpret skriptovacího jazyka Lua. Jedná se o projekty Gopher-Lua, go-lua a taktéž golua (interface k původnímu céčkovému interpretru). Rozšířením knihovny Gopher-Lua je Binder.

2. Doménově specifické jazyky

Doménově specifické jazyky (DSL – Domain-Specific Language) jsou velmi důležitou součástí informatiky a mnohé z nich jsou velmi úspěšné a rozšířené do mnoha oblastí. Za připomenutí stojí například jazyk pro popis regulárních výrazů, jenž je podporován jak mnoha nástroji, tak i knihovnami, popř. je přímo součástí některých obecných programovacích jazyků (Perl apod.). I v některých dalších případech je tento přístup velmi užitečný a rozšířený, ostatně například SQL je s velkou pravděpodobností nejpopulárnějším samostatně chápaným doménově specifickým jazykem neboli DSL vůbec, protože umožňuje snadné optimalizace dotazů a vůbec pokládání dotazů čitelným, pochopitelným a přenositelným způsobem. Dalším doménově specifickým jazykem, s nímž jsme se již na stránkách Roota v rámci několika článků seznámili, je jazyk Gherkin určený pro popis chování systémů a pro psaní BDD testů. Dalším příkladem je PostScript.

3. Obecné skriptovací jazyky versus doménově specifické jazyky (DSL)

Mnohé z doménově specifických jazyků nejsou Turingovsky kompletní (úplný), což však není nedostatek, ale mnohdy naopak požadovaná vlastnost. Příkladem mohou být DSL, v nichž není možné zapsat programové smyčky ani rekurzi – tudíž je většinou výpočet, resp. vyhodnocení výrazu časově dosti přesně určené. Další DSL neumožňují explicitní alokaci paměti, což může být v dané oblasti použití taktéž výhodné. Nasazení DSL oproti plnohodnotnému jazyku tedy může být výhodné, protože cíleně omezené možnosti takového jazyka můžeme chápat jako formu „sémantického sandboxingu“ (právě proto jsou regulární výrazy regulární).

Poznámka: na tomto místě je zajímavé zmínit PostScript, který je sice DSL, konkrétně specifickým jazykem pro popis tiskových stran, ovšem oproti mnoha jiným DSL je Turingovsky kompletní. To například umožňuje vykreslování procedurální grafiky (viz například tyto příklady), ovšem pokud tyto zcela korektní PostScriptové soubory spustíte na podnikové tiskárně, můžete se dočkat nemilého překvapení v několikahodinové odstávce, popř. nepříjemného e-mailu z IT oddělení :-)

4. Projekt Gval

V dnešním článku si popíšeme doménově specifický jazyk nazvaný Gval, což je zkratka odvozená ze sousloví „Go eVALuate“. Toto sousloví je poměrně přiléhavé, protože se skutečně jedná o DSL zaměřený na vyhodnocování (evaluate) výrazů, přičemž tyto výrazy mohou být v některých případech i poměrně komplikované. Jejich syntaxe je odvozena od samotného programovacího jazyka Go, jak ostatně uvidíme v navazujících kapitolách, v nichž si ukážeme praktické příklady.

Tento DSL lze použít například při zpracování konfiguračních souborů (například můžeme nastavit limit počtu workerů na výraz „2*CPU_threads“ namísto pracného nastavování konkrétní číselné hodnoty) nebo i v dialozích a formulářích grafického uživatelského rozhraní. Příkladem může být vstupní pole pro odhad pracnosti, do kterého takto můžeme zadat „3*8*60“ namísto nutnosti počítání výsledné hodnoty v nějakém jiném nástroji (ostatně informační systémy jsou tady od toho, aby nám práci zjednodušovaly a nikoli ji dělaly složitější – což se mnohdy děje).

Poznámka: to, jaké pojmenované (symbolické) konstanty je možné ve výrazech použít, je samozřejmě plně v rukou programátora – výraz tedy v žádném případě nemá přístup k celému stavovému prostoru aplikace.

5. Příklad typu „Hello world!“

Podívejme se nyní na způsob předání výrazu do knihovny gval, na způsob jeho vyhodnocení a předání výsledku. Kostra příkladu získaného přímo ze stránek projektu může vypadat následovně:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{"name": "World"}
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate(`"Hello " + name + "!"`, parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

Celý tento příklad je postaven okolo funkce Evaluate, jejíž hlavička vypadá následovně:

func Evaluate(expression string, parameter interface{}, opts ...Language) (interface{}, error)

Jak je už z hlavičky této funkce patrné, předá se jí především vlastní výraz reprezentovaný řetězcem. Dále je možné předat i parametry, což jsou dvojice jméno+hodnota, které můžeme v rámci výrazu většinou považovat za pojmenované konstanty (takto tedy aplikace uživatelům nabízí část svého interního stavu). Poslední parametr je nepovinný a umožňuje rozšiřovat možnosti doménově specifického jazyka. O této možnosti se zmíníme v navazujícím článku.

Funkce Evaluate vrací dvě hodnoty – v první řadě výsledek vyhodnoceného výrazu a taktéž informaci o případné chybě, která může nastat jak při parsingu výrazu, tak i při jeho vyhodnocování. Povšimněte si především toho, že výsledná hodnota je typu interface{}, což v programovacím jazyce Go značí libovolnou hodnotu (každý datový typ splňuje prázdné rozhraní). O případné přetypování se tedy musí postarat sám programátor, a to i s případnou kontrolou chyb – typ výsledné hodnoty totiž přímo závisí na vstupu od uživatele!

Pro spuštění příkladu je nutné vytvořit i projektový soubor příkazem:

$ go mod init jméno_projektu

Po překladu a spuštění by měl projektový soubor vypadat takto:

module gval01
 
go 1.16
 
require github.com/PaesslerAG/gval v1.1.1

A soubor se seznamem všech závislostí (i tranzitivních závislostí) bude vypadat následovně:

github.com/PaesslerAG/gval v1.1.1 h1:4d7pprU9876+m3rc08X33UjGip8oV1kkm8Gh5GBuTss=
github.com/PaesslerAG/gval v1.1.1/go.mod h1:Fa8gfkCmUsELXgayr8sfL/sw+VzCVoa03dcOcR/if2w=
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=

6. Aritmetický výraz

Ve druhém demonstračním příkladu je ukázáno vyhodnocení velmi jednoduchého aritmetického výrazu, který obsahuje pouze (celo)číselné konstanty. Jedná se o jednoduchou modifikaci prvního příkladu, na které budeme stavět i další demonstrační příklady:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := make(map[string]interface{})
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("6*7", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

7. Priority operátorů

Ve výrazech, a to jak ve výrazech aritmetických, tak i logických, je definována a dodržena priorita operátorů. Ta je přímo odvozena od specifikace programovacího jazyka Go. Tabulka priorit je v Go poměrně jednoduchá, zejména v porovnání s tabulkami, které mnozí čtenáři pravděpodobně znají z jazyků C, C++ či Javy. Nejvyšší prioritu mají unární operátory (s jediným operandem) a následně existuje pouze pět priorit operátorů binárních, které si můžete zapamatovat s využitím mnemotechnické pomůcky MACAO:

Úroveň Operátory Mnemotechnická pomůcka
1 * / % Multiplicative
2 + – Additive
3 == > < >= <= != Comparison
4 && And
5 || Or
Poznámka: ve skutečnosti je situace nepatrně složitější, a to kvůli ternárnímu operátoru, který do programovacího jazyka Go nebyl přidán.

Podívejme se nyní, jak se vyhodnotí výraz s aditivními a multiplikativními operátory:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := make(map[string]interface{})
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("2 + 4*10", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

Výsledek je korektní: 42.

8. Parametry ve výrazech

Ve výrazech vyhodnocovaných knihovnou gval je možné používat i jména parametrů. Prozatím si ukažme, jak vypadá takový výraz:

2*x + y

Nyní se můžeme pokusit o vyhodnocení takového výrazu – viz též zdrojový kód uvedený pod tímto odstavcem:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := make(map[string]interface{})
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("2*x + y", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

Výraz je sice po syntaktické stránce zapsán korektně, ovšem nelze vyhodnotit, protože vyhodnocovací engine nemá přístup k parametrům x a y. Dojde tedy k chybě:

can not evaluate 2*x + y: invalid operation (float64) * (<nil>)
exit status 1

9. Předání parametrů pro vyhodnocení výrazu

Parametry, které mají být viditelné ve vyhodnocovaném výrazu, je nutné explicitně vytvořit a přidat jím nějakou hodnotu. Tento krok je sice zdánlivě zbytečně zdlouhavý, ale právě díky explicitně nastaveným parametrům je striktně oddělen stavový prostor aplikace od vyhodnocovaného výrazu – tedy jedná se o další formu sandboxingu.

Příklad deklarace mapy s parametry (ty mohou mít libovolnou hodnotu):

// parametry předávané vyhodnocovanému výrazu
parameters := map[string]interface{}{
        "x": 10,
        "y": 20,
}

Takto definovanou mapu lze předat funkci gval.Evaluate:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x": 10,
                "y": 20,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("2*x + y", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

Výsledkem bude v tomto případě hodnota 40 (podle očekávání).

10. Předání parametrů, které se ve výrazu nepoužijí

Funkci gval.Evaluate je možné předat větší množství parametrů, než bude skutečně ve výrazu využito. V tomto případě se ani nenahlásí varování (bylo by ostatně ve většině případů zbytečné). V dalším demonstračním příkladu předáváme čtyři parametry, ovšem ve skutečnosti jsou ve výrazu použity jen dva z nich:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x": 10,
                "y": 20,
                "z": 0,
                "w": -1,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("2*x + y", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

I v tomto případě se výraz vyhodnotí a vrátí hodnotu 40.

11. Logické operátory

Ve výrazech vyhodnocovaných knihovnou gval je možné použít i logické operátory a taktéž logické konstanty true a false. Následuje jednoduchý demonstrační příklad, který je založen na použití relačních operací, které vrací pravdivostní hodnotu true či false:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x": 10,
                "y": 20,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("2*x > y-5", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

Nepatrně složitější příklad používá logickou spojku && neboli logický součin:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x": 10,
                "y": 20,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("x < y && 2*x > y-5", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

12. Ternární operátor

Ve výrazech vyhodnocovaných knihovnou gval je možné použít i takzvaný ternární operátor. Ten byl představen v programovacím jazyku C a odtud se rozšířil i do mnohých dalších programovacích jazyků, včetně Javy, JavaScriptu či C#. V jazyce Go tento operátor použit není, ovšem ve výrazech je velmi užitečný (nemáme zde totiž možnost použít konstrukci if-else) a proto byl do gval přidán.

Pochopitelně se opět podíváme na způsob jeho využití:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x": 10,
                "y": 20,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("x<y ? \"mensi\":\"vetsi\"", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

V tomto případě je však výhodnější řetězec s výrazem zapsat do zpětných apostrofů, čímž obejdeme nutnost použití \" pro zápis jednoduchých uvozovek:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x": 10,
                "y": 20,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate(`x<y ? "mensi":"vetsi"`, parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

13. Přístup k prvkům pole

Mezi parametry, které se předávají k vyhodnocení výrazem, mohou být i pole nebo řezy (slice):

parameters := map[string]interface{}{
        "x":   10,
        "y":   20,
        "arr": []int{10, 20, 30},
}

Ve výrazu se k prvkům polí přistupuje běžným způsobem – použitím celočíselných indexů, které začínají od nuly (tedy naprosto stejně, jako je tomu v jazyce Go):

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x":   10,
                "y":   20,
                "arr": []int{10, 20, 30},
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("arr[0] + arr[1] + arr[2]", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

14. Přístup k prvkům datové struktury

Ve výrazu vyhodnocovaném knihovnou gval je možné přistupovat i k prvkům datové struktury. V následujícím demonstračním příkladu je použita tato jednoduchá datová struktura:

type User struct {
        ID      uint32
        Name    string
        Surname string
}

Následně je vytvořena instance této struktury a všechny tři její prvky jsou naplněny daty:

user := User{
        1,
        "Pepek",
        "Vyskoč"}

V případě, že tento objekt předáme jako parametr:

parameters := map[string]interface{}{
        "x":    10,
        "y":    20,
        "arr":  []int{10, 20, 30},
        "user": user,
}

…je možné přistupovat k prvkům parametru s využitím běžných hranatých závorek, tedy stejně, jako je tomu v samotném jazyku Go při práci s mapami (nikoli s datovými strukturami!):

`"Jméno:    " + user["Name"] + "\nPříjmení: " + user["Surname"]`

Následuje úplný zdrojový kód výše popsaného demonstračního příkladu:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
// User je uživatelsky definovaná datová struktura s viditelnými atributy
type User struct {
        ID      uint32
        Name    string
        Surname string
}
 
func main() {
        user := User{
                1,
                "Pepek",
                "Vyskoč"}
 
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "x":    10,
                "y":    20,
                "arr":  []int{10, 20, 30},
                "user": user,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate(`"Jméno:    " + user["Name"] + "\nPříjmení: " + user["Surname"]`, parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

15. Indexy pole předané v parametru

Ve třinácté kapitole jsme k prvkům pole přistupovali s využitím celočíselných indexů. Ovšem stejně dobře je možné použít hodnoty parametrů nebo nějaké složitější podvýrazy využívající parametry. Tento způsob je ukázán v dalším demonstračním příkladu, v němž se pro přístup k prvkům pole používají parametry nazvané index1 a index2:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
func main() {
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "index1": 0,
                "index2": 2,
                "arr":    []int{10, 20, 30},
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate("arr[index1] + arr[index2]", parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

16. Jména prvků datové struktury předaná v parametru

I k prvkům datové struktury můžeme přistupovat tak, že využijeme jméno prvku uložené v parametru (taktéž se může jednat o výsledek nějakého složitějšího podvýrazu, který se musí vyhodnotit na hodnotu typu řetězec). Takto reprezentované jméno prvku zapíšeme do hranaté závorky za jméno parametru nesoucího hodnotu datové struktury:

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
// User je uživatelsky definovaná datová struktura s viditelnými atributy
type User struct {
        ID      uint32
        Name    string
        Surname string
}
 
func main() {
        user := User{
                1,
                "Pepek",
                "Vyskoč"}
 
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "selector1": "Name",
                "selector2": "Surname",
                "arr":       []int{10, 20, 30},
                "user":      user,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate(`"Jméno:    " + user[selector1] + "\nPříjmení: " + user[selector2]`, parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

17. Zkrácený přístup k prvkům datové struktury s využitím tečkové notace

Podobně jako přímo v programovacím jazyce Go i při vyhodnocování výrazů knihovnou gval je možné pro přístup k prvkům datové struktury použít tečkovou notaci. Toto řešení je ukázáno v dnešním posledním demonstračním příkladu:

ict ve školství 24

package main
 
import (
        "fmt"
        "os"
 
        "github.com/PaesslerAG/gval"
)
 
// User je uživatelsky definovaná datová struktura s viditelnými atributy
type User struct {
        ID      uint32
        Name    string
        Surname string
}
 
func main() {
        user := User{
                1,
                "Pepek",
                "Vyskoč"}
 
        // parametry předávané vyhodnocovanému výrazu
        parameters := map[string]interface{}{
                "user": user,
        }
 
        // vyhodnocení výrazu
        value, err := gval.Evaluate(`"Jméno:    " + user.Name + "\nPříjmení: " + user.Surname`, parameters)
        if err != nil {
                // kód pro zpracování chyby při vyhodnocování výrazu
                fmt.Println(err)
                os.Exit(1)
        }
 
        // výpis výsledku výrazu
        fmt.Print(value)
}

18. Obsah dalšího článku

Knihovna gval sice vypadá zdánlivě jednoduše, ovšem ve skutečnosti vývojářům nabízí i některé další zajímavé a potenciálně užitečné technologie. Jednou z nich je možnost přidat si vlastní syntaktické pravidlo do doménově specifického jazyka a tím vlastně tento jazyk rozšířit. Taktéž je možné přistupovat k metodám objektů, používat vnořené datové struktury atd. atd. – tyto podrobnosti, které nemusí být na první pohled patrné, si popíšeme v navazujícím článku.

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně stovku kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:

# Příklad/soubor Stručný popis Cesta
1 01_hello_world varianta programu typu „Hello world“ založená na gval https://github.com/tisnik/go-root/blob/master/article78/01_he­llo_world
2 02_arithmetic aritmetické operátory https://github.com/tisnik/go-root/blob/master/article78/02_a­rithmetic
3 03_priority priority operátorů https://github.com/tisnik/go-root/blob/master/article78/03_pri­ority
4 04_no_constants pokus o vyhodnocení bez parametrů/konstant https://github.com/tisnik/go-root/blob/master/article78/04_no_con­stants
5 05_constants vyhodnocení výrazu s předanými parametry https://github.com/tisnik/go-root/blob/master/article78/05_con­stants
6 06_more_constants nepoužité parametry https://github.com/tisnik/go-root/blob/master/article78/06_mo­re_constants
7 07_boolean_expression pravdivostní výrazy, jednoduchý příklad https://github.com/tisnik/go-root/blob/master/article78/07_bo­olean_expression
8 08_boolean_expression pravdivostní výrazy, komplikovanější příklad https://github.com/tisnik/go-root/blob/master/article78/08_bo­olean_expression
9 09_ternary_operator ternární operátor https://github.com/tisnik/go-root/blob/master/article78/09_ter­nary_operator
10 10_ternary_operator ternární operátor, lepší zápis výrazu https://github.com/tisnik/go-root/blob/master/article78/10_ter­nary_operator
11 11_array_indexing indexování prvků v polích konstantou https://github.com/tisnik/go-root/blob/master/article78/11_a­rray_indexing
12 12_struct_selector výběr prvků struktury konstantou https://github.com/tisnik/go-root/blob/master/article78/12_struc­t_selector
13 13_array_indexing_by_parameter indexování prvků v polích parametrem https://github.com/tisnik/go-root/blob/master/article78/13_a­rray_indexing_by_parameter
14 14_struct_selector_by_parameter výběr prvků struktury parametrem https://github.com/tisnik/go-root/blob/master/article78/14_struc­t_selector_by_parameter
15 15_dot_selector výběr prvku struktury tečkovým operátorem https://github.com/tisnik/go-root/blob/master/article78/15_dot_se­lector

20. Odkazy na Internetu

  1. A curated list of awesome Go frameworks, libraries and software
    https://awesome-go.com/
  2. Gval na GitHubu
    https://github.com/PaesslerAG/gval
  3. Dokumentace k balíčku Gval
    https://coveralls.io/github/Pa­esslerAG/gval?branch=master
  4. Gval code coverage report
    https://coveralls.io/github/Pa­esslerAG/gval?branch=master
  5. Gopher-Lua
    https://github.com/yuin/gopher-lua
  6. Go-lua
    https://github.com/Shopify/go-lua
  7. Binder
    https://github.com/alexeyco/binder
  8. Kooperace mezi jazykem Lua a nativním (céčkovým) kódem
    https://www.root.cz/clanky/kooperace-mezi-jazykem-lua-a-nativnim-ceckovym-kodem/
  9. Kooperace mezi jazykem Lua a nativním (céčkovým) kódem: knihovna FFI
    https://www.root.cz/clanky/kooperace-mezi-jazykem-lua-a-nativnim-ceckovym-kodem-knihovna-ffi/
  10. Skriptovací jazyk Lua v aplikacích naprogramovaných v Go
    https://www.root.cz/clanky/skriptovaci-jazyk-lua-v-aplikacich-naprogramovanych-v-go/
  11. Jazyk Joker: dialekt Clojure naprogramovaný v Go
    https://www.root.cz/clanky/jazyk-joker-dialekt-clojure-naprogramovany-v-go/
  12. Behave na GitHubu
    https://github.com/behave/behave
  13. behave 1.2.6 (PyPi)
    https://pypi.python.org/pypi/behave
  14. Dokumentace k Behave
    http://behave.readthedocs­.io/en/latest/
  15. Příklady použití Behave
    https://github.com/behave/be­have.example
  16. Domain-specific language
    https://en.wikipedia.org/wiki/Domain-specific_language
  17. Turingovská úplnost
    https://cs.wikipedia.org/wi­ki/Turingovsk%C3%A1_%C3%BA­plnost

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.