parser.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package picol
  2. import (
  3. "unicode"
  4. "unicode/utf8"
  5. )
  6. const (
  7. PT_ESC = iota
  8. PT_STR
  9. PT_CMD
  10. PT_VAR
  11. PT_SEP
  12. PT_EOL
  13. PT_EOF
  14. )
  15. type Parser struct {
  16. text string
  17. p, start, end, ln int
  18. insidequote int
  19. Type int
  20. }
  21. func InitParser(text string) *Parser {
  22. return &Parser{text, 0, 0, 0, len(text), 0, PT_EOL}
  23. }
  24. func (p *Parser) next() {
  25. _, w := utf8.DecodeRuneInString(p.text[p.p:])
  26. p.p += w
  27. p.ln -= w
  28. }
  29. func (p *Parser) current() rune {
  30. r, _ := utf8.DecodeRuneInString(p.text[p.p:])
  31. return r
  32. }
  33. func (p *Parser) token() (t string) {
  34. defer recover()
  35. return p.text[p.start:p.end]
  36. }
  37. func (p *Parser) parseSep() string {
  38. p.start = p.p
  39. for ; p.p < len(p.text); p.next() {
  40. if !unicode.IsSpace(p.current()) {
  41. break
  42. }
  43. }
  44. p.end = p.p
  45. p.Type = PT_SEP
  46. return p.token()
  47. }
  48. func (p *Parser) parseEol() string {
  49. p.start = p.p
  50. for ; p.p < len(p.text); p.next() {
  51. if p.current() == ';' || unicode.IsSpace(p.current()) {
  52. // pass
  53. } else {
  54. break
  55. }
  56. }
  57. p.end = p.p
  58. p.Type = PT_EOL
  59. return p.token()
  60. }
  61. func (p *Parser) parseCommand() string {
  62. level, blevel := 1, 0
  63. p.next() // skip
  64. p.start = p.p
  65. Loop:
  66. for {
  67. switch {
  68. case p.ln == 0:
  69. break Loop
  70. case p.current() == '[' && blevel == 0:
  71. level++
  72. case p.current() == ']' && blevel == 0:
  73. level--
  74. if level == 0 {
  75. break Loop
  76. }
  77. case p.current() == '\\':
  78. p.next()
  79. case p.current() == '{':
  80. blevel++
  81. case p.current() == '}' && blevel != 0:
  82. blevel--
  83. }
  84. p.next()
  85. }
  86. p.end = p.p
  87. p.Type = PT_CMD
  88. if p.p < len(p.text) && p.current() == ']' {
  89. p.next()
  90. }
  91. return p.token()
  92. }
  93. func (p *Parser) parseVar() string {
  94. p.next() // skip the $
  95. p.start = p.p
  96. if p.current() == '{' {
  97. p.Type = PT_VAR
  98. return p.parseBrace()
  99. }
  100. for p.p < len(p.text) {
  101. c := p.current()
  102. if unicode.IsLetter(c) || ('0' <= c && c <= '9') || c == '_' {
  103. p.next()
  104. continue
  105. }
  106. break
  107. }
  108. if p.start == p.p { // It's just a single char string "$"
  109. p.start = p.p - 1
  110. p.end = p.p
  111. p.Type = PT_STR
  112. } else {
  113. p.end = p.p
  114. p.Type = PT_VAR
  115. }
  116. return p.token()
  117. }
  118. func (p *Parser) parseBrace() string {
  119. level := 1
  120. p.next() // skip
  121. p.start = p.p
  122. Loop:
  123. for p.p < len(p.text) {
  124. c := p.current()
  125. switch {
  126. case p.ln >= 2 && c == '\\':
  127. p.next()
  128. case p.ln == 0 || c == '}':
  129. level--
  130. if level == 0 || p.ln == 0 {
  131. break Loop
  132. }
  133. case c == '{':
  134. level++
  135. }
  136. p.next()
  137. }
  138. p.end = p.p
  139. if p.ln != 0 { // Skip final closed brace
  140. p.next()
  141. }
  142. return p.token()
  143. }
  144. func (p *Parser) parseString() string {
  145. newword := p.Type == PT_SEP || p.Type == PT_EOL || p.Type == PT_STR
  146. if c := p.current(); newword && c == '{' {
  147. p.Type = PT_STR
  148. return p.parseBrace()
  149. } else if newword && c == '"' {
  150. p.insidequote = 1
  151. p.next() // skip
  152. }
  153. p.start = p.p
  154. Loop:
  155. for ; p.ln != 0; p.next() {
  156. switch p.current() {
  157. case '\\':
  158. if p.ln >= 2 {
  159. p.next()
  160. }
  161. case '$', '[':
  162. break Loop
  163. case '"':
  164. if p.insidequote != 0 {
  165. p.end = p.p
  166. p.Type = PT_ESC
  167. p.next()
  168. p.insidequote = 0
  169. return p.token()
  170. }
  171. }
  172. if p.current() == ';' || unicode.IsSpace(p.current()) {
  173. if p.insidequote == 0 {
  174. break Loop
  175. }
  176. }
  177. }
  178. p.end = p.p
  179. p.Type = PT_ESC
  180. return p.token()
  181. }
  182. func (p *Parser) parseComment() string {
  183. for p.ln != 0 && p.current() != '\n' {
  184. p.next()
  185. }
  186. return p.token()
  187. }
  188. func (p *Parser) GetToken() string {
  189. for {
  190. if p.ln == 0 {
  191. if p.Type != PT_EOL && p.Type != PT_EOF {
  192. p.Type = PT_EOL
  193. } else {
  194. p.Type = PT_EOF
  195. }
  196. return p.token()
  197. }
  198. switch p.current() {
  199. case ' ', '\t', '\r':
  200. if p.insidequote != 0 {
  201. return p.parseString()
  202. }
  203. return p.parseSep()
  204. case '\n', ';':
  205. if p.insidequote != 0 {
  206. return p.parseString()
  207. }
  208. return p.parseEol()
  209. case '[':
  210. return p.parseCommand()
  211. case '$':
  212. return p.parseVar()
  213. case '#':
  214. if p.Type == PT_EOL {
  215. p.parseComment()
  216. continue
  217. }
  218. return p.parseString()
  219. default:
  220. return p.parseString()
  221. }
  222. }
  223. return p.token() /* unreached */
  224. }