Go v roli skriptovacího programovacího jazyka

30. 1. 2025
Doba čtení: 33 minut

Sdílet

Autor: Go lang
Jazyk Go je kompilovaný jazyk. Ovšem i přesto vzniklo několik interpretrů tohoto jazyka, což umožňuje Go použít v interaktivní smyčce REPL i v Jupyter Notebooku. Navíc je možné interpret Go zabudovat do jiných aplikací.

Obsah

1. Go v roli skriptovacího programovacího jazyka

2. Skriptovací jazyky v současnosti

3. Projekt Gore

4. Instalace projektu Gore a spuštění interaktivní smyčky REPL

5. První kroky s interpretrem Gore

6. Speciální pseudopříkazy používané v REPL projektu Gore

7. Získání typu výrazu a omezené možnosti této techniky

8. Automatický import balíčků

9. Způsob generování zdrojových kódů interpretrem Gore

10. Interpet Yaegi

11. REPL interpretru Yaegi

12. Interpret Yaegi zabudovaný do aplikace

13. Interpret Gomacro

14. Instalace Gomacra s jeho spuštěním

15. Interní příkazy interpretru Gomacro

16. Inspektor v interpretru Gomacro

17. Debugger integrovaný do Gomacra

18. Interpret Gomacro zabudovaný do aplikace

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

20. Odkazy na Internetu

1. Go v roli skriptovacího programovacího jazyka

Pro čtenáře seriálu o programovacím jazyce Go pravděpodobně nebude tvrzení, že Go je překládaný (kompilovaný) programovací jazyk, žádným překvapením. Zdrojové kódy psané v jazyce Go se skutečně před svým spuštěním nejdříve překládají, a to buď do nativního kódu zvolené platformy (x86, x86–64, ARM 32bit, Aarch64, RISC-V atd.), nebo do bajtkódu WebAssembly. Alternativně je možné použít transpřekladač (transpiler) GopherJS, který dokáže zdrojové kódy psané v jazyce Go transformovat do JavaScriptu. Touto technologií jsme se zabývali v [1], [2] a [3].

Výhody překladače, resp. přesněji řečeno překladače programovacího jazyka se silným typovým systémem, jsou pravděpodobně většině vývojářů zřejmé – vyšší rychlost běhu aplikací, obecně menší paměťové nároky procesů, na cílovém počítači nemusí být nainstalován žádný interpret a v neposlední řadě je poměrně velká část chyb (bohužel spíše těch triviálních) objevena již překladačem. Navíc je programovací jazyk Go známý tím, že jeho překladač je velmi rychlý – mnohdy tak rychlý, že je fáze překladu prakticky „neviditelná“ (v závislosti na velikosti projektu se může jednat například o 200 ms atd. – což je z pohledu lidského vývojáře prakticky okamžitě a cyklus edit-compile-run se tak de facto zkracuje na edit-run). Ostatně příkaz go run se skrytím procesu překladu a následným odstraněním spustitelného souboru snaží tvářit jako interpretovaný jazyk.

Nicméně existují oblasti, v nichž se prosadil dosti odlišný způsob vývoje, který je založen na přímé interakci (resp. dialogu) mezi uživatelem a počítačem. V tom nejjednodušším případě se jedná o systémy vybavené interaktivní smyčkou REPL (Read Eval Print Loop), jejichž poněkud primitivní podobu si někteří mohou pamatovat z dob osmibitových mikropočítačů a interpretrů jazyka BASIC. Nicméně klasický REPL najdeme například i v Pythonu a dalších moderních skriptovacích jazycích (pro Python navíc existují různá jeho rozšíření, například v podobě IPythonu). V současnosti se mnohdy původní REPL nahrazuje spíše rozhraním ve stylu diáře (notebooku). Příkladem je projekt Jupyter Notebook, který v současnosti podporuje velké množství skriptovacích jazyků.

Kromě existence REPLu je důležitá i další technologie – rozšíření nějaké aplikace o možnost jejího ovládání pomocí skriptů. Tato technologie dokáže dosti podstatným způsobem rozšířit možnosti takové aplikace, a to klidně o řád nebo i několik řádů. Možnosti skriptování je vybavena relativně velká část aplikací, ať již se to týká programátorských textových editorů, nástrojů typu office, CADů a 3D modelovacích programů, ale například i takové „maličkosti“, jakou jsou prohlížeče HTML stránek (zde je zvětšení možností o několik řádů asi nejvíce patrné). To znamená, že bychom uvítali, kdyby i námi vyvíjené aplikace tuto možnost nabízely.

Go jakožto překládaný jazyk do této kategorie nepatří, ovšem i přesto vzniklo hned několik projektů, které se snaží o využití Go jakožto interaktivního skriptovacího jazyka. A právě některé z těchto projektů si představíme v dnešním článku.

Poznámka: už na tomto místě je vhodné upozornit na to, že některé dále zmíněné projekty se stále nachází ve fázi vývoje a není tak praktické na nich postavit aplikace určené do produkčního nasazení. Na druhou stranu například integrace jazyka Go do Jupyter Notebooku je již dosti stabilní a užitečná i v praxi.

2. Skriptovací jazyky v současnosti

V současnosti se v praxi používá poměrně velké množství různých skriptovacích jazyků. A nutno dodat, že se v některých případech jedná o velmi populární jazyky, o čemž se ostatně můžeme snadno přesvědčit například na stránkách indexu Tiobe nebo je to ještě více patrné na stránce PYPL PopularitY of Programming Language. V tabulce zobrazené pod tímto odstavcem jsou vypsány některé populární (či v minulosti populární – příkladem je ActionScript) skriptovací jazyky. Ty jsou seřazeny podle svého jména, ovšem nechybí zde ani Python, JavaScript či jazyk R (ten je ovšem používán spíše mimo profesionální IT komunitu; můžeme ho pokládat za doménově specifický jazyk):

