picol.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package picol
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. )
  7. var (
  8. PICOL_RETURN = errors.New("RETURN")
  9. PICOL_BREAK = errors.New("BREAK")
  10. PICOL_CONTINUE = errors.New("CONTINUE")
  11. )
  12. type Var string
  13. type CmdFunc func(i *Interp, argv []string, privdata interface{}) (string, error)
  14. type Cmd struct {
  15. fn CmdFunc
  16. privdata interface{}
  17. }
  18. type CallFrame struct {
  19. vars map[string]Var
  20. parent *CallFrame
  21. }
  22. type Interp struct {
  23. level int
  24. callframe *CallFrame
  25. commands map[string]Cmd
  26. }
  27. func InitInterp() *Interp {
  28. return &Interp{
  29. level: 0,
  30. callframe: &CallFrame{vars: make(map[string]Var)},
  31. commands: make(map[string]Cmd),
  32. }
  33. }
  34. func (i *Interp) Var(name string) (Var, bool) {
  35. for frame := i.callframe; frame != nil; frame = frame.parent {
  36. v, ok := frame.vars[name]
  37. if ok {
  38. return v, ok
  39. }
  40. }
  41. return "", false
  42. }
  43. func (i *Interp) SetVar(name, val string) {
  44. i.callframe.vars[name] = Var(val)
  45. }
  46. func (i *Interp) UnsetVar(name string) {
  47. delete(i.callframe.vars, name)
  48. }
  49. func (i *Interp) Command(name string) *Cmd {
  50. v, ok := i.commands[name]
  51. if !ok {
  52. return nil
  53. }
  54. return &v
  55. }
  56. func (i *Interp) RegisterCommand(name string, fn CmdFunc, privdata interface{}) error {
  57. c := i.Command(name)
  58. if c != nil {
  59. return fmt.Errorf("Command '%s' already defined", name)
  60. }
  61. i.commands[name] = Cmd{fn, privdata}
  62. return nil
  63. }
  64. /* EVAL! */
  65. func (i *Interp) Eval(t string) (string, error) {
  66. p := InitParser(t)
  67. var result string
  68. var err error
  69. argv := []string{}
  70. for {
  71. prevtype := p.Type
  72. // XXX
  73. t = p.GetToken()
  74. if p.Type == PT_EOF {
  75. break
  76. }
  77. switch p.Type {
  78. case PT_VAR:
  79. v, ok := i.Var(t)
  80. if !ok {
  81. return "", fmt.Errorf("No such variable '%s'", t)
  82. }
  83. t = string(v)
  84. case PT_CMD:
  85. result, err = i.Eval(t)
  86. if err != nil {
  87. return result, err
  88. } else {
  89. t = result
  90. }
  91. case PT_ESC:
  92. // XXX: escape handling missing!
  93. case PT_SEP:
  94. prevtype = p.Type
  95. continue
  96. }
  97. // We have a complete command + args. Call it!
  98. if p.Type == PT_EOL {
  99. prevtype = p.Type
  100. if len(argv) != 0 {
  101. c := i.Command(argv[0])
  102. if c == nil {
  103. return "", fmt.Errorf("No such command '%s'", argv[0])
  104. }
  105. result, err = c.fn(i, argv, c.privdata)
  106. if err != nil {
  107. return result, err
  108. }
  109. }
  110. // Prepare for the next command
  111. argv = []string{}
  112. continue
  113. }
  114. // We have a new token, append to the previous or as new arg?
  115. if prevtype == PT_SEP || prevtype == PT_EOL {
  116. argv = append(argv, t)
  117. } else { // Interpolation
  118. argv[len(argv)-1] = strings.Join([]string{argv[len(argv)-1], t}, "")
  119. }
  120. prevtype = p.Type
  121. }
  122. return result, nil
  123. }