dotfiles/.config/mpanel/parser-src/parser.go

374 lines
9.3 KiB
Go

package main
import (
"bufio"
"os"
"strings"
"errors"
"fmt"
"sort"
)
const LEFT = 0
const CENTER = 1
const RIGHT = 2
const GLOBAL_PREFIX = ""
const GLOBAL_SUFFIX = "%{B-}%{F-}"
const LEFT_BORDER_CHAR = ""
const RIGHT_BORDER_CHAR = ""
const RIGHT_ALIGN_PREFIX = "%%{B%s}%%{F%s}"
const LEFT_FACING_BORDER = "%%{B%s}%%{F%s}" + LEFT_BORDER_CHAR + "%%{B%s}%%{F%s}"
const RIGHT_ALIGN_SUFFIX = ""
const LEFT_ALIGN_PREFIX = "%%{B%s}%%{F%s}"
const RIGHT_FACING_BORDER = "%%{B%s}%%{F%s}" + RIGHT_BORDER_CHAR + "%%{B%s}%%{F%s}"
const LEFT_ALIGN_SUFFIX = ""
const LEFT_HANGING_BORDER = "%%{F%s}" + LEFT_BORDER_CHAR
const RIGHT_HANGING_BORDER = "%%{B-}%%{F%s}" + RIGHT_BORDER_CHAR
const CENTER_PREFIX = "%%{B%s}%%{F%s}"
const CENTER_BORDER = ""
const CENTER_SUFFIX = ""
type block struct {
tag string
align int
back string
fore string
content string
}
func main() {
blocks := make([]block, 0, 16)
reader := bufio.NewReader(os.Stdin)
for {
line, _ := reader.ReadString('\n')
if (line == "") {
os.Exit(0)
}
var err error = nil
new_blocks, err := runCommandOnSlice(line, blocks)
if (err != nil) {
continue
} else {
blocks = new_blocks
}
output := generateStringFromSlice(blocks)
if (err != nil) {
fmt.Printf("Fatal error! %w\n", err)
os.Exit(1)
}
fmt.Println(GLOBAL_PREFIX + output + GLOBAL_SUFFIX)
}
}
func runCommandOnSlice(command string, targetSlice []block) ([]block, error) {
tag, format, content, err := extractFieldsFromCommand(command)
if (err != nil) {
return nil, err
}
if (len(content) > 0) {
if (content == "!DEAD") {
return removeBlockFromSliceWithTag(targetSlice, tag), nil
}
if (content[0] == '\\') {
content = content[1:]
}
}
align, back, fore := parseFormatString(format)
curBlock := block {tag: tag, align: align, back: back, fore: fore, content: content}
newSlice, err := mergeBlockToSlice(curBlock, targetSlice)
if (err != nil) {
return nil, fmt.Errorf("runCommandOnSlice: %v", err)
}
newSlice = sortSliceOfBlocks(newSlice)
return newSlice, nil
}
func extractFieldsFromCommand(command string) (string, string, string, error) {
trimmedCommand := strings.TrimRight(command, " \n")
tag := ""
format := ""
content := ""
splitCommand := strings.SplitN(trimmedCommand, "/", 3)
tag = splitCommand[0]
if (len(splitCommand) == 3) {
format = splitCommand[1]
content = splitCommand[2]
} else if (len(splitCommand) == 2) {
format = ""
content = splitCommand[1]
} else {
return "", "", "", fmt.Errorf("Malformed command: %v", command)
}
if (content == "NULL") {
content = ""
}
return tag, format, content, nil
}
func removeBlockFromSliceWithTag(targetSlice []block, tag string) ([]block) {
outputSlice := make([]block, len(targetSlice), cap(targetSlice))
index := -1
copy(outputSlice, targetSlice)
for i, v := range outputSlice {
if v.tag == tag {
index = i
break
}
}
if (index != -1) {
outputSlice = removeIndexFromSlice(outputSlice, index)
}
return outputSlice
}
func removeIndexFromSlice(targetSlice []block, index int) []block {
outputSlice := make([]block, len(targetSlice), cap(targetSlice))
copy(outputSlice, targetSlice)
outputSlice[index] = outputSlice[len(outputSlice) - 1]
return outputSlice[:len(outputSlice) - 1]
}
func parseFormatString(format string) (int, string, string) {
align := LEFT
back := ""
fore := ""
splitFormat := strings.Split(format, "!")
align = parseAlignString(splitFormat[0])
if (len(splitFormat) > 1) {
back = splitFormat[1]
}
if (len(splitFormat) > 2) {
fore = splitFormat[2]
}
return align, back, fore
}
func parseAlignString(align string) int {
switch align {
case "left":
return LEFT
case "center":
return CENTER
case "right":
return RIGHT
default:
return -1
}
}
func mergeBlockToSlice(newBlock block, targetSlice []block) ([]block, error) {
targetBlock, index := getBlockAndIndexFromSliceWithTag(targetSlice, newBlock.tag)
outputSlice := make([]block, len(targetSlice), cap(targetSlice))
copy(outputSlice, targetSlice)
if (index == -1) {
outputSlice = append(outputSlice, newBlock)
return outputSlice, nil
}
mergedBlock, err := mergeTwoBlocks(newBlock, targetBlock)
if (err != nil) {
return nil, fmt.Errorf("mergeBlockToSlice: %v", err)
}
outputSlice[index] = mergedBlock
return outputSlice, nil
}
func getBlockAndIndexFromSliceWithTag(targetSlice []block, tag string) (block, int) {
for i, curBlock := range targetSlice {
if (curBlock.tag == tag) {
return curBlock, i
}
}
return block {tag: "", align: -1, back: "", fore: ""}, -1
}
func mergeTwoBlocks(newBlock block, oldBlock block) (block, error) {
if (newBlock.tag != "") {
oldBlock.tag = newBlock.tag
}
if (newBlock.align != -1) {
oldBlock.align = newBlock.align
}
if (newBlock.back != "") {
oldBlock.back = newBlock.back
}
if (newBlock.fore != "") {
oldBlock.fore = newBlock.fore
}
if (newBlock.content != "") {
oldBlock.content = newBlock.content
}
if (oldBlock.tag == "") {
return block {tag: "", align: -1, back: "", fore: ""}, errors.New("No tag given for new block!")
}
if (oldBlock.align == -1) {
oldBlock.align = LEFT
}
if (oldBlock.back == "") {
oldBlock.back = "-"
}
if (oldBlock.fore == "") {
oldBlock.fore = "-"
}
if (oldBlock.content == "") {
oldBlock.content = ""
}
return oldBlock, nil
}
func sortSliceOfBlocks(targetSlice []block) []block {
outputSlice := make([]block, len(targetSlice), cap(targetSlice))
copy(outputSlice, targetSlice)
sort.Slice(outputSlice, func (i, j int) bool {
if (outputSlice[i].align < outputSlice[j].align) {
return true
}
return outputSlice[i].tag < outputSlice[j].tag
})
return outputSlice
}
func generateStringFromSlice(sourceSlice []block) string {
if (len(sourceSlice) == 0) {
return ""
}
outputString := ""
mode := LEFT
index := 0
prevBlock := block {
tag: "",
align: sourceSlice[0].align,
back: sourceSlice[0].back,
fore: sourceSlice[0].fore,
content: "" }
nextBlock := block {
tag: "",
align: -1,
back: "",
fore: "",
content: "" }
for _, curBlock := range sourceSlice {
if (index == len(sourceSlice) - 1) {
nextBlock = block {
tag: "",
align: sourceSlice[0].align,
back: sourceSlice[0].back,
fore: sourceSlice[0].fore,
content: "" }
} else {
nextBlock = sourceSlice[index + 1]
}
index = index + 1
alignSegment := ""
if (curBlock.align != mode) {
switch curBlock.align {
case LEFT:
alignSegment = "%{l}"
case CENTER:
alignSegment = "%{c}"
case RIGHT:
alignSegment = "%{r}"
}
mode = curBlock.align
}
blockString := ""
switch curBlock.align {
case LEFT:
if (nextBlock.align == curBlock.align) {
blockString = fmt.Sprintf(LEFT_ALIGN_PREFIX, curBlock.back, curBlock.fore) + curBlock.content + fmt.Sprintf(RIGHT_FACING_BORDER, nextBlock.back, curBlock.back, nextBlock.back, nextBlock.fore)
} else {
blockString = fmt.Sprintf(LEFT_ALIGN_PREFIX, curBlock.back, curBlock.fore) + curBlock.content + fmt.Sprintf(RIGHT_HANGING_BORDER, curBlock.back)
}
case CENTER:
prefix := fmt.Sprintf(CENTER_PREFIX, curBlock.back, curBlock.fore)
suffix := ""
if (prevBlock.align != curBlock.align) {
prefix = fmt.Sprintf(LEFT_HANGING_BORDER, curBlock.back) + prefix
} else {
prefix = CENTER_BORDER + prefix
}
if (nextBlock.align != curBlock.align) {
suffix = suffix + fmt.Sprintf(RIGHT_HANGING_BORDER, curBlock.back)
}
blockString = prefix + curBlock.content + suffix
case RIGHT:
if (prevBlock.align == curBlock.align) {
blockString = fmt.Sprintf(LEFT_FACING_BORDER, prevBlock.back, curBlock.back, curBlock.back, curBlock.fore) + curBlock.content
} else {
blockString = fmt.Sprintf(LEFT_HANGING_BORDER, curBlock.back) + fmt.Sprintf(RIGHT_ALIGN_PREFIX, curBlock.back, curBlock.fore) + curBlock.content
}
}
outputString = outputString + alignSegment + blockString
prevBlock = curBlock
}
return outputString
}