Skip to content

Instantly share code, notes, and snippets.

@bodhi
Created August 3, 2015 07:11
Show Gist options
  • Save bodhi/bdf49c39a258649de8f5 to your computer and use it in GitHub Desktop.
Save bodhi/bdf49c39a258649de8f5 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"fmt"
"os"
"regexp"
"strconv"
"strings"
)
type word string
type line string
// reads lines from input, emits each line on returned channel
func readLines(input *bufio.Reader) <-chan line {
output := make(chan line)
go func() {
defer close(output)
var (
next string
err error
)
for err == nil {
next, err = input.ReadString('\n')
next = strings.Trim(next, " \n")
// fmt.Printf("line: '%s'\n", next)
output <- line(next)
}
}()
return output
}
// ingests lines, emits words
func splitIntoWords(input <-chan line) <-chan word {
output := make(chan word)
go func() {
defer close(output)
splitter := regexp.MustCompile(`[\s\n]+`)
for line := range input {
for _, w := range splitter.Split(string(line), -1) {
// fmt.Printf("\tword: '%s'\n", w)
if w != "" {
output <- word(w)
}
}
}
}()
return output
}
// ingests words, buffers and emits lines with len(line) < columnWidth if possible
// (words longer than columnWidth prevent a guarantee)
func fillWords(columnWidth uint64, input <-chan word) <-chan line {
output := make(chan line)
go func() {
defer close(output)
firstWord, ok := <-input
if !ok {
return
}
buffer := []string{string(firstWord)}
for w := range input {
newBuffer := append(buffer, string(w))
// fmt.Printf("\t\tbuffer: %v <- %s\n", buffer, w)
if uint64(len(strings.Join(newBuffer, " "))) > columnWidth {
fullLine := strings.Join(buffer, " ")
output <- line(fullLine)
buffer = []string{string(w)}
} else {
buffer = newBuffer
}
}
if len(buffer) > 0 {
// fmt.Printf("\t\tlast buffer: '%v' %d\n", buffer, len(buffer))
output <- line(strings.Join(buffer, " "))
}
}()
return output
}
// ingests words, buffers and emits lines with len(line) < columnWidth if possible
// (words longer than columnWidth prevent a guarantee)
func fillWordsAppend(columnWidth uint64, input <-chan word) <-chan line {
output := make(chan line)
go func() {
defer close(output)
buffer, ok := <-input
if !ok { // upstream already gave up! lets give up too
return
}
for w := range input {
// fmt.Printf("\t\tbuffer: %v <- %s\n", buffer, w)
if uint64(len(buffer)+1+len(w)) > columnWidth {
output <- line(buffer)
buffer = w
} else {
buffer += " " + w
}
}
if len(buffer) > 0 {
// fmt.Printf("\t\tlast buffer: '%v' %d\n", buffer, len(buffer))
output <- line(buffer)
}
}()
return output
}
// prints ingested lines to stdout
func writeLines(input <-chan line) <-chan struct{} {
output := make(chan struct{})
go func() {
defer close(output)
for line := range input {
fmt.Println(line)
}
}()
return output
}
func main() {
columnWidth, _ := strconv.ParseUint(os.Args[1], 10, 8)
input := bufio.NewReader(os.Stdin)
<-writeLines(fillWords(columnWidth, splitIntoWords(readLines(input))))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment