Skip to content

Commit

Permalink
Merge branch 'algorithm-optimization'
Browse files Browse the repository at this point in the history
  • Loading branch information
Mauricio Klein committed May 15, 2019
2 parents a1fc61a + 92497fc commit 25204e0
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 57 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ $ go get -u github.com/mauricioklein/go-spacetree/spacetree

// Generate the tree
// Returns a *spacetree.Node, holding the tree's root node
root := spacetree.New(scanner, indentationSymbol)
root, err := spacetree.New(scanner, indentationSymbol)
```

Runnable examples can be found [here](examples/).
Expand Down
4 changes: 2 additions & 2 deletions examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func main() {
scanner := bufio.NewScanner(file)
indentationSymbol := " " // File is indented by two consecutive spaces

root := spacetree.New(scanner, indentationSymbol)
root, err := spacetree.New(scanner, indentationSymbol)

fmt.Printf("Root: %+v\n", root)
fmt.Printf("Root: %+v (error: %+v)\n", root, err)
}
5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
module github.com/mauricioklein/go-spacetree

require (
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/stretchr/testify v1.3.0
)
require github.com/stretchr/testify v1.3.0
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down
6 changes: 4 additions & 2 deletions spacetree/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ Spacetree expects two attributes as input:
- the content buffer (*bufio.Scanner), holding the indented content
- the indentation symbol (string)
The result is a *spacetree.Node representing the root of the tree.
The result is:
- a *spacetree.Node, representing the root of the tree
- an error, set in case of a processing error (broken indentation, for example)
Example:
Expand All @@ -40,6 +42,6 @@ Example:
scanner := [your *bufio.Scanner containing the indented content]
indentationSymbol := " " // single space
root := spacetree.New(scanner, indentationSymbol)
root, err := spacetree.New(scanner, indentationSymbol)
*/
package spacetree
31 changes: 23 additions & 8 deletions spacetree/line_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ func Test_toLines_OneLevelWithSpaces(t *testing.T) {

lines := toLines(scanner, indentationSymbol)

assert.Equal(t, len(lines), 3)
assert.Equal(t, lines[0], line{Value: "1", Level: 0})
assert.Equal(t, lines[1], line{Value: "2", Level: 0})
assert.Equal(t, lines[2], line{Value: "3", Level: 0})
assert.Equal(t, lines, []line{
{Value: "1", Level: 0},
{Value: "2", Level: 0},
{Value: "3", Level: 0},
})
}

func Test_toLines_TwoLevelsWithSpaces(t *testing.T) {
Expand All @@ -30,7 +31,6 @@ func Test_toLines_TwoLevelsWithSpaces(t *testing.T) {

lines := toLines(scanner, indentationSymbol)

assert.Equal(t, len(lines), 6)
assert.Equal(t, lines, []line{
{Value: "1", Level: 0},
{Value: "1.1", Level: 1},
Expand All @@ -49,7 +49,6 @@ func Test_toLines_MultipleLevelsWithSpaces(t *testing.T) {

lines := toLines(scanner, indentationSymbol)

assert.Equal(t, len(lines), 8)
assert.Equal(t, lines, []line{
{Value: "1", Level: 0},
{Value: "1.1", Level: 1},
Expand All @@ -70,7 +69,6 @@ func Test_toLines_MultipleLevelsWithTabs(t *testing.T) {

lines := toLines(scanner, indentationSymbol)

assert.Equal(t, len(lines), 8)
assert.Equal(t, lines, []line{
{Value: "1", Level: 0},
{Value: "1.1", Level: 1},
Expand All @@ -91,7 +89,6 @@ func Test_toLines_MultipleLevelsWithDashes(t *testing.T) {

lines := toLines(scanner, indentationSymbol)

assert.Equal(t, len(lines), 8)
assert.Equal(t, lines, []line{
{Value: "1", Level: 0},
{Value: "1.1", Level: 1},
Expand All @@ -104,6 +101,24 @@ func Test_toLines_MultipleLevelsWithDashes(t *testing.T) {
})
}

func Test_toLines_IndentationBrokenWithSpaces(t *testing.T) {
scanner, closeFile := toScanner("indentation_broken_with_spaces.txt")
indentationSymbol := " "

defer closeFile()

lines := toLines(scanner, indentationSymbol)

assert.Equal(t, lines, []line{
{Value: "1", Level: 0},
{Value: "1.1", Level: 1},
{Value: "1.2", Level: 1},
{Value: "2", Level: 0},
{Value: "3", Level: 0},
{Value: "3.1", Level: 4}, // Indentation broken here!
})
}

func toScanner(filename string) (*bufio.Scanner, func() error) {
file, err := os.Open("../test_input/" + filename)

Expand Down
2 changes: 1 addition & 1 deletion spacetree/spacetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "bufio"

// New returns the root node of the tree generated processing
// the provided scanner
func New(scanner *bufio.Scanner, indentationSymbol string) *Node {
func New(scanner *bufio.Scanner, indentationSymbol string) (*Node, error) {
return newTree(
toLines(scanner, indentationSymbol),
)
Expand Down
4 changes: 3 additions & 1 deletion spacetree/spacetree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ func Test_New(t *testing.T) {

defer closeFile()

root := New(scanner, indentationSymbol)
root, err := New(scanner, indentationSymbol)

assert.NoError(t, err)

assert.Equal(t, root, &Node{
Value: "",
Expand Down
48 changes: 16 additions & 32 deletions spacetree/tree.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,34 @@
package spacetree

import (
"github.com/golang-collections/collections/stack"
"fmt"
)

func newTree(lines []line) *Node {
func newTree(lines []line) (*Node, error) {
root := newNode("")
lastNode := root

stk := stack.New()
stk.Push(root)
levelNodeMapping := map[int]*Node{-1: root}
level := -1

currentLevel := -1

for _, line := range lines {
levelDiff := line.Level - currentLevel

node := newNode(line.Value)
for lineNumber, line := range lines {
levelDiff := line.Level - level

if levelDiff > 1 {
// Entry is a child of the last node
stk.Push(lastNode)
currentLevel++
} else if levelDiff < 1 {
// Entry is in a higher (or same level) than last node.
// So, unstack by the level diff
for i := 0; i < abs(levelDiff)+1; i++ {
stk.Pop()
currentLevel--
}
return nil, newIndentationBrokenError(lineNumber)
}

// Add the new node to the tree
stk.Peek().(*Node).addChild(node)
parentNode := levelNodeMapping[line.Level-1]
n := newNode(line.Value)

parentNode.addChild(n)

// Update the last node
lastNode = node
levelNodeMapping[line.Level] = n
level = line.Level
}

return root
return root, nil
}

func abs(x int) int {
if x < 0 {
return -x
}

return x
func newIndentationBrokenError(lineNumber int) error {
return fmt.Errorf("indentation broken at line %d", lineNumber)
}
32 changes: 28 additions & 4 deletions spacetree/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ func Test_newTree_OneLevelIndentation(t *testing.T) {
{Value: "3", Level: 0},
}

root := newTree(lines)
root, err := newTree(lines)

assert.NoError(t, err)

assert.Equal(t, root.Children[0], newNode("1"))
assert.Equal(t, root.Children[1], newNode("2"))
Expand All @@ -29,7 +31,10 @@ func Test_newTree_TwoLevelIndentation(t *testing.T) {
{Value: "2.1", Level: 1},
}

root := newTree(lines)
root, err := newTree(lines)

assert.NoError(t, err)

var n *Node

// Node "1"
Expand All @@ -54,7 +59,10 @@ func Test_newTree_MultiLevelIndentation(t *testing.T) {
{Value: "3.1", Level: 1},
}

root := newTree(lines)
root, err := newTree(lines)

assert.NoError(t, err)

var n *Node

// Node "1"
Expand All @@ -80,7 +88,23 @@ func Test_newTree_MultiLevelIndentation(t *testing.T) {
func Test_newTree_Empty(t *testing.T) {
lines := []line{}

root := newTree(lines)
root, err := newTree(lines)

assert.NoError(t, err)
assert.Equal(t, root, newNode(""))
}

func Test_newTree_IndentationBroken(t *testing.T) {
lines := []line{
{Value: "1", Level: 0},
{Value: "1.1", Level: 1},
{Value: "1.2", Level: 1},
{Value: "2", Level: 0},
{Value: "2.1.1", Level: 2}, // Indentation broken here!
}

root, err := newTree(lines)

assert.Nil(t, root)
assert.Errorf(t, err, "indentation broken at line 4")
}
6 changes: 6 additions & 0 deletions test_input/indentation_broken_with_spaces.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
1
1.1
1.2
2
3
3.1

0 comments on commit 25204e0

Please sign in to comment.