Skriptovací jazyk
AppleScript
AWK
BeanShell
Bash
Ch
ActionScript
JavaScript
Game Maker Language
Julia
Groovy
Korn shell
Lua
Perl
PHP
PowerShell
Python
R
Rebol
Rexx
Ruby
S-lang
Tcl
VBScript
Poznámka: na tomto místě je vhodné doplnit, že pro některé výše zmíněné skriptovací jazyky existují i (plnohodnotné) překladače. Takový programovací jazyk je tedy možné použít podle přání a potřeb vývojáře jak pro tvorbu přímo spouštěných skriptů, tak i pro překlad do nativního kódu. Příkladem takového programovacího jazyka je Julia, kterému jsme se již na stránkách Roota věnovali v samostatném seriálu.

3. Projekt Gore

Prvním interpretrem programovacího jazyka Go, se kterým se dnes seznámíme, je interpret nazvaný Gore. S tímto projektem jsme se již v seriálu o programovacím jazyce Go jednou setkali. Připomeňme si, že tento projekt nabízí (z pohledu uživatele – programátora) interpret plnohodnotného jazyka Go, který je navíc vybaven interaktivní smyčkou REPL. Ta nabízí základní editační příkazy, historii zadaných příkazů, hledání v historii příkazů atd. – tedy dnes již standardní a očekávané vlastnosti.

Díky využití projektu gocode nabízí interaktivní smyčka REPL i automatické doplňování příkazů, což je samozřejmě velmi užitečná technologie (a opět – dnes již očekávaná jako standard). Projekt Gore je dostupný na adrese https://github.com/x-motemen/gore. Interně je řešen vlastně dosti triviálním způsobem: používá totiž příkaz go run pro překlad a spuštění zadávaného kódu po zápisu každého příkazu nebo bloku. Díky rychlosti překladače jazyka Go je sice toto zdržení nepatrné a pravděpodobně si ho v případě menších skriptů ani nevšimnete, nicméně se nejedná o standardní chování, které se od interpretrů očekává a taktéž se některé příkazy a knihovní funkce mohou chovat „divně“, protože se ztrácí kontext. Například se může jednat o přečtení času, vygenerování náhodného čísla, přístup do databáze atd.

4. Instalace projektu Gore a spuštění interaktivní smyčky REPL

Ještě před samotnou instalací projektu Gore je nutné nainstalovat nástroj gocode, o němž jsme se zmínili již v předchozí kapitole. Je dosti pravděpodobné, že pokud vyvíjíte programy v jazyce Go, máte již gocode nainstalován, což lze velmi snadno otestovat:

$ whereis gocode
 
gocode: /home/ptisnovs/go/bin/gocode

V případě, že gocode nainstalován není, použijte následující příkaz, který nainstaluje jeho poslední dostupnou verzi:

$ go install github.com/mdempsky/gocode@latest
 
go: downloading github.com/mdempsky/gocode v0.0.0-20200405233807-4acdcbdea79d
go: finding module for package golang.org/x/tools/go/gcexportdata
go: downloading golang.org/x/tools v0.28.0
go: found golang.org/x/tools/go/gcexportdata in golang.org/x/tools v0.28.0

Ověříme si, že gocode lze spustit

$ gocode -help
 
Usage: gocode [-s] [-f=] [-in=] [-sock=] [-addr=]
        []
 
Flags:
  -addr string
        address for tcp socket (default "127.0.0.1:37373")
  -builtin
        propose completions for built-in functions and types
  -cache
        use the cache importer
  -debug
        enable server-side debug mode
  -f string
        output format (vim | emacs | sexp | nice | csv | json) (default "nice")
  -fallback-to-source
        if importing a package fails, fallback to the source importer
  -ignore-case
        do case-insensitive matching
  -in string
        use this file instead of stdin input
  -s    run a server instead of a client
  -sock string
        socket type (unix | tcp | none) (default "unix")
  -source
        use source importer
  -unimported-packages
        propose completions for standard library packages not explicitly imported
 
Commands:
  autocomplete []      main autocompletion command
  exit                               terminate the gocode daemon

Ve druhém kroku již nainstalujeme samotný projekt Gore; opět jeho poslední verzi:

$ go install github.com/x-motemen/gore/cmd/gore@latest
 
go: downloading github.com/x-motemen/gore v0.5.7
go: downloading github.com/peterh/liner v1.2.2
go: downloading golang.org/x/tools v0.13.0
go: downloading github.com/motemen/go-quickfix v0.0.0-20230925231438-5cf0001766ff
go: downloading golang.org/x/text v0.13.0
go: downloading github.com/mattn/go-runewidth v0.0.15
go: downloading golang.org/x/sys v0.12.0
go: downloading golang.org/x/mod v0.12.0
go: downloading github.com/rivo/uniseg v0.4.4

Ověříme, že je příkaz gore dostupný z shellu:

$ whereis gore
 
gore: /home/ptisnovs/go/bin/gore
Poznámka: mělo by tomu tak být, protože v opačném případě (špatně nastavená $PATH) nebudou správně pracovat ani další nástroje pro jazyk Go.

Nakonec gore spustíme, resp. si necháme zobrazit nápovědu:

$ gore --help
 
gore - A Go REPL
 
Version: 0.5.7 (rev: HEAD/go1.22.1)
 
Synopsis:
    % gore
 
Options:
  -autoimport
        formats and adjusts imports automatically
  -context string
        import packages, functions, variables and constants from external golang source files
  -pkg string
        the package where the session will be run inside
  -version
        print gore version

Samotný interpret se spouští bez přepínačů:

$ gore
 
:gore version 0.5.7  :help for help

Po spuštění interpretru by se měla zobrazit jednořádková nápověda (viz předchozí výpis) a gore očekává uživatelské příkazy. Například si můžeme zobrazit jeho krátkou nápovědu zadáním :help (včetně dvojtečky na začátku):

gore> :help
 
    :import <package>     import a package
    :type <expr>          print the type of expression
    :print                print current source
    :write [<file>]       write out current source
    :clear                clear the codes
    :doc <expr or pkg>    show documentation
    :help                 show this help
    :quit                 quit the session

5. První kroky s interpretrem Gore

V projektu Gore je možné, ostatně podobně jako v klasických interpretrech, zadávat jednotlivé příkazy, které se ihned vykonají a vypíše se jejich návratová hodnota. Pokud se jedná o běžné výrazy, je to snadné, což si můžeme ukázat na několika příkladech:

gore> 1+2
3
 
gore> nil
nil
 
gore> "foo" + "bar"
"foobar"
 
gore> "foobar"[2:6]
"obar"

