Le go-template est-il parfait pour le tuling? J'ai créé un système de traitement brainf * ck

TL;DR

introduction

Modèles C ++, inclusions Python, ... il existe une fonctionnalité de langage qui a été complètement maîtrisée par elle-même en raison de la magie noire provoquée par la liberté supplémentaire.

Et ici, nous déclarons que ** go-template a rejoint les rangs de la magie noire **.

En parlant de go-template, il existe une image forte d'un mini langage pour des modèles tels que le HTML de gin et la mise en forme de la réponse de kubectl (bien qu'il soit fait avec cette intention dans le nom ~~), Ce qui suit peut être ** tout possible ** [^ 1].

Référence (officielle): template --GoDoc

J'ai donc essayé d'implémenter le système de traitement de brainf \ * ck, qui est la passerelle vers l'exhaustivité de Turing, avec go-template (fonction / syntaxe standard uniquement [^ 2] sans compter sur le langage Go).

Comment ça fonctionne

Utilisez kubectl.

Mettez le code source de brainf \ * ck dans le fichier manifeste du pod, et lorsque vous l'obtenez avec kubectl, utilisez go-template pour mouler (= évaluer avec l'interpréteur brainf \ * ck de go-template). Je suis.

Dépôt: go-template-bf-interpreter

(J'ai utilisé kubectl parce que je connaissais go-template pour la première fois avec kubectl. ~~ J'ai écrit au début que" je connais la génération HTML de gin ", mais je suis désolé pour Airp ~~)

Stocker le code source brainf \ * ck dans le fichier manifeste

Puisque n'importe quelle clé et valeur (chaîne de caractères) peut être stockée dans metadata.annotations, le code source est stocké ici. Aussi, ajoutons l'entrée standard à brainf \ * ck.

hello.yaml


metadata:
  name: bf-source-pod
# add dummies to make annotations long enough to loop (see bf-interpreter.tpl for details)
  annotations:
# used for bf stdin
    input: ""
# bf source code
    src: >
      +++++++++[>++++++++>+++++++++++>+++>+<<<<-]>.>++.+++++++..+++.
      >+++++.<<+++++++++++++++.>.+++.------.--------.>+.>+.
    dummy1: dummy1
    dummy2: dummy2
    #...

Au fait, la raison pour laquelle j'ai mis beaucoup de clés factices est d'augmenter le nombre de boucles de l'interpréteur (décrit plus loin).

J'ai utilisé le code helloworld de Brainfuck Super Primer --Qiita.

Je n'utilise de toute façon pas le conteneur à dosettes, donc tout va bien. Pour le moment, j'ai réalisé une image alpine qui démarre rapidement.

Flux d'exécution

python


#Construire un cluster k8s(Un exemple est gentil)
$ kind create cluster

#Créez un pod avec le code helloworld ci-dessus
$ kubectl create -f hello.yaml
pod/bf-source-pod created

#informations sur le pod(=Code source)Et son contenu devient un interprète aller-Évaluer avec un modèle
$ kubectl get pods -o go-template-file=bf-interpreter.tpl
Hello World!

go-template Modèle de conception de programmation (?)

L'implémentation de l'interpréteur brainf \ * ck ressemble à ceci. ~~ Retrait de l'enfer. ~~ bf-interpreter.tpl

Ci-dessous, je vais vous présenter les astuces que j'ai utilisées.

Remplir les espaces vides

Vous pouvez remplir l'espace en dehors des parenthèses en ajoutant - aux deux extrémités de {{}}. Si vous utilisez ceci, même si vous mettez des retraits et des sauts de ligne en dehors de {{}}, ils seront tous ignorés. ** Requis pour la lisibilité ** dans la programmation go-template. ** S'il n'est pas attaché, la reliure sur une seule ligne commencera **.

withspace.tpl


{{if true}}
    {{println "got it!"}}
{{else}}
    {{println "no..."}}
{{end}}

L'espace gaspillé est sorti tel quel


 kubectl get pods -o go-template-file=withspace.tpl

    got it!

