374 lines
9.3 KiB
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
|
|
}
|