Pracovat je možné i s proměnnými, u kterých se dá s výhodou využít speciální operátor pro jejich deklaraci s přiřazením a s odvozením datového typu:

gore> x:=6
6
 
gore> y:=7
7
 
gore> x*y
42
 
gore> x<y
true

Samotné výrazy jsou vždy pouze vyhodnoceny a poté interpretr „zapomene“, že byly vyhodnocovány. Naproti tomu deklarace proměnných se stává součástí interně vytvářeného programu (zdrojového kódu). Ten si můžeme zobrazit pseudopříkazem :print:

gore> :print
 
package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { x := 6; y := 7 }

Povšimněte si, že se v postupně doplňované funkci main skutečně nachází deklarace proměnných. A interpret Gore nás odstíní od chyb typu „proměnná X je deklarovaná, ale nepoužívá se“ (což je dobrá detekce pro překladač, ale nikoli pro interaktivní interpret).

6. Speciální pseudopříkazy používané v REPL projektu Gore

S takzvanými pseudopříkazy jsme se již částečně setkali v předchozích dvou kapitolách. Tyto pseudopříkazy začínají znakem dvojtečky a můžeme je použít například pro získání dokumentace. Následující pseudopříkaz například vypíše dokumentaci k zabudované funkci println:

gore> :doc println
 
func println(args ...Type)
    The println built-in function formats its arguments in an
    implementation-specific way and writes the result to standard error. Spaces
    are always added between arguments and a newline is appended. Println is
    useful for bootstrapping and debugging; it is not guaranteed to stay in the
    language.

Dalším často používaným pseudopříkazem je příkaz :import, který je v Gore možné použít kdykoli, nikoli jen na začátku balíčku (pojem balíček je ovšem v rámci REPLu poněkud mlhavý):

gore> :import fmt

Ihned po importu balíčku je možné získat nápovědu k jeho funkcím, konstantám, typům a metodám:

gore> :doc fmt.Println
 
func Println(a ...interface{}) (n int, err error)
    Println formats using the default formats for its operands and writes to
    standard output. Spaces are always added between operands and a newline is
    appended. It returns the number of bytes written and any write error
    encountered.

Totéž například platí i pro další standardní balíček time a jeho funkci Sleep:

gore> :import time
gore> :doc time.Sleep
func Sleep(d Duration)
    Sleep pauses the current goroutine for at least the duration d. A negative
    or zero duration causes Sleep to return immediately.

Velmi užitečný je i pseudopříkaz :write jméno_souboru, který zapíše zapsané definice do specifikovaného souboru, který by měl být později přeložitelný překladačem jazyka Go (viz též seznam demonstračních příkladů, které vznikly právě tímto způsobem).

7. Získání typu výrazu a omezené možnosti této techniky

Pseudopříkazem :type můžeme zjistit typ výrazu, resp. přesněji řečeno typ vypočtené hodnoty. Jedná se o operaci, ke které se interpretry velmi dobře hodí (možná ještě lépe než debuggery):

gore> :type 42
int

Nemusíme zjišťovat jen typ konstanty (konstantního výrazu), ale i výrazů poněkud složitějších:

gore> :type 6*7
int

Pravdivostní výraz:

gore> :type 1==2
bool

Totéž platí i pro řetězce:

gore> :type "foo"
string

Řezy

gore> :type []int{1,2,3}
[]int

Pole (mimochodem lze takto velmi dobře pochopit rozdíl mezi řezy a poli):

gore> :type [...]int{1,2,3}
[3]int

Ovšem pozor je nutné si dát na to, abychom nezjišťovali typy proměnných (i když by to bylo velmi užitečné). V tomto ohledu je projekt Gore dosti omezený:

gore> x:=6*7
42

Pokus o zjištění typu proměnné x skončí s chybou:

gore> :type x
panic: file not found for pos = 1 (gore_session.go:1:1) [recovered]
        panic: file not found for pos = 1 (gore_session.go:1:1)

8. Automatický import balíčků

V interpretru Gore pochopitelně můžeme přímo volat libovolnou funkci z implicitního balíčku. Takové funkce totiž není zapotřebí importovat. Pro úplnost – jedná se o následující funkce (jejich seznam lze nalézt například ve specifikaci jazyka Go, ale většinu pravděpodobně dobře znáte a používáte):

append cap clear close complex copy delete imag len
make max min new panic print println real recover

To v praxi znamená, že si například můžeme nechat vypsat výsledek nějakého výrazu funkcí println:

gore> println(6*7)
42

Popř. si můžeme vypsat obsah proměnné:

gore> x:=10
10
gore> println(x)
10

Vypisovat hodnoty pochopitelně můžeme i z programové smyčky (spustí se automaticky po zápisu uzavírací závorky a odeslání řádku klávesou Enter):

gore> for i := range 10 {
.....     println(i)
..... }
0
1
2
3
4
5
6
7
8
9

Ovšem co se stane ve chvíli, kdy například budeme chtít namísto funkce println zavolat například fmt.Println nebo fmt.Printf? Můžeme si to vyzkoušet:

gore> fmt.Println("foo")
undefined: fmt

Interpret v tomto případě (korektně) napsal, že nezná identifikátor fmt.

Import balíčku můžeme kdykoli provést pseudopříkazem :import, například takto:

gore> :import fmt
gore> fmt.Printf("%5.3f", 1/3.)
0.3335
nil
Poznámka: užitečné je, že import lze provést skutečně kdykoli, na rozdíl od překládaného Go, v němž se importované balíčky definují na začátku a nelze je poté měnit (což dává pro staticky překládaný jazyk smysl).

Ovšem interpret Gore nám dokáže práci ještě více zjednodušit, a to v případě, že povolíme takzvaný automatický import balíčků. K tomuto účelu je nutné použít přepínač –autoimport:

$ gore --autoimport

Můžeme si to velmi snadno otestovat spuštěním nové seance interpretru:

gore version 0.5.7  :help for help
gore> x:=6*7
42
 
gore> fmt.Println(x)
42
3
nil

Poslední volání vypsalo tři hodnoty:

42
3
nil

První hodnota byla vypsána přímo volanou funkcí a je v okně interpretru zvýrazněna odlišnou barvou. A další dvě hodnoty označují počet zapsaných bajtů a případnou chybu – viz hlavičku této funkce:

