Package gribble

import "github.com/BurntSushi/gribble"
Overview
Index
Subdirectories

Overview ▾

Package gribble provides a command oriented language whose environment of commands is defined by Go structs via reflection. The primary use case for Gribble is to provide an easy to use command language for users to interact with your program.

For example, to define an environment with an "Add" command that adds two operators and print the output of the command "Add (Add 1 2) 42":

package main

import (
	"fmt"
	"github.com/BurntSushi/gribble"
)

type Add struct {
	Op1 int `param:"1"`
	Op2 int `param:"2"`
}

func (cmd Add) Run() gribble.Value {
	return cmd.Op1 + cmd.Op2
}

func main() {
	env := gribble.New([]gribble.Command{Add{}})
	val, _ := env.Run("Add (Add 1 2) 42")
	fmt.Println(val)
}

How to define commands

Commands that make up a Gribble environment are struct values that implement the 'Command' interface. A minimal command with no parameters could be defined as follows:

type Minimal struct {}
func (cmd Minimal) Run() gribble.Value {
	return "Hello"
}

Parameters are added by defining a new struct field to hold that parameter's value, and labeling it with a parameter number using struct tags:

type Negation struct {
	Op int `param:"1"`
}

Struct tags MUST be used to define parameters. For example, the following command has ZERO parameters:

type ZeroParameters struct {
	something string
}

Parameters may also accept more than one type of argument by defining the parameter struct field to have type 'gribble.Any' and specifying the allowable types with the 'types' struct tag:

type Negation struct {
	Op gribble.Any `param:"1" types:"int,float"`
}

And the value of the parameter can be safely type switched on only the types specified when running the command:

func (cmd Negation) Run() gribble.Value {
	switch val := cmd.Op.(type) {
	case int:
		return -val
	case float64:
		return -val
	}
	panic("unreachable")
}

Gribble enforces the invariant that 'Op' must only contain values with concrete type 'int' or 'float64' by returning an error if a type not specified in the 'types' struct tag is found.

Currently, the only types allowed in the 'types' struct tag are int, float and string.

Command names

Command names must be Go identifiers and are automatically set to the name of the struct. Alternatively, the name can be overridden by adding a 'name' field with the desired name in the struct tag:

type ThisIsNotTheName struct {
	name string `TheName`
	Op int `param:"1"`
}

Where the command is invoked using 'TheName'.

Errors

Gribble will panic when any of the invariants involved in defining commands are broken. This includes, but is not limited to: Specifying non-contiguous parameter numbers. Using a type for a parameter other than int, float64, string or gribble.Any. Specifying a type other than int, float or string in the 'types' struct tag. Using the gribble.Any type without the 'types' struct tag or with a 'types' struct tag with only one type specified. Using a sub-command that returns a concrete value other than an int, float64 or string.

Other errors like parse errors or run-time type errors are returned as standard Go errors when using the 'Run' or 'Command' methods.

Gribble EBNF

What follows is an EBNF grammar of the Gribble language. (Bug reports are welcome!)

Names of the format 'go-NAME' refer to lexical elements called NAME in the Go Programming Language Specification.

program = command ;

command = [ "(" ], go-identifier, { param }, [ ")" ] ;

param = go-string_lit | go-int_lit | go-float_lit | "(" command ")" ;

Installation

Gribble is go gettable:

go get github.com/BurntSushi/gribble

Quick example

To demonstrate the bundled integer calculator:

go get github.com/BurntSushi/gribble
GO/PATH/bin/int-calc 'add 5 (mul 2 6)'

The output should be '17'.

Why

The initial motivation for Gribble was for interacting with my window manager, Wingo (which is where the name Gribble came from). Wingo initially had a hacked together set of commands, but it quickly grew out of control and incredibly difficult to maintain. Having a language that is definable using just Go structs makes everything a lot cleaner.

type Any

type Any interface{}

Any is an empty interface that can be used as a type for parameters that can accept values of more than one type. When using the 'Any' type as a parameter type, the struct tag 'types' MUST be set.

type Command

type Command interface {
    Run() Value
}

Command is an interface that all commands in the Gribble environment must satisfy. Namely, every command must define a 'Run' method that returns some 'Value'. The 'Run' method ought to perform the computation required by the command.

For example, consider an 'add' command with two parameters:

type Add struct {
	Op1 int `param:"1"`
	Op2 int `param:"2"`
}
func (cmd *Add) Run() gribble.Value {
	return c.Op1 + c.Op2
}

Where 'Value' is an empty interface whose concrete type is guaranteed to be an int, float64 or string by the Gribble runtime.

type Environment

type Environment struct {
    // Verbose controls the detail of error messages from the Gribble parser.
    // When true (the default value), a single error message may span
    // multiple lines, and will attempt to pinpoint exactly where an error
    // is occurring.
    // When false, a single error message will always be on one line.
    // This value may be changed at any time.
    Verbose bool
    // contains filtered or unexported fields
}