--Avec trait d'union

trimspace.tpl


{{- if true -}}
    {{- println "got it!" -}}
{{- else -}}
    {{- println "no..." -}}
{{- end -}}

L'espace gaspillé disparaît


$ kubectl get pods -o go-template-file=trimspace.tpl
got it!

boucle

brainf \ * ck nécessite une boucle while. Code source Utilisé pour analyser chaque caractère et sauter lors de l'évaluation de [, ].

Mais malheureusement, string ne peut pas être itéré avec range. De plus, comme les seuls littéraux qui peuvent être créés avec go-template sont des constantes, il n'est pas possible de créer de nouveaux tableaux ou cartes.

python


$ kubectl get pods -o go-template --template '{{range $c := "abc"}}{{println $c}}{{end}}'
...
error: error executing template "{{range $c := \"abc\"}}{{println $c}}{{end}}": template: output:1:14: executing "output" at <"abc">: range can't iterate over abc

Nous utilisons donc les informations de pod metadata.annotations ( map [string] string) pour la boucle dans range. Un mannequin est mélangé avec l'annotation afin qu'il puisse être bouclé 16 fois.

hello.yaml


metadata:
  name: bf-source-pod
  annotations:
# used for bf stdin
    input: ""
# bf source code
    src: >
      +++++++++[>++++++++>+++++++++++>+++>+<<<<-]>.>++.+++++++..+++.
      >+++++.<<+++++++++++++++.>.+++.------.--------.>+.>+.
    dummy1: dummy1
    dummy2: dummy2
    #...
    dummy14: dummy14

En utilisant cette boucle en plusieurs étapes, l'initialisation de la mémoire et l'analyse du code source sont effectuées.

bf-interpreter.tpl


{{- /*Carte de substitution et utilisation pour le bloc de plage*/ -}}
{{- $Looper := (index .items 0).metadata.annotations -}}

{{- /*Initialisation de la mémoire(len $Looper)^Remplissez 2 octets avec 0) */ -}}
{{- $memory := "" -}}
{{- range $Looper -}}
    {{- range $Looper -}}
        {{- $memory = print $memory "\x00" -}}
    {{- end -}}
{{- end -}}

{{- /*Lire le code source(len $Looper)^Perspective de 3 lettres depuis le début) */ -}}
{{- range $Looper -}}
    {{- range $Looper -}}
        {{- range $Looper -}}
            {{- /* NOTE: exists is implemented only in k8s parser */ -}}
            {{- if exists $Source (len $parsingBytePos) -}}
                {{- $tokenByte := index $Source (len $parsingBytePos) -}}
                {{- $token := printf "%c" $tokenByte -}}
                
                {{- /*Évaluer les jetons (omis)*/ -}}

                {{- /* increment pos */ -}}
                {{- $parsingBytePos = print $parsingBytePos " " -}}
            {{- end -}}
        {{- end -}}
    {{- end -}}
{{- end -}}

À propos, la raison pour laquelle il boucle 16 fois est d'améliorer les spécifications de l'interpréteur.

--Taille de la mémoire: 256byte (boucle à 2 étages $ Looper)

Addition et soustraction

Malheureusement (deuxième fois), go-template n'a pas de fonctions ou d'opérateurs d'addition / soustraction d'entiers. Cependant, brainf \ * ck nécessite une addition et une soustraction lors de la mise à jour des valeurs de mémoire et des pointeurs.

Donc ** utilisez la longueur de la chaîne au lieu d'un entier **. La longueur de la chaîne de caractères peut être modifiée par combinaison et découpage, et la longueur peut être obtenue sous forme d'entier avec la fonction «len».

--Une addition

inc.tpl


{{- /* go-l'impression de modèle est équivalente au Sprint de Go(Pas d'effets secondaires) */ -}}
{{- $numStr := " " -}}
{{- println (len $numStr) -}}
{{- $numStr = print $numStr " " -}}
{{- println (len $numStr) -}}

python


$ kubectl get pods -o go-template-file=inc.tpl
1
2