func Println(a ...any) (n int, err error)

9. Způsob generování zdrojových kódů interpretrem Gore

Díky tomu, že interpret Gore obsahuje i podporu pro pseudopříkaz :print, si můžeme nechat vypsat zdrojový kód v jazyce Go, který je generovaný na pozadí – a to po odeslání každého příkazu, ať již jednořádkového či víceřádkového. Ukažme si, jak může vypadat celé „sezení“ v němž uživatel postupně do interpretru zadává příkazy a deklarace a posléze si nechá vypsat vygenerovaný zdrojový kód pseudopříkazem :print:

gore version 0.5.7  :help for help
gore> x:=42
42
gore> :print

Vypíše se:

package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { x := 42 }

Doplníme další deklaraci a opět si necháme vypsat zdrojový kód:

gore> y:=10
10
gore> :print
package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { x := 42; y := 10 }

Do zdrojového kódu se doplní i přímo vykonané příkazy:

gore> println(x)
42
gore> :print
package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { x := 42; y := 10; println(x) }

Dtto pro další příkaz:

gore> println(y)
42
10
gore> :print
package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { x := 42; y := 10; println(x); println(y) }

Chování interpretru při povolení automatického importu balíčků:

$ gore --autoimport
 
gore version 0.5.7  :help for help
gore> x:=6*7
42
gore> fmt.Println(x)
42
3
nil
gore> :print
package main
 
import (
    "fmt"
 
    "github.com/k0kubun/pp/v3"
)
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { x := 6 * 7; _, _ = fmt.Println(x) }

Do kódu lze přidávat i funkce, datové typy atd.:

gore> func add(x, y int) int {
.....     return x+y
..... }
gore> add(3,7)
10
gore> :print
package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { _ = add(3, 7) }
 
func add(x, y int) int {
    return x + y
}

Datové typy:

gore> type User struct {
.....     Name string
.....     Surname string
..... }
gore> :print
package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() {}
 
type User struct {
    Name    string
    Surname string
}

Použití uživatelského datového typu:

gore> u:=User{"John", "Doe"}
main.User{
  Name:    "John",
  Surname: "Doe",
}
gore> :print
package main
 
import "github.com/k0kubun/pp/v3"
 
func __gore_p(xs ...any) {
    for _, x := range xs {
        pp.Println(x)
    }
}
func main() { u := User{"John", "Doe"} }
 
type User struct {
    Name    string
    Surname string
}

10. Interpet Yaegi

Druhý interpret, s nímž se v dnešním článku seznámíme, se jmenuje Yaegi. Tento interpret pracuje běžným způsobem (tj. bez automatického generování Go kódu) a je určen především pro vložení (embedding) do dalších aplikací. Interaktivní smyčka REPL je sice taktéž implementována, ale nenabízí příliš mnoho vlastností, které jsou dnes očekávány: automatické doplňování příkazů, editace vstupního řádku, historie příkazů atd.

Instalace tohoto interpretru je snadná a rychlá (nemá další závislosti):

$ go install github.com/traefik/yaegi/cmd/yaegi@latest
 
go: downloading github.com/traefik/yaegi v0.16.1

Po instalaci si ověříme, zda je interpret dostupný:

$ yaegi --help
 
Yaegi is a Go interpreter.
 
Usage:
 
    yaegi [command] [arguments]
 
The commands are:
 
    extract     generate a wrapper file from a source package
    help        print usage information
    run         execute a Go program from source
    test        execute test functions in a Go package
    version     print version
 
Use "yaegi help <command>" for more information about a command.
 
If no command is given or if the first argument is not a command, then
the run command is assumed.
Poznámka: povšimněte si, že i nabídka dostupných příkazů je dosti omezená.

11. REPL interpretru Yaegi

Interaktivní smyčka interpretru Yaegi je v porovnání s ostatními dvěma interpretry spíše dosti primitivní. Nenajdeme zde ani historii příkazů ani jejich vyhledávání v historii. Dokonce tento interpret nenabízí ani žádné pseudopříkazy; pouze akceptuje vstupní výrazy a příkazy a ihned je vyhodnocuje. Je tomu tak z toho důvodu, že se Yaegi má používat spíše jako interpret vestavěný do aplikací a nikoli přímo z REPLu.

I přesto se v krátkosti podívejme na to, jak může vypadat interaktivní „sezení“ s interpretem Yaegi. Pracujeme v něm s výrazy, deklaracemi proměnných, funkcemi z jiných balíčků i s deklaracemi vlastních funkcí:

> 1+2
: 3
 
> x:=6
: 6
 
> y:=7
: 7
 
> x*y
: 42
 
> z:=x*y
: 42
 
> z
: 42
 
> import "fmt"
: 0xc00019dca0
 
> fmt.Printf("%T\n", z)
int
: 4
 
> func add(a, b int) int {
return a+b
}
: 0xc00058f5a0
 
> add(1, 2)
: 3
>

12. Interpret Yaegi zabudovaný do aplikace

Interpret Yaegi je možné, podobně jako dále popsaný interpret Gomacro, zabudovat do vyvíjené aplikace. Ve stručnosti se podívejme, jakým způsobem to může být realizováno.

Inicializace interpretru pomocí New a spuštění skriptu metodou Eval. Vypíše se výsledek skriptu, tedy hodnota 42:

package main
 
import (
        "fmt"
 
        "github.com/traefik/yaegi/interp"
        "github.com/traefik/yaegi/stdlib"
)
 
func RunYaegi(script string) any {
        interpreter := interp.New(interp.Options{})
 
        interpreter.Use(stdlib.Symbols)
 
        ret, err := interpreter.Eval(script)
        if err != nil {
                panic(err)
        }
 
        return ret
}
 
func main() {
        fmt.Println(RunYaegi("6*7"))
}

Složitější skript s deklaracemi proměnných a využitím jejich hodnot. Tento skript vrací hodnotu poslední deklarované proměnné (v Gomacru tomu ovšem bude jinak!):

package main
 
import (
        "fmt"
 
        "github.com/traefik/yaegi/interp"
        "github.com/traefik/yaegi/stdlib"
)
 
const script = `
x:=6
y:=7
z:=x*y
`
 