Environment represents the set of all commands available to the Gribble run-time. A new Environment should be created with gribble.New.

func New

func New(commands []Command) *Environment

New creates a new Gribble environment with the given list of values that implement the Command interface. An environment is returned with which commands can be executed. An environment can also be queried for usage information for a particular command in the environment.

There is currently no way to add or remove commands from an environment once it is created.

func (*Environment) ArgNames

func (env *Environment) ArgNames(cmdName string) []string

ArgNames returns a list of names for the arguments of a command. (These correspond to the names of the struct members.)

This function panics if it's given a command name that doesn't exist.

func (*Environment) ArgTypes

func (env *Environment) ArgTypes(cmdName string) [][]string

ArgTypes returns a list of valid argument types for the command given. Each argument may have more than one valid type. The possible types are "int", "float" or "string". The first argument's types are found at index 0, the second at index 1, and so on.

This function panics if it's given a command name that doesn't exist.

func (*Environment) Check

func (env *Environment) Check(cmd string) error

Check attempts to parse a command and look it up in the environment. If either fails, an error is returned.

func (*Environment) Command

func (env *Environment) Command(cmd string) (Command, error)

Command returns a value implementing the Command interface that is ready to be executed. In particular, the concrete struct underlying the Command interface has had its values filled with literals specified in the "cmd" string or by values returned from sub-commands.

This method exists in the event that a particular command requires additional information at run-time that cannot be captured by Gribble, or if a command needs to be stored and executed later.

As a contrived example, consider the case when SomeOp differs depending upon who is running it:

package main

import (
	"fmt"
	"github.com/BurntSushi/gribble"
)

type SomeOp struct {
	Op1 int `param:"1"`
	Op2 int `param:"2"`
	who string
}

func (cmd *SomeOp) Run() gribble.Value {
	switch cmd.who {
	case "Andrew":
		return cmd.Op1 * cmd.Op2
	default:
		return cmd.Op1 + cmd.Op2
	}
	panic("unreachable")
}

func main() {
	env := gribble.New([]gribble.Command{&SomeOp{}})
	cmd, _ := env.Command("SomeOp 2 4")
	someOp := cmd.(*SomeOp)

	someOp.who = "Andrew"
	fmt.Println(someOp.Run()) // outputs "8"
	someOp.who = "Someone Else"
	fmt.Println(someOp.Run()) // outputs "6"
}

Note that due to the fact that commands themselves don't know if values have come from sub-commands or literals, this sort of state injection is only possible with top-level commands. (More precisely, both (*Environment).Run and (*Environment).Command execute all sub-commands for you.)

func (*Environment) CommandName

func (env *Environment) CommandName(cmd string) string

CommandName is a convenience method for returning the name of the command used in the command string 'cmd'. The important aspect of this method is that it is impervious to most kinds of errors, including parse errors (so long as the parse error doesn't occur in the command name itself, in which case, the returned string will be empty).

func (*Environment) Commands

func (env *Environment) Commands(cmds string) ([]Command, error)

func (*Environment) Each

func (env *Environment) Each(f func(name, help string))

Each calls 'f' on each command in the environment.

func (*Environment) Help

func (env *Environment) Help(cmdName string) string

Help returns the help message (if one exists) in a command struct. Help messages are located in the struct tag of a 'help' field with type string.

func (*Environment) Run

func (env *Environment) Run(cmd string) (Value, error)

Run will execute a given command in the provided environment. An error can occur when either parsing or running the command.

The return value of all Gribble commands must implement the Value interface, which is empty. The conrete types of values returned are int, float64 or string. This is enforced by the Gribble run-time.

func (*Environment) RunMany

func (env *Environment) RunMany(cmds string) (Value, error)

func (*Environment) String

func (env *Environment) String() string

String outputs a listing of all commands in the environment.

func (*Environment) StringTypes

func (env *Environment) StringTypes() string

StringTypes outputs a listing of all commands with parameter types in the environment.

func (*Environment) Usage

func (env *Environment) Usage(cmdName string) string

Usage returns a usage string derived from the command struct, including the command and parameter names.

func (*Environment) UsageTypes

func (env *Environment) UsageTypes(cmdName string) string

Usage returns a usage string derived from the command struct, including the command and parameter names, along with the allowable types for each parameter.

type Value

type Value interface{}

Value is a named empty interface that corresponds to the type of any value returned by a Gribble command. All Value values returned by sub-commands must have a concrete type of int, float64 or string. If this invariant is violated, Gribble will panic with a run-time type error.

Subdirectories

Name      Synopsis
..
examples     
     float-calc      Example float-calc demonstrates a simple floating point calculator with commands defined using Gribble.
     int-calc      Example int-calc demonstrates a simple integer calculator with commands defined using Gribble.