transition_table.go

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