func RunYaegi(script string) any {
        interpreter := interp.New(interp.Options{})
 
        interpreter.Use(stdlib.Symbols)
 
        ret, err := interpreter.Eval(script)
        if err != nil {
                panic(err)
        }
 
        return ret
}
 
func main() {
        fmt.Println(RunYaegi(script))
}

Úprava předchozího skriptu tak, že se v něm bude explicitně vracet hodnota nějakého výrazu. Toto chování je kompatibilní s dále popsaným Gomacrem:

package main
 
import (
        "fmt"
 
        "github.com/traefik/yaegi/interp"
        "github.com/traefik/yaegi/stdlib"
)
 
const script = `
x:=6
y:=7
z:=x*y
z
`
 
func RunYaegi(script string) any {
        interpreter := interp.New(interp.Options{})
 
        interpreter.Use(stdlib.Symbols)
 
        ret, err := interpreter.Eval(script)
        if err != nil {
                panic(err)
        }
 
        return ret
}
 
func main() {
        fmt.Println(RunYaegi(script))
}

Deklarace funkce, kterou bude možné později zavolat přes reflexi. Na rozdíl od Gomacra však není možné tuto funkci zavolat z toho samého skriptu:

package main
 
import (
        "fmt"
 
        "github.com/traefik/yaegi/interp"
        "github.com/traefik/yaegi/stdlib"
)
 
const script = `
func multiply(a, b int) int {
    return a*b
}
`
 
func RunYaegi(script string) any {
        interpreter := interp.New(interp.Options{})
 
        interpreter.Use(stdlib.Symbols)
 
        ret, err := interpreter.Eval(script)
        if err != nil {
                panic(err)
        }
 
        return ret
}
 
func main() {
        fmt.Println(RunYaegi(script))
}
Poznámka: v praxi se mi spíše osvědčil interpret Gomacro popsaný v navazujících kapitolách.

13. Interpret Gomacro

Další interpret programovacího jazyka Go, se kterým se v dnešním článku setkáme, se jmenuje Gomacro. S tímto interpretrem jsme se již dříve setkali v článku Gophernotes: kombinace interaktivního prostředí Jupyteru s jazykem Go, protože jazyk Go je do prostředí Jupyter Notebooku integrován právě s využitím tohoto interpretru. Ovšem Gomacro není navázáno na Jupyter Notebook a lze ho použít samostatně (ovládá se tedy z terminálu s využitím REPLu, jak je dobrým zvykem). Podobně, jako tomu bylo u výše uvedených interpretrů, i zde jsou možnosti programovacího jazyka Go rozšířeny o další pseudopříkazy. A zapomenout nesmíme ani na debugger, který je součástí tohoto interpretru a s nímž se blíže seznámíme v samostatné kapitole.

Samotný název projektu Gomacro vychází z toho, že je podporována i tvorba a expanze maker. Ovšem pod pojmem makro si musíme představit LISPovská makra (resp. makra pojatá tak, jak je tomu v LISPovských programovacích jazycích) a nikoli primitivní makra jazyka C. S koncepcí maker se podrobněji seznámíme v samostatném článku, protože se vyžaduje znalost AST (abstraktního syntaktického stromu) a způsobu jeho zpracování v jazyce Go.

Poznámka: ovšem samotný jazyk Go makra nepodporuje a pravděpodobně nikdy podporovat nebude, protože to jde proti filozofii tohoto programovacího jazyka.

14. Instalace Gomacra s jeho spuštěním

Instalace tohoto projektu se opět provede, jak je ve světě jazyka Go zvykem, příkazem „go get“:

$ go install github.com/cosmos72/gomacro@latest
 
go: downloading github.com/cosmos72/gomacro v0.0.0-20240506194242-2ff796e3da10
go: downloading github.com/peterh/liner v1.2.2
go: downloading golang.org/x/tools v0.14.0
go: downloading github.com/mattn/go-runewidth v0.0.15
go: downloading github.com/rivo/uniseg v0.2.0
go: downloading golang.org/x/sys v0.13.0
go: downloading golang.org/x/mod v0.13.0

Dále je vhodné se přesvědčit o tom, že je adresář $HOME/go/bin zařazen do proměnné prostředí PATH (což platí pro všechny tři zmíněné interpretry). V opačném případě by totiž nebylo možné příkaz gomacro volat odkudkoli (což platí i pro další nástroje naprogramované v jazyku Go a instalované příkazem „go get“).

Pokud je $HOME/go/bin vložen do proměnné prostředí PATH, bude možné Gomacro spustit, například s přepínačem –help:

$ gomacro --help
 
usage: gomacro [OPTIONS] [files-and-dirs]

  Recognized options:
    -c,   --collect          collect declarations and statements, to print them later
    -e,   --expr EXPR        evaluate expression
    -f,   --force-overwrite  option -w will overwrite existing files
    -g,   --genimport [PATH] write x_package.go bindings for specified import path and exit.
                             Use "gomacro -g ." or omit path to import the current dir.
                             Used in "//go:generate gomacro -g ." directives.
    -h,   --help             show this help and exit
    -i,   --repl             interactive. start a REPL after evaluating expression, files and dirs.
                             default: start a REPL only if no expressions, files or dirs are specified
    -m,   --macro-only       do not execute code, only parse and macroexpand it.
                             useful to run gomacro as a Go preprocessor
    -n,   --no-trap          do not trap panics in the interpreter
    -t,   --trap             trap panics in the interpreter (default)
    -s,   --silent           silent. do NOT show startup message, prompt, and expressions results.
                             default when executing files and dirs.
    -v,   --verbose          verbose. show startup message, prompt, and expressions results.
                             default when executing an expression.
    -vv,  --very-verbose     as -v, and in addition show the type of expressions results.
                             default when executing a REPL
    -w,   --write-decls      write collected declarations and statements to *.go files.
                             implies -c
    -x,   --exec             execute parsed code (default). disabled by -m

    Options are processed in order, except for -i that is always processed as last.

    Collected declarations and statements can be also written to standard output
    or to a file with the REPL command :write

Spuštění interpretru:

$ gomacro
 
