Skip to content

Instantly share code, notes, and snippets.

@imyousuf
Created July 18, 2019 21:46
Show Gist options
  • Save imyousuf/45017c1b12168145b05c4f5dde0dbf0d to your computer and use it in GitHub Desktop.
Save imyousuf/45017c1b12168145b05c4f5dde0dbf0d to your computer and use it in GitHub Desktop.
A crack at the code cleaner problem
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strings"
"unicode"
)
func main() {
params := os.Args[1:]
originalLines, _ := readInput(params[0])
processedLines := squashWhitespaceWhenSingleLineComment(stripMultilineComments(stripSingleLineComments(originalLines)))
printLines(processedLines)
writeLines(processedLines, params[1])
}
type marker struct {
Line int
Position int
}
var defaultMarker = marker{Line: -1, Position: -1}
type truncator struct {
Start marker
End marker
}
func (t *truncator) hasStarted() bool {
return t.Start != defaultMarker
}
func newTruncator() *truncator {
return &truncator{
Start: defaultMarker,
End: defaultMarker,
}
}
func truncateSlice(toBeTruncatedLines []string, truncators []truncator) []string {
sort.Slice(truncators, func(i, j int) bool {
if truncators[i].Start.Line != truncators[j].Start.Line {
return truncators[i].Start.Line > truncators[j].Start.Line
} else {
return truncators[i].Start.Position > truncators[j].Start.Position
}
})
// Now that there is a truncate list in descending order just loop through and truncate them
// It means lines will be squashed and entire block will be replaced with single value
// signified by replaceChar or left alone
result := make([]string, len(toBeTruncatedLines))
copy(result, toBeTruncatedLines)
for _, truncat := range truncators {
if truncat.End.Line == truncat.Start.Line {
result[truncat.Start.Line] = result[truncat.Start.Line][:truncat.Start.Position] + result[truncat.Start.Line][truncat.End.Position:]
} else {
result[truncat.End.Line] = result[truncat.End.Line][truncat.End.Position:]
result[truncat.Start.Line] = result[truncat.Start.Line][:truncat.Start.Position]
}
if truncat.End.Line-truncat.Start.Line > 1 {
result = append(result[:truncat.Start.Line+1], result[truncat.End.Line:]...)
}
}
return result
}
const (
startMultilineCommentMarker = "/*"
endMultilineCommentMarker = "*/"
singleLineCommentMarker = "//"
)
func stripMultilineComments(tobeProcessedLines []string) []string {
workingTruncator := newTruncator()
truncators := []truncator{}
for index, line := range tobeProcessedLines {
if !workingTruncator.hasStarted() && strings.Contains(line, startMultilineCommentMarker) {
workingTruncator.Start = marker{
Line: index,
Position: strings.Index(line, startMultilineCommentMarker),
}
}
if workingTruncator.hasStarted() && strings.Contains(line, endMultilineCommentMarker) {
workingTruncator.End = marker{
Line: index,
Position: strings.Index(line, endMultilineCommentMarker) + 2,
}
truncators = append(truncators, *workingTruncator)
workingTruncator = newTruncator()
}
}
return truncateSlice(tobeProcessedLines, truncators)
}
func stripSingleLineComments(tobeProcessedLines []string) []string {
workingTruncator := newTruncator()
truncators := []truncator{}
for index, line := range tobeProcessedLines {
if strings.Contains(line, singleLineCommentMarker) {
workingTruncator.Start.Line = index
workingTruncator.End.Line = index
workingTruncator.Start.Position = strings.Index(line, singleLineCommentMarker)
workingTruncator.End.Position = len(line)
truncators = append(truncators, *workingTruncator)
workingTruncator = newTruncator()
}
}
return truncateSlice(tobeProcessedLines, truncators)
}
func squashWhitespaceWhenSingleLineComment(tobeProcessedLines []string) []string {
// This should also replace all newline with a single white space
linesInSingleString := strings.Join(tobeProcessedLines, " ")
// Converts all type of whitespace to a single type of whitespace - the regular one
flattenedSingleString := ""
workingTruncator := newTruncator()
truncators := []truncator{}
// Tracks beginning of a whitespace patch
whitespace := false
for index, char := range linesInSingleString {
// Tracks - '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP)
if unicode.IsSpace(char) {
flattenedSingleString += " "
if !whitespace {
whitespace = true
} else if !workingTruncator.hasStarted() {
workingTruncator.Start.Line = 0
workingTruncator.Start.Position = index
}
} else {
flattenedSingleString += string(char)
whitespace = false
if workingTruncator.hasStarted() {
workingTruncator.End.Line = 0
workingTruncator.End.Position = index
truncators = append(truncators, *workingTruncator)
workingTruncator = newTruncator()
}
}
}
return truncateSlice([]string{flattenedSingleString}, truncators)
}
func printLines(linesToPrint []string) {
for _, line := range linesToPrint {
println(line)
}
}
// Copied from https://stackoverflow.com/questions/5884154/read-text-file-into-string-array-and-write
func readInput(filepath string) ([]string, error) {
file, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}
func writeLines(linesToWrite []string, outputFilepath string) error {
file, err := os.Create(outputFilepath)
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
for _, line := range linesToWrite {
fmt.Fprintln(w, line)
}
return w.Flush()
}
# include<stdio.h>
// This is a single line
int main() {/*
Multiline comment
*/
// Single line comment takes priority over /*
return 0; // Another single line
// Single line comment takes priority over */
/* Single line multi-line comment */
/*
Another Multiline comment
*/
}
// Output of this code is -
// # include<stdio.h> int main() { return 0; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment