parser.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // Copyright 2012 Neal van Veen. All rights reserved.
  2. // Usage of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. package gotty
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. )
  13. var exp = [...]string{
  14. "%%",
  15. "%c",
  16. "%s",
  17. "%p(\\d)",
  18. "%P([A-z])",
  19. "%g([A-z])",
  20. "%'(.)'",
  21. "%{([0-9]+)}",
  22. "%l",
  23. "%\\+|%-|%\\*|%/|%m",
  24. "%&|%\\||%\\^",
  25. "%=|%>|%<",
  26. "%A|%O",
  27. "%!|%~",
  28. "%i",
  29. "%(:[\\ #\\-\\+]{0,4})?(\\d+\\.\\d+|\\d+)?[doxXs]",
  30. "%\\?(.*?);",
  31. }
  32. var regex *regexp.Regexp
  33. var staticVar map[byte]stacker
  34. // Parses the attribute that is received with name attr and parameters params.
  35. func (term *TermInfo) Parse(attr string, params ...interface{}) (string, error) {
  36. // Get the attribute name first.
  37. iface, err := term.GetAttribute(attr)
  38. str, ok := iface.(string)
  39. if err != nil {
  40. return "", err
  41. }
  42. if !ok {
  43. return str, errors.New("Only string capabilities can be parsed.")
  44. }
  45. // Construct the hidden parser struct so we can use a recursive stack based
  46. // parser.
  47. ps := &parser{}
  48. // Dynamic variables only exist in this context.
  49. ps.dynamicVar = make(map[byte]stacker, 26)
  50. ps.parameters = make([]stacker, len(params))
  51. // Convert the parameters to insert them into the parser struct.
  52. for i, x := range params {
  53. ps.parameters[i] = x
  54. }
  55. // Recursively walk and return.
  56. result, err := ps.walk(str)
  57. return result, err
  58. }
  59. // Parses the attribute that is received with name attr and parameters params.
  60. // Only works on full name of a capability that is given, which it uses to
  61. // search for the termcap name.
  62. func (term *TermInfo) ParseName(attr string, params ...interface{}) (string, error) {
  63. tc := GetTermcapName(attr)
  64. return term.Parse(tc, params)
  65. }
  66. // Identify each token in a stack based manner and do the actual parsing.
  67. func (ps *parser) walk(attr string) (string, error) {
  68. // We use a buffer to get the modified string.
  69. var buf bytes.Buffer
  70. // Next, find and identify all tokens by their indices and strings.
  71. tokens := regex.FindAllStringSubmatch(attr, -1)
  72. if len(tokens) == 0 {
  73. return attr, nil
  74. }
  75. indices := regex.FindAllStringIndex(attr, -1)
  76. q := 0 // q counts the matches of one token
  77. // Iterate through the string per character.
  78. for i := 0; i < len(attr); i++ {
  79. // If the current position is an identified token, execute the following
  80. // steps.
  81. if q < len(indices) && i >= indices[q][0] && i < indices[q][1] {
  82. // Switch on token.
  83. switch {
  84. case tokens[q][0][:2] == "%%":
  85. // Literal percentage character.
  86. buf.WriteByte('%')
  87. case tokens[q][0][:2] == "%c":
  88. // Pop a character.
  89. c, err := ps.st.pop()
  90. if err != nil {
  91. return buf.String(), err
  92. }
  93. buf.WriteByte(c.(byte))
  94. case tokens[q][0][:2] == "%s":
  95. // Pop a string.
  96. str, err := ps.st.pop()
  97. if err != nil {
  98. return buf.String(), err
  99. }
  100. if _, ok := str.(string); !ok {
  101. return buf.String(), errors.New("Stack head is not a string")
  102. }
  103. buf.WriteString(str.(string))
  104. case tokens[q][0][:2] == "%p":
  105. // Push a parameter on the stack.
  106. index, err := strconv.ParseInt(tokens[q][1], 10, 8)
  107. index--
  108. if err != nil {
  109. return buf.String(), err
  110. }
  111. if int(index) >= len(ps.parameters) {
  112. return buf.String(), errors.New("Parameters index out of bound")
  113. }
  114. ps.st.push(ps.parameters[index])
  115. case tokens[q][0][:2] == "%P":
  116. // Pop a variable from the stack as a dynamic or static variable.
  117. val, err := ps.st.pop()
  118. if err != nil {
  119. return buf.String(), err
  120. }
  121. index := tokens[q][2]
  122. if len(index) > 1 {
  123. errorStr := fmt.Sprintf("%s is not a valid dynamic variables index",
  124. index)
  125. return buf.String(), errors.New(errorStr)
  126. }
  127. // Specify either dynamic or static.
  128. if index[0] >= 'a' && index[0] <= 'z' {
  129. ps.dynamicVar[index[0]] = val
  130. } else if index[0] >= 'A' && index[0] <= 'Z' {
  131. staticVar[index[0]] = val
  132. }
  133. case tokens[q][0][:2] == "%g":
  134. // Push a variable from the stack as a dynamic or static variable.
  135. index := tokens[q][3]
  136. if len(index) > 1 {
  137. errorStr := fmt.Sprintf("%s is not a valid static variables index",
  138. index)
  139. return buf.String(), errors.New(errorStr)
  140. }
  141. var val stacker
  142. if index[0] >= 'a' && index[0] <= 'z' {
  143. val = ps.dynamicVar[index[0]]
  144. } else if index[0] >= 'A' && index[0] <= 'Z' {
  145. val = staticVar[index[0]]
  146. }
  147. ps.st.push(val)
  148. case tokens[q][0][:2] == "%'":
  149. // Push a character constant.
  150. con := tokens[q][4]
  151. if len(con) > 1 {
  152. errorStr := fmt.Sprintf("%s is not a valid character constant", con)
  153. return buf.String(), errors.New(errorStr)
  154. }
  155. ps.st.push(con[0])
  156. case tokens[q][0][:2] == "%{":
  157. // Push an integer constant.
  158. con, err := strconv.ParseInt(tokens[q][5], 10, 32)
  159. if err != nil {
  160. return buf.String(), err
  161. }
  162. ps.st.push(con)
  163. case tokens[q][0][:2] == "%l":
  164. // Push the length of the string that is popped from the stack.
  165. popStr, err := ps.st.pop()
  166. if err != nil {
  167. return buf.String(), err
  168. }
  169. if _, ok := popStr.(string); !ok {
  170. errStr := fmt.Sprintf("Stack head is not a string")
  171. return buf.String(), errors.New(errStr)
  172. }
  173. ps.st.push(len(popStr.(string)))
  174. case tokens[q][0][:2] == "%?":
  175. // If-then-else construct. First, the whole string is identified and
  176. // then inside this substring, we can specify which parts to switch on.
  177. ifReg, _ := regexp.Compile("%\\?(.*)%t(.*)%e(.*);|%\\?(.*)%t(.*);")
  178. ifTokens := ifReg.FindStringSubmatch(tokens[q][0])
  179. var (
  180. ifStr string
  181. err error
  182. )
  183. // Parse the if-part to determine if-else.
  184. if len(ifTokens[1]) > 0 {
  185. ifStr, err = ps.walk(ifTokens[1])
  186. } else { // else
  187. ifStr, err = ps.walk(ifTokens[4])
  188. }
  189. // Return any errors
  190. if err != nil {
  191. return buf.String(), err
  192. } else if len(ifStr) > 0 {
  193. // Self-defined limitation, not sure if this is correct, but didn't
  194. // seem like it.
  195. return buf.String(), errors.New("If-clause cannot print statements")
  196. }
  197. var thenStr string
  198. // Pop the first value that is set by parsing the if-clause.
  199. choose, err := ps.st.pop()
  200. if err != nil {
  201. return buf.String(), err
  202. }
  203. // Switch to if or else.
  204. if choose.(int) == 0 && len(ifTokens[1]) > 0 {
  205. thenStr, err = ps.walk(ifTokens[3])
  206. } else if choose.(int) != 0 {
  207. if len(ifTokens[1]) > 0 {
  208. thenStr, err = ps.walk(ifTokens[2])
  209. } else {
  210. thenStr, err = ps.walk(ifTokens[5])
  211. }
  212. }
  213. if err != nil {
  214. return buf.String(), err
  215. }
  216. buf.WriteString(thenStr)
  217. case tokens[q][0][len(tokens[q][0])-1] == 'd': // Fallthrough for printing
  218. fallthrough
  219. case tokens[q][0][len(tokens[q][0])-1] == 'o': // digits.
  220. fallthrough
  221. case tokens[q][0][len(tokens[q][0])-1] == 'x':
  222. fallthrough
  223. case tokens[q][0][len(tokens[q][0])-1] == 'X':
  224. fallthrough
  225. case tokens[q][0][len(tokens[q][0])-1] == 's':
  226. token := tokens[q][0]
  227. // Remove the : that comes before a flag.
  228. if token[1] == ':' {
  229. token = token[:1] + token[2:]
  230. }
  231. digit, err := ps.st.pop()
  232. if err != nil {
  233. return buf.String(), err
  234. }
  235. // The rest is determined like the normal formatted prints.
  236. digitStr := fmt.Sprintf(token, digit.(int))
  237. buf.WriteString(digitStr)
  238. case tokens[q][0][:2] == "%i":
  239. // Increment the parameters by one.
  240. if len(ps.parameters) < 2 {
  241. return buf.String(), errors.New("Not enough parameters to increment.")
  242. }
  243. val1, val2 := ps.parameters[0].(int), ps.parameters[1].(int)
  244. val1++
  245. val2++
  246. ps.parameters[0], ps.parameters[1] = val1, val2
  247. default:
  248. // The rest of the tokens is a special case, where two values are
  249. // popped and then operated on by the token that comes after them.
  250. op1, err := ps.st.pop()
  251. if err != nil {
  252. return buf.String(), err
  253. }
  254. op2, err := ps.st.pop()
  255. if err != nil {
  256. return buf.String(), err
  257. }
  258. var result stacker
  259. switch tokens[q][0][:2] {
  260. case "%+":
  261. // Addition
  262. result = op2.(int) + op1.(int)
  263. case "%-":
  264. // Subtraction
  265. result = op2.(int) - op1.(int)
  266. case "%*":
  267. // Multiplication
  268. result = op2.(int) * op1.(int)
  269. case "%/":
  270. // Division
  271. result = op2.(int) / op1.(int)
  272. case "%m":
  273. // Modulo
  274. result = op2.(int) % op1.(int)
  275. case "%&":
  276. // Bitwise AND
  277. result = op2.(int) & op1.(int)
  278. case "%|":
  279. // Bitwise OR
  280. result = op2.(int) | op1.(int)
  281. case "%^":
  282. // Bitwise XOR
  283. result = op2.(int) ^ op1.(int)
  284. case "%=":
  285. // Equals
  286. result = op2 == op1
  287. case "%>":
  288. // Greater-than
  289. result = op2.(int) > op1.(int)
  290. case "%<":
  291. // Lesser-than
  292. result = op2.(int) < op1.(int)
  293. case "%A":
  294. // Logical AND
  295. result = op2.(bool) && op1.(bool)
  296. case "%O":
  297. // Logical OR
  298. result = op2.(bool) || op1.(bool)
  299. case "%!":
  300. // Logical complement
  301. result = !op1.(bool)
  302. case "%~":
  303. // Bitwise complement
  304. result = ^(op1.(int))
  305. }
  306. ps.st.push(result)
  307. }
  308. i = indices[q][1] - 1
  309. q++
  310. } else {
  311. // We are not "inside" a token, so just skip until the end or the next
  312. // token, and add all characters to the buffer.
  313. j := i
  314. if q != len(indices) {
  315. for !(j >= indices[q][0] && j < indices[q][1]) {
  316. j++
  317. }
  318. } else {
  319. j = len(attr)
  320. }
  321. buf.WriteString(string(attr[i:j]))
  322. i = j
  323. }
  324. }
  325. // Return the buffer as a string.
  326. return buf.String(), nil
  327. }
  328. // Push a stacker-value onto the stack.
  329. func (st *stack) push(s stacker) {
  330. *st = append(*st, s)
  331. }
  332. // Pop a stacker-value from the stack.
  333. func (st *stack) pop() (stacker, error) {
  334. if len(*st) == 0 {
  335. return nil, errors.New("Stack is empty.")
  336. }
  337. newStack := make(stack, len(*st)-1)
  338. val := (*st)[len(*st)-1]
  339. copy(newStack, (*st)[:len(*st)-1])
  340. *st = newStack
  341. return val, nil
  342. }
  343. // Initialize regexes and the static vars (that don't get changed between
  344. // calls.
  345. func init() {
  346. // Initialize the main regex.
  347. expStr := strings.Join(exp[:], "|")
  348. regex, _ = regexp.Compile(expStr)
  349. // Initialize the static variables.
  350. staticVar = make(map[byte]stacker, 26)
  351. }