--Soustraction

dec.tpl


{{- $numStr := " " -}}
{{- println (len $numStr) -}}
{{- $numStr = slice $numStr 1 -}}
{{- println (len $numStr) -}}

python


$ kubectl get pods -o go-template-file=dec.tpl
1
0

Mise à jour de la mémoire

Comme mentionné ci-dessus, vous ne pouvez pas créer de tableau avec go-template. De plus, il n'est pas possible de mettre à jour uniquement les éléments d'un objet existant. Seules les variables peuvent être des rvaleurs dans les expressions d'affectation.

python


$ kubectl get pods -o go-template --template '{{(index .items 0) := "hoge"}}'
error: error parsing template {{(index .items 0) := "hoge"}}, template: output:1: unexpected ":=" in operand

Par conséquent, la chaîne de caractères est utilisée comme mémoire. Dans Go, lors de l'indexation d'une chaîne de caractères, la chaîne de caractères est traitée comme «[] octet», de sorte que la chaîne de caractères elle-même peut être considérée comme une chaîne d'octets.

index des langues


s := "abc"
fmt.Println([]byte(s)) // [97 98 99]
fmt.Println(s[0]) // 97
fmt.Println([]byte(s)[0]) // 97

Et, lors de la mise à jour uniquement d'un certain octet dans la chaîne de caractères avec «+» ou «-» etc., "une nouvelle chaîne de caractères mémoire dans laquelle seul l'octet est remplacé" est créée.

bf-interpreter.tpl


{{- else if eq $token "+" -}}
	{{- /* ...Prend la valeur de l'adresse de référence et l'incrémente(réduction) */ -}}

	{{- /*Mise à jour de la mémoire*/ -}}
	{{- /*Remplacez par une nouvelle mémoire avec uniquement l'adresse de référence remplacée*/ -}}

	{{- /*Mémoire avant l'adresse de référence*/ -}}
	{{- $former := slice $memory 0 (len $memoryPtr) -}}

	{{- /*Mémoire après l'adresse de référence*/ -}}
	{{- /* NOTE: (len (print $memoryPtr " ")Est l'adresse de référence+1 */ -}}
	{{- $latter := slice $memory (len (print $memoryPtr " ")) -}}

	{{- /*Remplacer (Si vous imprimez la valeur d'octet telle quelle, l'entier sera converti en chaîne de caractères, donc printf le convertira en caractère de code ASCII correspondant)*/ -}}
	{{- $memory = print $former (printf "%c" $incrementedValue) $latter -}}
{{- end -}}

en conclusion

Ceci est l'introduction du système de traitement go-template brainf \ * ck.

** Allons-y de la programmation par modèle! ** **

[^ 1]: En regardant ça, on a l'impression que "Vous pouvez faire ça avec erb!", Mais si erb peut écrire n'importe quelle expression Ruby, go-template a une grammaire indépendante (seuls les scalaires peuvent être créés, et les fonctions standard de go sont également disponibles). Il existe une liaison que vous ne pouvez pas appeler telle quelle ...

[^ 2]: Certaines fonctions go-template originales de k8 (ʻindex`) sont utilisées. La mise en œuvre est ici

Recommended Posts

Le go-template est-il parfait pour le tuling? J'ai créé un système de traitement brainf * ck
〇✕ J'ai fait un jeu
Est-ce un commerce de système?
Qu'est-ce qu'un appel système
J'ai fait un texte Python
J'ai fait un robot discord
J'ai fait une commande lo qui est plus utile que ls
i! i! ← Ceci est une formule
J'ai créé un site d'apprentissage C ++
J'ai fait un Line-bot avec Python!
J'ai créé un script de traduction basé sur CUI (2)
J'ai fait un wikipedia gacha bot
J'ai fait une loterie avec Python.
J'ai créé un script de traduction basé sur CUI
J'ai créé un démon avec Python
J'ai fait une sorte d'outil de traitement d'image simple en langage Go.
[Langage C] Ma locomotive est trop lente ~ J'ai fait une commande sl ~