TL;DR
-** Implement brainfuck \ * ck processing system only with standard go-template function / syntax ! - Use kubectl as brainfuck \ * ck interpreter ** (Powerword) --Brainfuck \ * ck code in pod manifest --When getting pod information with kubectl, apply to go-template of brainfuck \ * ck interpreter --Repository: go-template-bf-interpreter --Practicality ...: smirk:
C ++ templates, Python comprehensions, ... there are language features that have become Turing complete by themselves due to the black magic provided by the extra freedom.
And here we declare that ** go-template has joined the ranks of black magic **.
Speaking of go-template, there is a strong image of a mini language for templates such as HTML of gin
and response shaping of kubectl
(~~ or it is made with that intention by name ~~), but The following can be ** all possible ** [^ 1].
--Collection loop with range
block
--Conditional branching by if
block
--Declaration and update of variables (reassignment)
Reference (official): template --GoDoc
So, I implemented the processing system of brainfuck \ * ck, which is the gateway to Turing completeness, with go-template (standard function / syntax only [^ 2] without relying on Go language).
Use kubectl
.
Put the source code of brainfuck \ * ck in the manifest file of pod, and when you get it with kubectl
, use go-template to mold (= evaluate with brainfuck \ * ck interpreter of go-template). I have.
Repository: go-template-bf-interpreter
(I used kubectl
because I knew go-template for the first time with kubectl
. ~~ I wrote at the beginning that "I'm familiar with the HTML generation of gin
", but I'm sorry for Airp ~~)
Since any key and value (character string) can be stored in metadata.annotations
, the source code is stored here. Also, let's put in the standard input to brainfuck \ * 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
#...
By the way, the reason why I put a lot of dummy keys is to increase the number of loops of the interpreter (described later).
I used the code of helloworld from Brainfuck Super Introduction --Qiita.
I don't use the pod container anyway, so anything is fine. For the time being, I made an alpine image that starts up quickly.
python
#Build a k8s cluster(An example is kind)
$ kind create cluster
#Create a pod with the above helloworld code
$ kubectl create -f hello.yaml
pod/bf-source-pod created
#pod information(=Source code)And its contents become an interpreter go-Evaluate with template
$ kubectl get pods -o go-template-file=bf-interpreter.tpl
Hello World!
The implementation of the brainf \ * ck interpreter looks like this. ~~ Indent hell. ~~ bf-interpreter.tpl
Below, I will introduce the tricks I used.
You can fill the space outside the parentheses by adding -
to both ends of{{}}
. If you use this, even if you put indents and line breaks outside {{}}
, they will all be ignored.
** Required for readability ** in go-template programming. ** If not attached, one-liner binding will start **.
withspace.tpl
{{if true}}
{{println "got it!"}}
{{else}}
{{println "no..."}}
{{end}}
Wasted space is output as it is
kubectl get pods -o go-template-file=withspace.tpl
got it!
--With hyphen
trimspace.tpl
{{- if true -}}
{{- println "got it!" -}}
{{- else -}}
{{- println "no..." -}}
{{- end -}}
Wasted space disappears
$ kubectl get pods -o go-template-file=trimspace.tpl
got it!
brainf \ * ck requires a while loop. Source code Used for parsing each character and jumping when evaluating [
, ]
.
But unfortunately string
cannot iterate with range
.
Furthermore, since the only literals that can be created with go-template are constants, it is not possible to create new arrays or maps.
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
So we use the pod info metadata.annotations
(map [string] string
) for the loop in range
. A dummy is mixed with the annotation so that it can be looped 16 times.
hello.yaml
metadata:
name: bf-source-pod
annotations:
# used for bf stdin
input: ""
# bf source code
src: >
+++++++++[>++++++++>+++++++++++>+++>+<<<<-]>.>++.+++++++..+++.
>+++++.<<+++++++++++++++.>.+++.------.--------.>+.>+.
dummy1: dummy1
dummy2: dummy2
#...
dummy14: dummy14
By using this loop in multiple stages, memory initialization and source code parsing are performed.
bf-interpreter.tpl
{{- /*Substitute map and use for range block*/ -}}
{{- $Looper := (index .items 0).metadata.annotations -}}
{{- /*Memory initialization(len $Looper)^Fill 2 bytes with 0) */ -}}
{{- $memory := "" -}}
{{- range $Looper -}}
{{- range $Looper -}}
{{- $memory = print $memory "\x00" -}}
{{- end -}}
{{- end -}}
{{- /*Load source code(len $Looper)^Perth 3 letters from the beginning) */ -}}
{{- 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 -}}
{{- /*Evaluate tokens (omitted)*/ -}}
{{- /* increment pos */ -}}
{{- $parsingBytePos = print $parsingBytePos " " -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
By the way, the reason why it loops 16 times is to improve the specifications of the interpreter.
--Memory size: 256byte
($ Looper
2-stage loop)
--Upper limit of parseable source code length: 4096 characters
( $ Looper
3-stage loop)
Unfortunately (second time), go-template doesn't have integer addition / subtraction functions or operators. However, brainfuck \ * ck needs addition and subtraction when updating memory values and pointers.
So ** use the length of the string instead of an integer **.
The length of the character string can be changed by combining and slicing, and the length can be obtained as an integer with the len
function.
--Addition
inc.tpl
{{- /* go-template print is equivalent to Go's Sprint(No side effects) */ -}}
{{- $numStr := " " -}}
{{- println (len $numStr) -}}
{{- $numStr = print $numStr " " -}}
{{- println (len $numStr) -}}
python
$ kubectl get pods -o go-template-file=inc.tpl
1
2
--Subtraction
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
As mentioned above, you cannot create an array with go-template. Also, it is not possible to update only the elements of an existing object. Only variables can be rvalues in assignment expressions.
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
Therefore, the character string is used as memory.
In Go, when indexing a string, the string is treated as [] byte
, so the string itself can be regarded as a byte string.
go language index
s := "abc"
fmt.Println([]byte(s)) // [97 98 99]
fmt.Println(s[0]) // 97
fmt.Println([]byte(s)[0]) // 97
And, when updating only a certain byte in the character string with +
or -
etc.," a new memory character string in which only the byte is replaced "is created.
bf-interpreter.tpl
{{- else if eq $token "+" -}}
{{- /* ...Takes out the value of the reference address and increments it(abridgement) */ -}}
{{- /*Memory update*/ -}}
{{- /*Replace with new memory that replaces only the reference address*/ -}}
{{- /*Memory before the reference address*/ -}}
{{- $former := slice $memory 0 (len $memoryPtr) -}}
{{- /*Memory after the reference address*/ -}}
{{- /* NOTE: (len (print $memoryPtr " ")Is the reference address+1 */ -}}
{{- $latter := slice $memory (len (print $memoryPtr " ")) -}}
{{- /*Substitution (If you print the byte value as it is, the integer will be converted to a character string, so printf will convert it to the corresponding ASCII code character)*/ -}}
{{- $memory = print $former (printf "%c" $incrementedValue) $latter -}}
{{- end -}}
This is the introduction of the go-template brainfuck \ * ck processing system.
** Let's go-template programming! ** **
[^ 1]: Looking at this, it feels like "You can do that with erb!", But while erb can write any Ruby expression, go-template has an independent grammar (only scalars can be created, and go's standard functions are also available. There is a binding that you can not call it as it is ...
[^ 2]: Some k8s original go-template functions (index
) are used. The implementation is here
Recommended Posts