// Welcome to gomacro. Type :help for help, :copy for copyright and license.
// This is free software with ABSOLUTELY NO WARRANTY.
gomacro>
Poznámka: případné hlášení zobrazené níže znamená, že byly smazány zdrojové kódy Gomacra, což však jeho práci nijak zásadně neovlivní:
// warning: could not find package "github.com/cosmos72/gomacro" in $GOPATH = "/home/tester/go/", assuming package is located in "/home/tester/go/src/github.com/cosmos72/gomacro"

15. Interní příkazy interpretru Gomacro

I interpret Gomacro má svůj vlastní repertoár interních příkazů (či pseudopříkazů). Ty začínají dvojtečkou, takže se zde používá stejný formát zápisu, jako v případě projektu Gore. Všechny tyto pseudopříkazy si můžeme vypsat pseudopříkazem :help:

gomacro> :help
// type Go code to execute it. example: func add(x, y int) int { return x + y }
 
// interpreter commands:
:copyright         show copyright and license
:debug EXPR        debug expression or statement interactively
:env [NAME]        show available functions, variables and constants
                   in current package, or from imported package NAME
:help              show this help
:inspect EXPR|TYPE inspect expression or type interactively
:options [OPTS]    show or toggle interpreter options
:package "PKGPATH" switch to package PKGPATH, importing it if possible
:quit              quit the interpreter
:unload "PKGPATH"  remove package PKGPATH from the list of known packages.
                   later attempts to import it will trigger a recompile
:write [FILE]      write collected declarations and/or statements to standard output or to FILE
                   use :opt Declarations and/or :opt Statements to start collecting them

V dalších kapitolách se seznámíme zejména s pseudopříkazy :inspect (inspektor) a :debug (debugger). Ovšem zajímavý je i způsob manipulace s balíčky s využitím :package a :unload. Protože se zde přímo nepracuje s postupně vytvářeným zdrojovým kódem v jazyce Go, nelze použít příkaz :print, ovšem můžeme si nechat vypsat všechny zadané příkazy a deklarace pseudopříkazem :write.

16. Inspektor v interpretru Gomacro

Jedním z nejužitečnějších nástrojů Gomacra je takzvaný inspector. Ten nám umožňuje například prozkoumat výrazy:

gomacro> :inspect 1*(2+3)
 
1*(2+3) = {int 5}       // untyped.Lit
    0. Kind     = int   // untyped.Kind
    1. Val      = 5     // constant.Value
    2. basicTypes       = [<nil> 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 0x11060b0 <nil> <nil> <nil> <nil> <nil> <nil> <nil> 0x11060b0 <nil> 0x11060b0]  // *[]xreflect.Type
// type ? for inspector help

Prozkoumat lze i volání funkcí, kdy se mj. zobrazí jejich výsledek:

gomacro> import "fmt"
 
gomacro> :inspect fmt.Printf("%d\n", 42)
 
// warning: expression returns 2 values, using only the first one: [int error]
42
fmt.Printf("%d\n", 42)  = 3     // int
// type ? for inspector help

Popř. lze vypsat metody aplikovatelné pro zvoleného příjemce (receiver). Pro numerické výrazy jsou to metody datového typu int (ty se nevolají jménem, ale typicky nějakým operátorem):

inspect fmt.Printf("%d\n", 42)> methods
 
methods of int:
    m0. func (int).Add(int, int) int
    m1. func (int).And(int, int) int
    m2. func (int).AndNot(int, int) int
    m3. func (int).Cmp(int) int
    m4. func (int).Equal(int) bool
    m5. func (int).Less(int) bool
    m6. func (int).Lsh(int, uint8) int
    m7. func (int).Mul(int, int) int
    m8. func (int).Neg(int) int
    m9. func (int).Not(int) int
    m10. func (int).Or(int, int) int
    m11. func (int).Quo(int, int) int
    m12. func (int).Rem(int, int) int
    m13. func (int).Rsh(int, uint8) int
    m14. func (int).Sub(int, int) int
    m15. func (int).Xor(int, int) int

Výpis takzvaného prostředí, tj. dostupných funkcí a datových typů:

gomacro> :env
 
// ----- builtin binds -----
Eval            = {0x1213270 func(interface{}, interface{}) interface{}}        // fast.Function
EvalKeepUntyped = {0x1213320 func(interface{}, interface{}) interface{}}        // fast.Function
EvalType        = {0x1213660 func(interface{}, interface{}) reflect.Type}       // fast.Function
Interp          = {0x1213240 func(interface{}) interface{}}     // fast.Function
MacroExpand     = {0x1214310 func(interface{}, interface{}) (go/ast.Node, bool)}        // fast.Function
MacroExpand1    = {0x1214400 func(interface{}, interface{}) (go/ast.Node, bool)}        // fast.Function
MacroExpandCodeWalk             = {0x12144f0 func(interface{}, interface{}) (go/ast.Node, bool)}        // fast.Function
Parse           = {0x1216310 func(string, interface{}) interface{}}     // fast.Function
append          = 0x120efd0     // fast.Builtin
cap             = 0x120fb30     // fast.Builtin
close           = 0x1210290     // fast.Builtin
complex         = 0x1210880     // fast.Builtin
copy            = 0x1211f00     // fast.Builtin
delete          = 0x1212ae0     // fast.Builtin
false           = {bool false}  // untyped.Lit
imag            = 0x1216e30     // fast.Builtin
len             = 0x1213960     // fast.Builtin
make            = 0x1214bd0     // fast.Builtin
new             = 0x1215bb0     // fast.Builtin
nil             = nil   // <nil>
panic           = 0x1216020     // fast.Builtin
print           = 0x1216a60     // fast.Builtin
println         = 0x1216a60     // fast.Builtin
real            = 0x1216e30     // fast.Builtin
recover         = 0x12180e0     // fast.Builtin
true            = {bool true}   // untyped.Lit
 
// ----- builtin types -----
Pointer         = unsafe.Pointer        // unsafe.Pointer
bool            = bool  // bool
byte            = uint8 // uint8
complex128      = complex128    // complex128
complex64       = complex64     // complex64
error           = error // interface
float32         = float32       // float32
float64         = float64       // float64
int             = int   // int
int16           = int16 // int16
int32           = int32 // int32
int64           = int64 // int64
int8            = int8  // int8
rune            = int32 // int32
string          = string        // string
uint            = uint  // uint
uint16          = uint16        // uint16
uint32          = uint32        // uint32
uint64          = uint64        // uint64
uint8           = uint8 // uint8
uintptr         = uintptr       // uintptr
 
