transition_table.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package parser
  2. // Table values are generated like this:
  3. //
  4. // index: currentState << IndexStateShift | charCode
  5. // value: action << TransitionActionShift | nextState
  6. const (
  7. TransitionActionShift = 4
  8. TransitionStateMask = 15
  9. IndexStateShift = 8
  10. // DefaultTableSize is the default size of the transition table.
  11. DefaultTableSize = 4096
  12. )
  13. // Table is a DEC ANSI transition table.
  14. var Table = GenerateTransitionTable()
  15. // TransitionTable is a DEC ANSI transition table.
  16. // https://vt100.net/emu/dec_ansi_parser
  17. type TransitionTable []byte
  18. // NewTransitionTable returns a new DEC ANSI transition table.
  19. func NewTransitionTable(size int) TransitionTable {
  20. if size <= 0 {
  21. size = DefaultTableSize
  22. }
  23. return TransitionTable(make([]byte, size))
  24. }
  25. // SetDefault sets default transition.
  26. func (t TransitionTable) SetDefault(action Action, state State) {
  27. for i := 0; i < len(t); i++ {
  28. t[i] = action<<TransitionActionShift | state
  29. }
  30. }
  31. // AddOne adds a transition.
  32. func (t TransitionTable) AddOne(code byte, state State, action Action, next State) {
  33. idx := int(state)<<IndexStateShift | int(code)
  34. value := action<<TransitionActionShift | next
  35. t[idx] = value
  36. }
  37. // AddMany adds many transitions.
  38. func (t TransitionTable) AddMany(codes []byte, state State, action Action, next State) {
  39. for _, code := range codes {
  40. t.AddOne(code, state, action, next)
  41. }
  42. }
  43. // AddRange adds a range of transitions.
  44. func (t TransitionTable) AddRange(start, end byte, state State, action Action, next State) {
  45. for i := int(start); i <= int(end); i++ {
  46. t.AddOne(byte(i), state, action, next)
  47. }
  48. }
  49. // Transition returns the next state and action for the given state and byte.
  50. func (t TransitionTable) Transition(state State, code byte) (State, Action) {
  51. index := int(state)<<IndexStateShift | int(code)
  52. value := t[index]
  53. return value & TransitionStateMask, value >> TransitionActionShift
  54. }
  55. // byte range macro
  56. func r(start, end byte) []byte {
  57. var a []byte
  58. for i := int(start); i <= int(end); i++ {
  59. a = append(a, byte(i))
  60. }
  61. return a
  62. }
  63. // GenerateTransitionTable generates a DEC ANSI transition table compatible
  64. // with the VT500-series of terminals. This implementation includes a few
  65. // modifications that include:
  66. // - A new Utf8State is introduced to handle UTF8 sequences.
  67. // - Osc and Dcs data accept UTF8 sequences by extending the printable range
  68. // to 0xFF and 0xFE respectively.
  69. // - We don't ignore 0x3A (':') when building Csi and Dcs parameters and
  70. // instead use it to denote sub-parameters.
  71. // - Support dispatching SosPmApc sequences.
  72. // - The DEL (0x7F) character is executed in the Ground state.
  73. // - The DEL (0x7F) character is collected in the DcsPassthrough string state.
  74. // - The ST C1 control character (0x9C) is executed and not ignored.
  75. func GenerateTransitionTable() TransitionTable {
  76. table := NewTransitionTable(DefaultTableSize)
  77. table.SetDefault(NoneAction, GroundState)
  78. // Anywhere
  79. for _, state := range r(GroundState, Utf8State) {
  80. // Anywhere -> Ground
  81. table.AddMany([]byte{0x18, 0x1a, 0x99, 0x9a}, state, ExecuteAction, GroundState)
  82. table.AddRange(0x80, 0x8F, state, ExecuteAction, GroundState)
  83. table.AddRange(0x90, 0x97, state, ExecuteAction, GroundState)
  84. table.AddOne(0x9C, state, ExecuteAction, GroundState)
  85. // Anywhere -> Escape
  86. table.AddOne(0x1B, state, ClearAction, EscapeState)
  87. // Anywhere -> SosStringState
  88. table.AddOne(0x98, state, StartAction, SosStringState)
  89. // Anywhere -> PmStringState
  90. table.AddOne(0x9E, state, StartAction, PmStringState)
  91. // Anywhere -> ApcStringState
  92. table.AddOne(0x9F, state, StartAction, ApcStringState)
  93. // Anywhere -> CsiEntry
  94. table.AddOne(0x9B, state, ClearAction, CsiEntryState)
  95. // Anywhere -> DcsEntry
  96. table.AddOne(0x90, state, ClearAction, DcsEntryState)
  97. // Anywhere -> OscString
  98. table.AddOne(0x9D, state, StartAction, OscStringState)
  99. // Anywhere -> Utf8
  100. table.AddRange(0xC2, 0xDF, state, CollectAction, Utf8State) // UTF8 2 byte sequence
  101. table.AddRange(0xE0, 0xEF, state, CollectAction, Utf8State) // UTF8 3 byte sequence
  102. table.AddRange(0xF0, 0xF4, state, CollectAction, Utf8State) // UTF8 4 byte sequence
  103. }
  104. // Ground
  105. table.AddRange(0x00, 0x17, GroundState, ExecuteAction, GroundState)
  106. table.AddOne(0x19, GroundState, ExecuteAction, GroundState)
  107. table.AddRange(0x1C, 0x1F, GroundState, ExecuteAction, GroundState)
  108. table.AddRange(0x20, 0x7E, GroundState, PrintAction, GroundState)
  109. table.AddOne(0x7F, GroundState, ExecuteAction, GroundState)
  110. // EscapeIntermediate
  111. table.AddRange(0x00, 0x17, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
  112. table.AddOne(0x19, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
  113. table.AddRange(0x1C, 0x1F, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
  114. table.AddRange(0x20, 0x2F, EscapeIntermediateState, CollectAction, EscapeIntermediateState)
  115. table.AddOne(0x7F, EscapeIntermediateState, IgnoreAction, EscapeIntermediateState)
  116. // EscapeIntermediate -> Ground
  117. table.AddRange(0x30, 0x7E, EscapeIntermediateState, DispatchAction, GroundState)
  118. // Escape
  119. table.AddRange(0x00, 0x17, EscapeState, ExecuteAction, EscapeState)
  120. table.AddOne(0x19, EscapeState, ExecuteAction, EscapeState)
  121. table.AddRange(0x1C, 0x1F, EscapeState, ExecuteAction, EscapeState)
  122. table.AddOne(0x7F, EscapeState, IgnoreAction, EscapeState)
  123. // Escape -> Ground
  124. table.AddRange(0x30, 0x4F, EscapeState, DispatchAction, GroundState)
  125. table.AddRange(0x51, 0x57, EscapeState, DispatchAction, GroundState)
  126. table.AddOne(0x59, EscapeState, DispatchAction, GroundState)
  127. table.AddOne(0x5A, EscapeState, DispatchAction, GroundState)
  128. table.AddOne(0x5C, EscapeState, DispatchAction, GroundState)
  129. table.AddRange(0x60, 0x7E, EscapeState, DispatchAction, GroundState)
  130. // Escape -> Escape_intermediate
  131. table.AddRange(0x20, 0x2F, EscapeState, CollectAction, EscapeIntermediateState)
  132. // Escape -> Sos_pm_apc_string
  133. table.AddOne('X', EscapeState, StartAction, SosStringState) // SOS
  134. table.AddOne('^', EscapeState, StartAction, PmStringState) // PM
  135. table.AddOne('_', EscapeState, StartAction, ApcStringState) // APC
  136. // Escape -> Dcs_entry
  137. table.AddOne('P', EscapeState, ClearAction, DcsEntryState)
  138. // Escape -> Csi_entry
  139. table.AddOne('[', EscapeState, ClearAction, CsiEntryState)
  140. // Escape -> Osc_string
  141. table.AddOne(']', EscapeState, StartAction, OscStringState)
  142. // Sos_pm_apc_string
  143. for _, state := range r(SosStringState, ApcStringState) {
  144. table.AddRange(0x00, 0x17, state, PutAction, state)
  145. table.AddOne(0x19, state, PutAction, state)
  146. table.AddRange(0x1C, 0x1F, state, PutAction, state)
  147. table.AddRange(0x20, 0x7F, state, PutAction, state)
  148. // ESC, ST, CAN, and SUB terminate the sequence
  149. table.AddOne(0x1B, state, DispatchAction, EscapeState)
  150. table.AddOne(0x9C, state, DispatchAction, GroundState)
  151. table.AddMany([]byte{0x18, 0x1A}, state, IgnoreAction, GroundState)
  152. }
  153. // Dcs_entry
  154. table.AddRange(0x00, 0x07, DcsEntryState, IgnoreAction, DcsEntryState)
  155. table.AddRange(0x0E, 0x17, DcsEntryState, IgnoreAction, DcsEntryState)
  156. table.AddOne(0x19, DcsEntryState, IgnoreAction, DcsEntryState)
  157. table.AddRange(0x1C, 0x1F, DcsEntryState, IgnoreAction, DcsEntryState)
  158. table.AddOne(0x7F, DcsEntryState, IgnoreAction, DcsEntryState)
  159. // Dcs_entry -> Dcs_intermediate
  160. table.AddRange(0x20, 0x2F, DcsEntryState, CollectAction, DcsIntermediateState)
  161. // Dcs_entry -> Dcs_param
  162. table.AddRange(0x30, 0x3B, DcsEntryState, ParamAction, DcsParamState)
  163. table.AddRange(0x3C, 0x3F, DcsEntryState, PrefixAction, DcsParamState)
  164. // Dcs_entry -> Dcs_passthrough
  165. table.AddRange(0x08, 0x0D, DcsEntryState, PutAction, DcsStringState) // Follows ECMA-48 § 8.3.27
  166. // XXX: allows passing ESC (not a ECMA-48 standard) this to allow for
  167. // passthrough of ANSI sequences like in Screen or Tmux passthrough mode.
  168. table.AddOne(0x1B, DcsEntryState, PutAction, DcsStringState)
  169. table.AddRange(0x40, 0x7E, DcsEntryState, StartAction, DcsStringState)
  170. // Dcs_intermediate
  171. table.AddRange(0x00, 0x17, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  172. table.AddOne(0x19, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  173. table.AddRange(0x1C, 0x1F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  174. table.AddRange(0x20, 0x2F, DcsIntermediateState, CollectAction, DcsIntermediateState)
  175. table.AddOne(0x7F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  176. // Dcs_intermediate -> Dcs_passthrough
  177. table.AddRange(0x30, 0x3F, DcsIntermediateState, StartAction, DcsStringState)
  178. table.AddRange(0x40, 0x7E, DcsIntermediateState, StartAction, DcsStringState)
  179. // Dcs_param
  180. table.AddRange(0x00, 0x17, DcsParamState, IgnoreAction, DcsParamState)
  181. table.AddOne(0x19, DcsParamState, IgnoreAction, DcsParamState)
  182. table.AddRange(0x1C, 0x1F, DcsParamState, IgnoreAction, DcsParamState)
  183. table.AddRange(0x30, 0x3B, DcsParamState, ParamAction, DcsParamState)
  184. table.AddOne(0x7F, DcsParamState, IgnoreAction, DcsParamState)
  185. table.AddRange(0x3C, 0x3F, DcsParamState, IgnoreAction, DcsParamState)
  186. // Dcs_param -> Dcs_intermediate
  187. table.AddRange(0x20, 0x2F, DcsParamState, CollectAction, DcsIntermediateState)
  188. // Dcs_param -> Dcs_passthrough
  189. table.AddRange(0x40, 0x7E, DcsParamState, StartAction, DcsStringState)
  190. // Dcs_passthrough
  191. table.AddRange(0x00, 0x17, DcsStringState, PutAction, DcsStringState)
  192. table.AddOne(0x19, DcsStringState, PutAction, DcsStringState)
  193. table.AddRange(0x1C, 0x1F, DcsStringState, PutAction, DcsStringState)
  194. table.AddRange(0x20, 0x7E, DcsStringState, PutAction, DcsStringState)
  195. table.AddOne(0x7F, DcsStringState, PutAction, DcsStringState)
  196. table.AddRange(0x80, 0xFF, DcsStringState, PutAction, DcsStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
  197. // ST, CAN, SUB, and ESC terminate the sequence
  198. table.AddOne(0x1B, DcsStringState, DispatchAction, EscapeState)
  199. table.AddOne(0x9C, DcsStringState, DispatchAction, GroundState)
  200. table.AddMany([]byte{0x18, 0x1A}, DcsStringState, IgnoreAction, GroundState)
  201. // Csi_param
  202. table.AddRange(0x00, 0x17, CsiParamState, ExecuteAction, CsiParamState)
  203. table.AddOne(0x19, CsiParamState, ExecuteAction, CsiParamState)
  204. table.AddRange(0x1C, 0x1F, CsiParamState, ExecuteAction, CsiParamState)
  205. table.AddRange(0x30, 0x3B, CsiParamState, ParamAction, CsiParamState)
  206. table.AddOne(0x7F, CsiParamState, IgnoreAction, CsiParamState)
  207. table.AddRange(0x3C, 0x3F, CsiParamState, IgnoreAction, CsiParamState)
  208. // Csi_param -> Ground
  209. table.AddRange(0x40, 0x7E, CsiParamState, DispatchAction, GroundState)
  210. // Csi_param -> Csi_intermediate
  211. table.AddRange(0x20, 0x2F, CsiParamState, CollectAction, CsiIntermediateState)
  212. // Csi_intermediate
  213. table.AddRange(0x00, 0x17, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
  214. table.AddOne(0x19, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
  215. table.AddRange(0x1C, 0x1F, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
  216. table.AddRange(0x20, 0x2F, CsiIntermediateState, CollectAction, CsiIntermediateState)
  217. table.AddOne(0x7F, CsiIntermediateState, IgnoreAction, CsiIntermediateState)
  218. // Csi_intermediate -> Ground
  219. table.AddRange(0x40, 0x7E, CsiIntermediateState, DispatchAction, GroundState)
  220. // Csi_intermediate -> Csi_ignore
  221. table.AddRange(0x30, 0x3F, CsiIntermediateState, IgnoreAction, GroundState)
  222. // Csi_entry
  223. table.AddRange(0x00, 0x17, CsiEntryState, ExecuteAction, CsiEntryState)
  224. table.AddOne(0x19, CsiEntryState, ExecuteAction, CsiEntryState)
  225. table.AddRange(0x1C, 0x1F, CsiEntryState, ExecuteAction, CsiEntryState)
  226. table.AddOne(0x7F, CsiEntryState, IgnoreAction, CsiEntryState)
  227. // Csi_entry -> Ground
  228. table.AddRange(0x40, 0x7E, CsiEntryState, DispatchAction, GroundState)
  229. // Csi_entry -> Csi_intermediate
  230. table.AddRange(0x20, 0x2F, CsiEntryState, CollectAction, CsiIntermediateState)
  231. // Csi_entry -> Csi_param
  232. table.AddRange(0x30, 0x3B, CsiEntryState, ParamAction, CsiParamState)
  233. table.AddRange(0x3C, 0x3F, CsiEntryState, PrefixAction, CsiParamState)
  234. // Osc_string
  235. table.AddRange(0x00, 0x06, OscStringState, IgnoreAction, OscStringState)
  236. table.AddRange(0x08, 0x17, OscStringState, IgnoreAction, OscStringState)
  237. table.AddOne(0x19, OscStringState, IgnoreAction, OscStringState)
  238. table.AddRange(0x1C, 0x1F, OscStringState, IgnoreAction, OscStringState)
  239. table.AddRange(0x20, 0xFF, OscStringState, PutAction, OscStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
  240. // ST, CAN, SUB, ESC, and BEL terminate the sequence
  241. table.AddOne(0x1B, OscStringState, DispatchAction, EscapeState)
  242. table.AddOne(0x07, OscStringState, DispatchAction, GroundState)
  243. table.AddOne(0x9C, OscStringState, DispatchAction, GroundState)
  244. table.AddMany([]byte{0x18, 0x1A}, OscStringState, IgnoreAction, GroundState)
  245. return table
  246. }