transition_table.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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. func GenerateTransitionTable() TransitionTable {
  73. table := NewTransitionTable(DefaultTableSize)
  74. table.SetDefault(NoneAction, GroundState)
  75. // Anywhere
  76. for _, state := range r(GroundState, Utf8State) {
  77. // Anywhere -> Ground
  78. table.AddMany([]byte{0x18, 0x1a, 0x99, 0x9a}, state, ExecuteAction, GroundState)
  79. table.AddRange(0x80, 0x8F, state, ExecuteAction, GroundState)
  80. table.AddRange(0x90, 0x97, state, ExecuteAction, GroundState)
  81. table.AddOne(0x9C, state, IgnoreAction, GroundState)
  82. // Anywhere -> Escape
  83. table.AddOne(0x1B, state, ClearAction, EscapeState)
  84. // Anywhere -> SosStringState
  85. table.AddOne(0x98, state, StartAction, SosStringState)
  86. // Anywhere -> PmStringState
  87. table.AddOne(0x9E, state, StartAction, PmStringState)
  88. // Anywhere -> ApcStringState
  89. table.AddOne(0x9F, state, StartAction, ApcStringState)
  90. // Anywhere -> CsiEntry
  91. table.AddOne(0x9B, state, ClearAction, CsiEntryState)
  92. // Anywhere -> DcsEntry
  93. table.AddOne(0x90, state, ClearAction, DcsEntryState)
  94. // Anywhere -> OscString
  95. table.AddOne(0x9D, state, StartAction, OscStringState)
  96. // Anywhere -> Utf8
  97. table.AddRange(0xC2, 0xDF, state, PrintAction, Utf8State) // UTF8 2 byte sequence
  98. table.AddRange(0xE0, 0xEF, state, PrintAction, Utf8State) // UTF8 3 byte sequence
  99. table.AddRange(0xF0, 0xF4, state, PrintAction, Utf8State) // UTF8 4 byte sequence
  100. }
  101. // Ground
  102. table.AddRange(0x00, 0x17, GroundState, ExecuteAction, GroundState)
  103. table.AddOne(0x19, GroundState, ExecuteAction, GroundState)
  104. table.AddRange(0x1C, 0x1F, GroundState, ExecuteAction, GroundState)
  105. table.AddRange(0x20, 0x7F, GroundState, PrintAction, GroundState)
  106. // EscapeIntermediate
  107. table.AddRange(0x00, 0x17, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
  108. table.AddOne(0x19, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
  109. table.AddRange(0x1C, 0x1F, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
  110. table.AddRange(0x20, 0x2F, EscapeIntermediateState, CollectAction, EscapeIntermediateState)
  111. table.AddOne(0x7F, EscapeIntermediateState, IgnoreAction, EscapeIntermediateState)
  112. // EscapeIntermediate -> Ground
  113. table.AddRange(0x30, 0x7E, EscapeIntermediateState, DispatchAction, GroundState)
  114. // Escape
  115. table.AddRange(0x00, 0x17, EscapeState, ExecuteAction, EscapeState)
  116. table.AddOne(0x19, EscapeState, ExecuteAction, EscapeState)
  117. table.AddRange(0x1C, 0x1F, EscapeState, ExecuteAction, EscapeState)
  118. table.AddOne(0x7F, EscapeState, IgnoreAction, EscapeState)
  119. // Escape -> Ground
  120. table.AddRange(0x30, 0x4F, EscapeState, DispatchAction, GroundState)
  121. table.AddRange(0x51, 0x57, EscapeState, DispatchAction, GroundState)
  122. table.AddOne(0x59, EscapeState, DispatchAction, GroundState)
  123. table.AddOne(0x5A, EscapeState, DispatchAction, GroundState)
  124. table.AddOne(0x5C, EscapeState, DispatchAction, GroundState)
  125. table.AddRange(0x60, 0x7E, EscapeState, DispatchAction, GroundState)
  126. // Escape -> Escape_intermediate
  127. table.AddRange(0x20, 0x2F, EscapeState, CollectAction, EscapeIntermediateState)
  128. // Escape -> Sos_pm_apc_string
  129. table.AddOne('X', EscapeState, StartAction, SosStringState) // SOS
  130. table.AddOne('^', EscapeState, StartAction, PmStringState) // PM
  131. table.AddOne('_', EscapeState, StartAction, ApcStringState) // APC
  132. // Escape -> Dcs_entry
  133. table.AddOne('P', EscapeState, ClearAction, DcsEntryState)
  134. // Escape -> Csi_entry
  135. table.AddOne('[', EscapeState, ClearAction, CsiEntryState)
  136. // Escape -> Osc_string
  137. table.AddOne(']', EscapeState, StartAction, OscStringState)
  138. // Sos_pm_apc_string
  139. for _, state := range r(SosStringState, ApcStringState) {
  140. table.AddRange(0x00, 0x17, state, PutAction, state)
  141. table.AddOne(0x19, state, PutAction, state)
  142. table.AddRange(0x1C, 0x1F, state, PutAction, state)
  143. table.AddRange(0x20, 0x7F, state, PutAction, state)
  144. // ESC, ST, CAN, and SUB terminate the sequence
  145. table.AddOne(0x1B, state, DispatchAction, EscapeState)
  146. table.AddOne(0x9C, state, DispatchAction, GroundState)
  147. table.AddMany([]byte{0x18, 0x1A}, state, IgnoreAction, GroundState)
  148. }
  149. // Dcs_entry
  150. table.AddRange(0x00, 0x07, DcsEntryState, IgnoreAction, DcsEntryState)
  151. table.AddRange(0x0E, 0x17, DcsEntryState, IgnoreAction, DcsEntryState)
  152. table.AddOne(0x19, DcsEntryState, IgnoreAction, DcsEntryState)
  153. table.AddRange(0x1C, 0x1F, DcsEntryState, IgnoreAction, DcsEntryState)
  154. table.AddOne(0x7F, DcsEntryState, IgnoreAction, DcsEntryState)
  155. // Dcs_entry -> Dcs_intermediate
  156. table.AddRange(0x20, 0x2F, DcsEntryState, CollectAction, DcsIntermediateState)
  157. // Dcs_entry -> Dcs_param
  158. table.AddRange(0x30, 0x3B, DcsEntryState, ParamAction, DcsParamState)
  159. table.AddRange(0x3C, 0x3F, DcsEntryState, MarkerAction, DcsParamState)
  160. // Dcs_entry -> Dcs_passthrough
  161. table.AddRange(0x08, 0x0D, DcsEntryState, PutAction, DcsStringState) // Follows ECMA-48 § 8.3.27
  162. // XXX: allows passing ESC (not a ECMA-48 standard) this to allow for
  163. // passthrough of ANSI sequences like in Screen or Tmux passthrough mode.
  164. table.AddOne(0x1B, DcsEntryState, PutAction, DcsStringState)
  165. table.AddRange(0x40, 0x7E, DcsEntryState, StartAction, DcsStringState)
  166. // Dcs_intermediate
  167. table.AddRange(0x00, 0x17, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  168. table.AddOne(0x19, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  169. table.AddRange(0x1C, 0x1F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  170. table.AddRange(0x20, 0x2F, DcsIntermediateState, CollectAction, DcsIntermediateState)
  171. table.AddOne(0x7F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
  172. // Dcs_intermediate -> Dcs_passthrough
  173. table.AddRange(0x30, 0x3F, DcsIntermediateState, StartAction, DcsStringState)
  174. table.AddRange(0x40, 0x7E, DcsIntermediateState, StartAction, DcsStringState)
  175. // Dcs_param
  176. table.AddRange(0x00, 0x17, DcsParamState, IgnoreAction, DcsParamState)
  177. table.AddOne(0x19, DcsParamState, IgnoreAction, DcsParamState)
  178. table.AddRange(0x1C, 0x1F, DcsParamState, IgnoreAction, DcsParamState)
  179. table.AddRange(0x30, 0x3B, DcsParamState, ParamAction, DcsParamState)
  180. table.AddOne(0x7F, DcsParamState, IgnoreAction, DcsParamState)
  181. table.AddRange(0x3C, 0x3F, DcsParamState, IgnoreAction, DcsParamState)
  182. // Dcs_param -> Dcs_intermediate
  183. table.AddRange(0x20, 0x2F, DcsParamState, CollectAction, DcsIntermediateState)
  184. // Dcs_param -> Dcs_passthrough
  185. table.AddRange(0x40, 0x7E, DcsParamState, StartAction, DcsStringState)
  186. // Dcs_passthrough
  187. table.AddRange(0x00, 0x17, DcsStringState, PutAction, DcsStringState)
  188. table.AddOne(0x19, DcsStringState, PutAction, DcsStringState)
  189. table.AddRange(0x1C, 0x1F, DcsStringState, PutAction, DcsStringState)
  190. table.AddRange(0x20, 0x7E, DcsStringState, PutAction, DcsStringState)
  191. table.AddOne(0x7F, DcsStringState, IgnoreAction, DcsStringState)
  192. table.AddRange(0x80, 0xFF, DcsStringState, PutAction, DcsStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
  193. // ST, CAN, SUB, and ESC terminate the sequence
  194. table.AddOne(0x1B, DcsStringState, DispatchAction, EscapeState)
  195. table.AddOne(0x9C, DcsStringState, DispatchAction, GroundState)
  196. table.AddMany([]byte{0x18, 0x1A}, DcsStringState, IgnoreAction, GroundState)
  197. // Csi_param
  198. table.AddRange(0x00, 0x17, CsiParamState, ExecuteAction, CsiParamState)
  199. table.AddOne(0x19, CsiParamState, ExecuteAction, CsiParamState)
  200. table.AddRange(0x1C, 0x1F, CsiParamState, ExecuteAction, CsiParamState)
  201. table.AddRange(0x30, 0x3B, CsiParamState, ParamAction, CsiParamState)
  202. table.AddOne(0x7F, CsiParamState, IgnoreAction, CsiParamState)
  203. table.AddRange(0x3C, 0x3F, CsiParamState, IgnoreAction, CsiParamState)
  204. // Csi_param -> Ground
  205. table.AddRange(0x40, 0x7E, CsiParamState, DispatchAction, GroundState)
  206. // Csi_param -> Csi_intermediate
  207. table.AddRange(0x20, 0x2F, CsiParamState, CollectAction, CsiIntermediateState)
  208. // Csi_intermediate
  209. table.AddRange(0x00, 0x17, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
  210. table.AddOne(0x19, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
  211. table.AddRange(0x1C, 0x1F, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
  212. table.AddRange(0x20, 0x2F, CsiIntermediateState, CollectAction, CsiIntermediateState)
  213. table.AddOne(0x7F, CsiIntermediateState, IgnoreAction, CsiIntermediateState)
  214. // Csi_intermediate -> Ground
  215. table.AddRange(0x40, 0x7E, CsiIntermediateState, DispatchAction, GroundState)
  216. // Csi_intermediate -> Csi_ignore
  217. table.AddRange(0x30, 0x3F, CsiIntermediateState, IgnoreAction, GroundState)
  218. // Csi_entry
  219. table.AddRange(0x00, 0x17, CsiEntryState, ExecuteAction, CsiEntryState)
  220. table.AddOne(0x19, CsiEntryState, ExecuteAction, CsiEntryState)
  221. table.AddRange(0x1C, 0x1F, CsiEntryState, ExecuteAction, CsiEntryState)
  222. table.AddOne(0x7F, CsiEntryState, IgnoreAction, CsiEntryState)
  223. // Csi_entry -> Ground
  224. table.AddRange(0x40, 0x7E, CsiEntryState, DispatchAction, GroundState)
  225. // Csi_entry -> Csi_intermediate
  226. table.AddRange(0x20, 0x2F, CsiEntryState, CollectAction, CsiIntermediateState)
  227. // Csi_entry -> Csi_param
  228. table.AddRange(0x30, 0x3B, CsiEntryState, ParamAction, CsiParamState)
  229. table.AddRange(0x3C, 0x3F, CsiEntryState, MarkerAction, CsiParamState)
  230. // Osc_string
  231. table.AddRange(0x00, 0x06, OscStringState, IgnoreAction, OscStringState)
  232. table.AddRange(0x08, 0x17, OscStringState, IgnoreAction, OscStringState)
  233. table.AddOne(0x19, OscStringState, IgnoreAction, OscStringState)
  234. table.AddRange(0x1C, 0x1F, OscStringState, IgnoreAction, OscStringState)
  235. table.AddRange(0x20, 0xFF, OscStringState, PutAction, OscStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
  236. // ST, CAN, SUB, ESC, and BEL terminate the sequence
  237. table.AddOne(0x1B, OscStringState, DispatchAction, EscapeState)
  238. table.AddOne(0x07, OscStringState, DispatchAction, GroundState)
  239. table.AddOne(0x9C, OscStringState, DispatchAction, GroundState)
  240. table.AddMany([]byte{0x18, 0x1A}, OscStringState, IgnoreAction, GroundState)
  241. return table
  242. }