// ----- main binds -----
fmt             = {fmt "fmt", 19 binds, 6 types}        // *fast.Import

Prozkoumání vlastností zvoleného datového typu:

gomacro> :inspect bool
bool    = false // bool
// type ? for inspector help
inspect bool> methods
methods of bool:
    m0. func (bool).Equal(bool) bool
    m1. func (bool).Not(bool) bool

17. Debugger integrovaný do Gomacra

Již v předchozích kapitolách jsme se zmínili o interním příkazu :debug, kterým se spouští debugger zabudovaný do nástroje Gomacro. Ukažme si použití tohoto nástroje na několika příkladech.

Nejprve si nadeklarujeme jednoduchou funkci pro součet dvou celých čísel:

gomacro> func add(x, y int) int {
. . . .    z := x
. . . .    z += y
. . . .    return z
. . . .    }

Tuto funkci pochopitelně můžeme přímo zavolat a získáme její výsledek:

gomacro> add(1,2)
3       // int

Ovšem můžeme ji zavolat i v rámci debuggeru, a to následujícím způsobem:

gomacro> :debug add(1,2)
// stopped at repl.go:1:1 IP=0, call depth=1. type ? for debugger help
func add(x, y int) int {
^^^

V debuggeru (změní se výzva) lze používat několik nových příkazů, které se píšou bez dvojtečky a lze je typicky zkrátit na jediný znak (n=next atd.):

// debugger commands:
backtrace       show call stack
env [NAME]      show available functions, variables and constants
                in current scope, or from imported package NAME
?               show this help
help            show this help
inspect EXPR    inspect expression interactively
kill   [EXPR]   terminate execution with panic(EXPR)
print   EXPR    print expression, statement or declaration
list            show current source code
continue        resume normal execution
finish          run until the end of current function
next            execute a single statement, skipping functions
step            execute a single statement, entering functions
vars            show local variables

Nejčastějším příkazem bude s (step) a n (next). Tyto příkazy jsou určeny pro krokování:

debug> n
// stopped at repl.go:2:1 IP=1, call depth=1. type ? for debugger help
z := x
^^^
debug> n
// stopped at repl.go:3:1 IP=2, call depth=1. type ? for debugger help
z += y
^^^
debug> n
// stopped at repl.go:4:8 IP=3, call depth=1. type ? for debugger help
return z
       ^^^
debug> n
3       // int

Zobrazit si můžeme i všechny v danou chvíli dostupné symboly. Lokální proměnné jsou zobrazeny na konci:

debug> env
 
...
...
...
// -----  binds -----
x               = 1     // int
y               = 2     // int
z               = 3     // int

Příkazem print se vypíše obsah téměř jakéhokoli výrazu, což je při ladění velmi užitečné:

debug> print z
3       // int
debug> n
// stopped at repl.go:3:1 IP=2, call depth=1. type ? for debugger help
z += y
^^^
debug> print z
1       // int

Ve druhé ukázce použijeme složitější funkci, konkrétně funkci pro výpočet faktoriálu:

gomacro> import "math/big"
gomacro> func factorial(n *big.Int) *big.Int {
. . . .    one := big.NewInt(1)
. . . .    if n.Cmp(big.NewInt(0)) <= 0 {
. . . .      return one
. . . .      }
. . . .    return one.Mul(n, factorial(one.Sub(n, one)))
. . . .    }
gomacro>

Základní otestování funkcionality:

gomacro> factorial(big.NewInt(10))
3628800 // *math/big.Int

Ladění / krokování této funkce:

gomacro> :debug factorial(big.NewInt(10))
// stopped at repl.go:1:1 IP=0, call depth=1. type ? for debugger help
func factorial(n *big.Int) *big.Int {
^^^

Jedná se o rekurzivní funkci, takže se bude hodit příkaz backtrace, resp. jen b pro zobrazení jejího volání (adres a argumentů uložených na zásobníku):

debug> backtrace
 
0xc000472000    func factorial(n=<*big.Int Value> <*math/big.Int>) <*big.Int Value> <*math/big.Int>
0xc0004720a0    func factorial(n=<*big.Int Value> <*math/big.Int>) <*big.Int Value> <*math/big.Int>
0xc000472140    func factorial(n=<*big.Int Value> <*math/big.Int>) <*big.Int Value> <*math/big.Int>

18. Interpret Gomacro zabudovaný do aplikace

I Gomacro je možné zabudovat přímo do vyvíjené aplikace a tak rozšířit její možnosti (typicky o řád, jak jsme si řekli v úvodní kapitole). Podívejme se na čtyři demonstrační příklady, které tyto možnosti ukazují.

Spuštění triviálního skriptu s výrazem „6*7“ a výpisem výsledku tohoto výrazu (opět se zde používá klasická funkce New následovaná voláním metody Eval):

package main
 
import (
        "fmt"
 
        "github.com/cosmos72/gomacro/fast"
)
 
func RunGomacro(script string) any {
        interp := fast.New()
        vals, _ := interp.Eval(script)
        return vals[0].ReflectValue()
}
 
func main() {
        fmt.Println(RunGomacro("6*7"))
}

Složitější víceřádkový skript s deklarací proměnných. Tento skript nevrací žádnou hodnotu, takže do kódu je doplněn test, zda skript vrací hodnotu (hodnoty) či nikoli:

package main
 
import (
        "fmt"
 
        "github.com/cosmos72/gomacro/fast"
)
 
const script = `
x:=6
y:=7
z:=x*y
`
 
func RunGomacro(script string) any {
        interp := fast.New()
        vals, _ := interp.Eval(script)
        if len(vals) < 1 {
                return "no value"
        }
        return vals[0].ReflectValue()
}
 
func main() {
        fmt.Println(RunGomacro(script))
}

Doplnění skriptu o výraz „z“, který způsobí, že se vrátí hodnota proměnné s tímto jménem:

package main
 
import (
        "fmt"
 
        "github.com/cosmos72/gomacro/fast"
)
 
const script = `
x:=6
y:=7
z:=x*y
z
`
 
func RunGomacro(script string) any {
        interp := fast.New()
        vals, _ := interp.Eval(script)
        if len(vals) < 1 {
                return "no value"
        }
        return vals[0].ReflectValue()
}
 
func main() {
        fmt.Println(RunGomacro(script))
}

A konečně deklarace funkce uvnitř skriptu s voláním této funkce:

package main
 
import (
        "fmt"
 
        "github.com/cosmos72/gomacro/fast"
)
 
const script = `
func multiply(a, b int) int {
    return a*b
}
 
x:=6
y:=7
multiply(x, y)
`
 
func RunGomacro(script string) any {
        interp := fast.New()
        vals, _ := interp.Eval(script)
        if len(vals) < 1 {
                return "no value"
        }
        return vals[0].ReflectValue()
}
 
func main() {
        fmt.Println(RunGomacro(script))
}
Poznámka: povšimněte si použití základního balíčku pro reflexi.

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

Zdrojové kódy interně vygenerované interpretrem Gore byly uloženy do Git repositáře, jenž je dostupný na adrese https://github.com/RedHatOf­ficial/GoCourse. Jednotlivé demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:

linux_sprava_tip

# Příklad Stručný popis Adresa
1 gore_init.go zdrojový kód vygenerovaný interpretrem Gore ihned po inicializaci https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/gore_init.go
2 variables.go deklarace proměnných v interpretru https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/variables.go
3 after_println.go zavolání základní funkce println https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/after_println.go
4 after_println2.go další zavolání základní funkce println https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/after_println.go
5 after_import.go zdrojový kód po explicitním importu balíčku https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/after_import.go
6 autoimport.go zdrojový kód po automatickém importu balíčku při zavolání jeho funkce https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/autoimport.go
7 function_declaration.go deklarace funkce https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/function_decla­ration.go
8 type_declaration.go deklarace datového typu https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/type_declarati­on.go
9 type_usage.go použití uživatelem definovaného datového typu https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gore/type_usage.go

Další čtveřice příkladů ukazuje použití Gomacra pro spuštění skriptů přímo z aplikace psané v jazyku Go:

# Příklad Stručný popis Adresa
1 gomacro_eval1.go spuštění jednoduchého skriptu s jediným výrazem https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gomacro/gomacro_eval.go
2 gomacro_eval2.go skript s deklaracemi proměnných, který nevrací žádnou hodnotu https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gomacro/gomacro_eval.go
3 gomacro_eval3.go skript s deklaracemi proměnných, který vrací hodnotu https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gomacro/gomacro_eval.go
4 gomacro_eval4.go skript obsahující deklaraci funkce, která je posléze zavolána https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/gomacro/gomacro_eval.go

Poslední čtveřice příkladů ukazuje použití Yaegi pro spuštění skriptů přímo z aplikace psané v jazyku Go:

# Příklad Stručný popis Adresa
1 yaegi_eval1.go spuštění jednoduchého skriptu s jediným výrazem https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/yaegi/yaegi_eval1.go
2 yaegi_eval2.go skript s deklaracemi proměnných, který nevrací žádnou hodnotu https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/yaegi/yaegi_eval2.go
3 yaegi_eval3.go skript s deklaracemi proměnných, který vrací hodnotu https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/yaegi/yaegi_eval3.go
4 yaegi_eval4.go skript obsahující deklaraci funkce https://github.com/RedHatOf­ficial/GoCourse/blob/master/les­son13/yaegi/yaegi_eval4.go

20. Odkazy na Internetu

  1. yaegi
    https://github.com/traefik/yaegi
  2. gore
    https://github.com/x-motemen/gore
  3. gomacro
    https://github.com/cosmos72/gomacro
  4. Stránky jazyka Go
    https://go.dev/
  5. Package names
    https://go.dev/blog/package-names
  6. The Go Programming Language Specification
    https://go.dev/ref/spec
  7. GoDoc: dokumentace k balíčkům
    https://godoc.org/
  8. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  9. Go: the Good, the Bad and the Ugly (dnes již v některých ohledech zastaralé)
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  10. Interpreter (computing)
    https://en.wikipedia.org/wi­ki/Interpreter_(computing)
  11. Scripting language
    https://en.wikipedia.org/wi­ki/Scripting_language
  12. Scripting languages
    https://en.wikipedia.org/wi­ki/List_of_programming_lan­guages_by_type#Scripting_lan­guages
  13. PYPL PopularitY of Programming Language
    https://pypl.github.io/PYPL.html
  14. Tiobe index
    https://www.tiobe.com/tiobe-index/
  15. GopherJS: transpřekladač z jazyka Go do JavaScriptu
    https://www.root.cz/clanky/gopherjs-transprekladac-z-jazyka-go-do-javascriptu/
  16. Technologie WebAssembly a GopherJS: předávání argumentů mezi Go a JavaScriptem
    https://www.root.cz/clanky/technologie-webassembly-a-gopherjs-predavani-argumentu-mezi-go-a-javascriptem/
  17. Technologie WebAssembly a GopherJS: předávání argumentů mezi Go a JavaScriptem (dokončení)
    https://www.root.cz/clanky/technologie-webassembly-a-gopherjs-predavani-argumentu-mezi-go-a-javascriptem-dokonceni/
  18. Gophernotes
    https://github.com/gopher­data/gophernotes
  19. Jupyter Notebook – nástroj pro programátory, výzkumníky i lektory
    https://www.root.cz/clanky/jupyter-notebook-nastroj-pro-programatory-vyzkumniky-i-lektory/
  20. Difference Between Compiler and Interpreter
    https://www.geeksforgeeks­.org/difference-between-compiler-and-interpreter/
  21. What are the best interpreted programming languages?
    https://www.slant.co/topic­s/15913/~interpreted-programming-languages
  22. Top 13 Scripting Languages You Should Pay Attention To
    https://kinsta.com/blog/scripting-languages/
  23. Go Interpreter
    https://github.com/go-interpreter/
  24. Compiler vs Interpreter: A Detailed Comparison
    https://www.theknowledgea­cademy.com/blog/compiler-vs-interpreter/
  25. Gomacro: code generation made easy and fun
    https://github.com/cosmos72/go­macro/blob/master/doc/code_ge­neration.pdf

Autor článku

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

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »