123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- package picol
- import (
- "errors"
- "fmt"
- "strings"
- )
- var (
- PICOL_RETURN = errors.New("RETURN")
- PICOL_BREAK = errors.New("BREAK")
- PICOL_CONTINUE = errors.New("CONTINUE")
- )
- type Var string
- type CmdFunc func(i *Interp, argv []string, privdata interface{}) (string, error)
- type Cmd struct {
- fn CmdFunc
- privdata interface{}
- }
- type CallFrame struct {
- vars map[string]Var
- parent *CallFrame
- }
- type Interp struct {
- level int
- callframe *CallFrame
- commands map[string]Cmd
- }
- func InitInterp() *Interp {
- return &Interp{
- level: 0,
- callframe: &CallFrame{vars: make(map[string]Var)},
- commands: make(map[string]Cmd),
- }
- }
- func (i *Interp) Var(name string) (Var, bool) {
- for frame := i.callframe; frame != nil; frame = frame.parent {
- v, ok := frame.vars[name]
- if ok {
- return v, ok
- }
- }
- return "", false
- }
- func (i *Interp) SetVar(name, val string) {
- i.callframe.vars[name] = Var(val)
- }
- func (i *Interp) UnsetVar(name string) {
- delete(i.callframe.vars, name)
- }
- func (i *Interp) Command(name string) *Cmd {
- v, ok := i.commands[name]
- if !ok {
- return nil
- }
- return &v
- }
- func (i *Interp) RegisterCommand(name string, fn CmdFunc, privdata interface{}) error {
- c := i.Command(name)
- if c != nil {
- return fmt.Errorf("Command '%s' already defined", name)
- }
- i.commands[name] = Cmd{fn, privdata}
- return nil
- }
- /* EVAL! */
- func (i *Interp) Eval(t string) (string, error) {
- p := InitParser(t)
- var result string
- var err error
- argv := []string{}
- for {
- prevtype := p.Type
- // XXX
- t = p.GetToken()
- if p.Type == PT_EOF {
- break
- }
- switch p.Type {
- case PT_VAR:
- v, ok := i.Var(t)
- if !ok {
- return "", fmt.Errorf("No such variable '%s'", t)
- }
- t = string(v)
- case PT_CMD:
- result, err = i.Eval(t)
- if err != nil {
- return result, err
- } else {
- t = result
- }
- case PT_ESC:
- // XXX: escape handling missing!
- case PT_SEP:
- prevtype = p.Type
- continue
- }
- // We have a complete command + args. Call it!
- if p.Type == PT_EOL {
- prevtype = p.Type
- if len(argv) != 0 {
- c := i.Command(argv[0])
- if c == nil {
- return "", fmt.Errorf("No such command '%s'", argv[0])
- }
- result, err = c.fn(i, argv, c.privdata)
- if err != nil {
- return result, err
- }
- }
- // Prepare for the next command
- argv = []string{}
- continue
- }
- // We have a new token, append to the previous or as new arg?
- if prevtype == PT_SEP || prevtype == PT_EOL {
- argv = append(argv, t)
- } else { // Interpolation
- argv[len(argv)-1] = strings.Join([]string{argv[len(argv)-1], t}, "")
- }
- prevtype = p.Type
- }
- return result, nil
- }
|