Skip to content

Instantly share code, notes, and snippets.

@wtask
Last active January 24, 2019 20:45
Show Gist options
  • Save wtask/fd4471b935fac5dfad3d9067cf2cf66b to your computer and use it in GitHub Desktop.
Save wtask/fd4471b935fac5dfad3d9067cf2cf66b to your computer and use it in GitHub Desktop.
Check brackets balanced
package brackets
type (
// B - bracketing type; [0] - open, [1] - close.
B [2]rune
)
// Round - return round bracketing type
func Round() B {
return B{'(', ')'}
}
// Curly - return curly bracketing type
func Curly() B {
return B{'{', '}'}
}
// Square - return square bracketing type
func Square() B {
return B{'[', ']'}
}
// IsBalanced - checks string contain balanced set of opening-closing brackets.
// For empty string or empty bracketing scope always returns true.
func IsBalanced(s string, scope ...B) bool {
if s == "" || len(scope) == 0 {
return true
}
expectation := make([]rune, 0, len(scope)) // stacked expectations of closing brackets
for _, r := range []rune(s) {
for _, b := range scope {
if b[0] == b[1] {
// invalid bracketing
continue
}
if r == b[0] {
// opening
expectation = append(expectation, b[1])
break
}
if r == b[1] {
// closing
if len(expectation) == 0 ||
expectation[len(expectation)-1] != r {
return false
}
expectation = expectation[:len(expectation)-1]
break
}
}
}
return len(expectation) == 0
}
package brackets
import "testing"
func TestIsBalanced(t *testing.T) {
cases := []struct {
s string
scope []B
expected bool
}{
{"", []B{}, true},
{"", []B{Round(), Curly(), Square()}, true},
{"][][", []B{}, true}, // empty bracketing scope
{"][][", []B{Round()}, true}, // balanced with round brackets, which are absent inside string
{"(][][)", []B{Round()}, true}, // balanced with round brackets, square are out of the scope
{"(][][)", []B{Round(), Square()}, false},
{"([])", []B{Round(), Square()}, true},
{"([)]", []B{Round(), Square()}, false},
{"(()()(){}", []B{Round(), Square(), Curly()}, false},
{"(()()(){})", []B{Round(), Square(), Curly()}, true},
{"(()()()[{})]", []B{Round(), Square(), Curly()}, false},
{"((((([]))))){{{[]}}}", []B{Round(), Square(), Curly()}, true},
{"({[})]", []B{Round(), Square(), Curly()}, false},
{"{ { { { { } } } } } word() [inside]", []B{Round(), Square(), Curly()}, true},
{"<><{}>(<...>)<[]>", []B{Round(), Square(), Curly(), B{'<', '>'}}, true},
{"])([", []B{B{']', '['}, B{')', '('}}, true}, // brainfuck brackets
{"])[(", []B{B{']', '['}, B{')', '('}}, false},
{"[(])", []B{B{'\x00', '\x00'}}, true}, // invalid brackets (opening == closing) are ignored
{"\x00[(])\x00", []B{B{'\x00', '\x00'}, Round(), Square()}, false}, // invalid brackets (opening == closing) are ignored
}
for _, c := range cases {
if actual := IsBalanced(c.s, c.scope...); actual != c.expected {
t.Errorf("IsBalanced() != %v for %q and %q", c.expected, c.s, c.scope)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment