Skip to content

Commit

Permalink
Merge pull request #20 from aichaos/sort-thats
Browse files Browse the repository at this point in the history
Refactor internal %Previous structure and prettify the shell
  • Loading branch information
kirsle authored Feb 8, 2017
2 parents b2781a8 + b4504ab commit 3d36e5b
Show file tree
Hide file tree
Showing 12 changed files with 234 additions and 164 deletions.
34 changes: 17 additions & 17 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ The tree looks like this (in JSON-style syntax):
*/
package ast

// Type Root represents the root of the AST tree.
// Root represents the root of the AST tree.
type Root struct {
Begin Begin `json:"begin"`
Topics map[string]*Topic `json:"topics"`
Objects []*Object `json:"objects"`
}

// Type Begin represents the "begin block" style data (configuration).
// Begin represents the "begin block" style data (configuration).
type Begin struct {
Global map[string]string `json:"global"`
Var map[string]string `json:"var"`
Expand All @@ -33,14 +33,14 @@ type Begin struct {
Array map[string][]string `json:"array"` // Map of string (names) to arrays-of-strings
}

// Type Topic represents a topic of conversation.
// Topic represents a topic of conversation.
type Topic struct {
Triggers []*Trigger `json:"triggers"`
Includes map[string]bool `json:"includes"`
Inherits map[string]bool `json:"inherits"`
}

// Type Trigger has a trigger pattern and all the subsequent handlers for it.
// Trigger has a trigger pattern and all the subsequent handlers for it.
type Trigger struct {
Trigger string `json:"trigger"`
Reply []string `json:"reply"`
Expand All @@ -49,7 +49,7 @@ type Trigger struct {
Previous string `json:"previous"`
}

// Type Object contains source code of dynamically parsed object macros.
// Object contains source code of dynamically parsed object macros.
type Object struct {
Name string `json:"name"`
Language string `json:"language"`
Expand All @@ -58,22 +58,22 @@ type Object struct {

// New creates a new, empty, abstract syntax tree.
func New() *Root {
ast := new(Root)

// Initialize all the structures.
ast.Begin.Global = map[string]string{}
ast.Begin.Var = map[string]string{}
ast.Begin.Sub = map[string]string{}
ast.Begin.Person = map[string]string{}
ast.Begin.Array = map[string][]string{}
ast := &Root{
// Initialize all the structures.
Begin: Begin{
Global: map[string]string{},
Var: map[string]string{},
Sub: map[string]string{},
Person: map[string]string{},
Array: map[string][]string{},
},
Topics: map[string]*Topic{},
Objects: []*Object{},
}

// Initialize the 'random' topic.
ast.Topics = map[string]*Topic{}
ast.AddTopic("random")

// Objects
ast.Objects = []*Object{}

return ast
}

Expand Down
93 changes: 76 additions & 17 deletions cmd/rivescript/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,31 @@ import (
"github.com/aichaos/rivescript-go/lang/javascript"
)

var (
// Command line arguments.
version bool
debug bool
utf8 bool
depth uint
nostrict bool
nocolor bool
)

func init() {
flag.BoolVar(&version, "version", false, "Show the version number and exit.")
flag.BoolVar(&debug, "debug", false, "Enable debug mode.")
flag.BoolVar(&utf8, "utf8", false, "Enable UTF-8 mode.")
flag.UintVar(&depth, "depth", 50, "Recursion depth limit (default 50)")
flag.BoolVar(&nostrict, "nostrict", false, "Disable strict syntax checking")
flag.BoolVar(&nocolor, "nocolor", false, "Disable ANSI colors")
}

func main() {
// Collect command line arguments.
version := flag.Bool("version", false, "Show the version number and exit.")
debug := flag.Bool("debug", false, "Enable debug mode.")
utf8 := flag.Bool("utf8", false, "Enable UTF-8 mode.")
depth := flag.Uint("depth", 50, "Recursion depth limit (default 50)")
nostrict := flag.Bool("nostrict", false, "Disable strict syntax checking")
flag.Parse()
args := flag.Args()

if *version == true {
if version {
fmt.Printf("RiveScript-Go version %s\n", rivescript.VERSION)
os.Exit(0)
}
Expand All @@ -51,10 +65,10 @@ func main() {

// Initialize the bot.
bot := rivescript.New(&rivescript.Config{
Debug: *debug,
Strict: !*nostrict,
Depth: *depth,
UTF8: *utf8,
Debug: debug,
Strict: !nostrict,
Depth: depth,
UTF8: utf8,
})

// JavaScript object macro handler.
Expand Down Expand Up @@ -85,31 +99,76 @@ Type a message to the bot and press Return to send it.
// Drop into the interactive command shell.
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("You> ")
color(yellow, "You>")
text, _ := reader.ReadString('\n')
text = strings.TrimSpace(text)
if len(text) == 0 {
continue
}

if strings.Index(text, "/help") == 0 {
if strings.Contains(text, "/help") {
help()
} else if strings.Index(text, "/quit") == 0 {
} else if strings.Contains(text, "/quit") {
os.Exit(0)
} else if strings.Contains(text, "/debug t") {
bot.SetGlobal("debug", "true")
color(cyan, "Debug mode enabled.", "\n")
} else if strings.Contains(text, "/debug f") {
bot.SetGlobal("debug", "false")
color(cyan, "Debug mode disabled.", "\n")
} else if strings.Contains(text, "/debug") {
debug, _ := bot.GetGlobal("debug")
color(cyan, "Debug mode is currently:", debug, "\n")
} else if strings.Contains(text, "/dump t") {
bot.DumpTopics()
} else if strings.Contains(text, "/dump s") {
bot.DumpSorted()
} else {
reply, err := bot.Reply("localuser", text)
if err != nil {
fmt.Printf("Error> %s\n", err)
color(red, "Error>", err.Error(), "\n")
} else {
fmt.Printf("Bot> %s\n", reply)
color(green, "RiveScript>", reply, "\n")
}
}
}
}

// Names for pretty ANSI colors.
const (
red = `31;1`
yellow = `33;1`
green = `32;1`
cyan = `36;1`
)

func color(color string, text ...string) {
if nocolor {
fmt.Printf(
"%s %s",
text[0],
strings.Join(text[1:], " "),
)
} else {
fmt.Printf(
"\x1b[%sm%s\x1b[0m %s",
color,
text[0],
strings.Join(text[1:], " "),
)
}
}

func help() {
fmt.Printf(`Supported commands:
- /help : Show this text.
- /quit : Exit the program.
- /help
Show this text.
- /quit
Exit the program.
- /debug [true|false]
Enable or disable debug mode. If no setting is given, it prints
the current debug mode.
- /dump <topics|sorted>
For debugging purposes, dump the topic and sorted trigger trees.
`)
}
4 changes: 2 additions & 2 deletions doc_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package rivescript
package rivescript_test

import (
"fmt"
Expand Down Expand Up @@ -108,7 +108,7 @@ func ExampleRiveScript_subroutine() {

// Define an object macro named `setname`
bot.SetSubroutine("setname", func(rs *rss.RiveScript, args []string) string {
uid := rs.CurrentUser()
uid, _ := rs.CurrentUser()
rs.SetUservar(uid, args[0], args[1])
return ""
})
Expand Down
4 changes: 1 addition & 3 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ type Parser struct {

// New creates and returns a new instance of a RiveScript Parser.
func New(config ParserConfig) *Parser {
self := new(Parser)
self.C = config
return self
return &Parser{config}
}

// say proxies to the OnDebug handler.
Expand Down
57 changes: 57 additions & 0 deletions rivescript_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package rivescript_test

// This test file contains the unit tests that had to be segregated from the
// others in the `src/` package.
//
// The only one here so far is an object macro test. It needed to use the public
// RiveScript API because the JavaScript handler expects an object of that type,
// and so it couldn't be in the `src/` package or it would create a dependency
// cycle.

import (
"testing"

rivescript "github.com/aichaos/rivescript-go"
"github.com/aichaos/rivescript-go/lang/javascript"
)

// This one has to test the public interface because of the JavaScript handler
// expecting a *RiveScript of the correct color.
func TestJavaScript(t *testing.T) {
rs := rivescript.New(nil)
rs.SetHandler("javascript", javascript.New(rs))
rs.Stream(`
> object reverse javascript
var msg = args.join(" ");
return msg.split("").reverse().join("");
< object
> object nolang
return "No language provided!"
< object
+ reverse *
- <call>reverse <star></call>
+ no lang
- <call>nolang</call>
`)
rs.SortReplies()

// Helper function to assert replies via the public interface.
assert := func(input, expected string) {
reply, err := rs.Reply("local-user", input)
if err != nil {
t.Errorf("Got error when trying to get a reply: %v", err)
} else if reply != expected {
t.Errorf("Got unexpected reply. Expected %s, got %s", expected, reply)
}
}

assert("reverse hello world", "dlrow olleh")
assert("no lang", "[ERR: Object Not Found]")

// Disable support.
rs.RemoveHandler("javascript")
assert("reverse hello world", "[ERR: Object Not Found]")
}
9 changes: 0 additions & 9 deletions src/astmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,3 @@ type astObject struct {
language string
code []string
}

// This is like astTopic but is just for %Previous mapping
type thatTopic struct {
triggers map[string]*thatTrigger
}

type thatTrigger struct {
previous map[string]*astTrigger
}
31 changes: 29 additions & 2 deletions src/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ package rivescript

import (
"errors"
"fmt"
"strconv"
"strings"

"github.com/aichaos/rivescript-go/macro"
"github.com/aichaos/rivescript-go/sessions"
Expand Down Expand Up @@ -54,6 +57,23 @@ func (rs *RiveScript) SetGlobal(name, value string) {
rs.cLock.Lock()
defer rs.cLock.Unlock()

// Special globals that reconfigure the interpreter.
if name == "debug" {
switch strings.ToLower(value) {
case "true", "t", "on", "yes":
rs.Debug = true
default:
rs.Debug = false
}
} else if name == "depth" {
depth, err := strconv.Atoi(value)
if err != nil {
rs.warn("Can't set global `depth` to `%s`: %s\n", value, err)
} else {
rs.Depth = uint(depth)
}
}

if value == UNDEFINED {
delete(rs.global, name)
} else {
Expand Down Expand Up @@ -114,10 +134,17 @@ func (rs *RiveScript) GetGlobal(name string) (string, error) {
rs.cLock.Lock()
defer rs.cLock.Unlock()

// Special globals.
if name == "debug" {
return fmt.Sprintf("%v", rs.Debug), nil
} else if name == "depth" {
return strconv.Itoa(int(rs.Depth)), nil
}

if _, ok := rs.global[name]; ok {
return rs.global[name], nil
}
return UNDEFINED, errors.New("Global variable not found.")
return UNDEFINED, fmt.Errorf("global variable %s not found", name)
}

// GetVariable retrieves the value of a bot variable.
Expand All @@ -128,7 +155,7 @@ func (rs *RiveScript) GetVariable(name string) (string, error) {
if _, ok := rs.vars[name]; ok {
return rs.vars[name], nil
}
return UNDEFINED, errors.New("Variable not found.")
return UNDEFINED, fmt.Errorf("bot variable %s not found", name)
}

// GetUservar retrieves the value of a user variable.
Expand Down
Loading

0 comments on commit 3d36e5b

Please sign in to comment.