File: transition_table.go

package info (click to toggle)
golang-github-charmbracelet-x 0.0~git20240809.9ab0ca0%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,004 kB
  • sloc: sh: 55; makefile: 5
file content (273 lines) | stat: -rw-r--r-- 12,783 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package parser

// Table values are generated like this:
//
//	index:  currentState << IndexStateShift | charCode
//	value:  action << TransitionActionShift | nextState
const (
	TransitionActionShift = 4
	TransitionStateMask   = 15
	IndexStateShift       = 8

	// DefaultTableSize is the default size of the transition table.
	DefaultTableSize = 4096
)

// Table is a DEC ANSI transition table.
var Table = GenerateTransitionTable()

// TransitionTable is a DEC ANSI transition table.
// https://vt100.net/emu/dec_ansi_parser
type TransitionTable []byte

// NewTransitionTable returns a new DEC ANSI transition table.
func NewTransitionTable(size int) TransitionTable {
	if size <= 0 {
		size = DefaultTableSize
	}
	return TransitionTable(make([]byte, size))
}

// SetDefault sets default transition.
func (t TransitionTable) SetDefault(action Action, state State) {
	for i := 0; i < len(t); i++ {
		t[i] = action<<TransitionActionShift | state
	}
}

// AddOne adds a transition.
func (t TransitionTable) AddOne(code byte, state State, action Action, next State) {
	idx := int(state)<<IndexStateShift | int(code)
	value := action<<TransitionActionShift | next
	t[idx] = value
}

// AddMany adds many transitions.
func (t TransitionTable) AddMany(codes []byte, state State, action Action, next State) {
	for _, code := range codes {
		t.AddOne(code, state, action, next)
	}
}

// AddRange adds a range of transitions.
func (t TransitionTable) AddRange(start, end byte, state State, action Action, next State) {
	for i := int(start); i <= int(end); i++ {
		t.AddOne(byte(i), state, action, next)
	}
}

// Transition returns the next state and action for the given state and byte.
func (t TransitionTable) Transition(state State, code byte) (State, Action) {
	index := int(state)<<IndexStateShift | int(code)
	value := t[index]
	return value & TransitionStateMask, value >> TransitionActionShift
}

// byte range macro
func r(start, end byte) []byte {
	var a []byte
	for i := int(start); i <= int(end); i++ {
		a = append(a, byte(i))
	}
	return a
}

// GenerateTransitionTable generates a DEC ANSI transition table compatible
// with the VT500-series of terminals. This implementation includes a few
// modifications that include:
//   - A new Utf8State is introduced to handle UTF8 sequences.
//   - Osc and Dcs data accept UTF8 sequences by extending the printable range
//     to 0xFF and 0xFE respectively.
//   - We don't ignore 0x3A (':') when building Csi and Dcs parameters and
//     instead use it to denote sub-parameters.
//   - Support dispatching SosPmApc sequences.
//   - The DEL (0x7F) character is executed in the Ground state.
//   - The DEL (0x7F) character is collected in the DcsPassthrough string state.
//   - The ST C1 control character (0x9C) is executed and not ignored.
func GenerateTransitionTable() TransitionTable {
	table := NewTransitionTable(DefaultTableSize)
	table.SetDefault(NoneAction, GroundState)

	// Anywhere
	for _, state := range r(GroundState, Utf8State) {
		// Anywhere -> Ground
		table.AddMany([]byte{0x18, 0x1a, 0x99, 0x9a}, state, ExecuteAction, GroundState)
		table.AddRange(0x80, 0x8F, state, ExecuteAction, GroundState)
		table.AddRange(0x90, 0x97, state, ExecuteAction, GroundState)
		table.AddOne(0x9C, state, ExecuteAction, GroundState)
		// Anywhere -> Escape
		table.AddOne(0x1B, state, ClearAction, EscapeState)
		// Anywhere -> SosStringState
		table.AddOne(0x98, state, StartAction, SosStringState)
		// Anywhere -> PmStringState
		table.AddOne(0x9E, state, StartAction, PmStringState)
		// Anywhere -> ApcStringState
		table.AddOne(0x9F, state, StartAction, ApcStringState)
		// Anywhere -> CsiEntry
		table.AddOne(0x9B, state, ClearAction, CsiEntryState)
		// Anywhere -> DcsEntry
		table.AddOne(0x90, state, ClearAction, DcsEntryState)
		// Anywhere -> OscString
		table.AddOne(0x9D, state, StartAction, OscStringState)
		// Anywhere -> Utf8
		table.AddRange(0xC2, 0xDF, state, CollectAction, Utf8State) // UTF8 2 byte sequence
		table.AddRange(0xE0, 0xEF, state, CollectAction, Utf8State) // UTF8 3 byte sequence
		table.AddRange(0xF0, 0xF4, state, CollectAction, Utf8State) // UTF8 4 byte sequence
	}

	// Ground
	table.AddRange(0x00, 0x17, GroundState, ExecuteAction, GroundState)
	table.AddOne(0x19, GroundState, ExecuteAction, GroundState)
	table.AddRange(0x1C, 0x1F, GroundState, ExecuteAction, GroundState)
	table.AddRange(0x20, 0x7E, GroundState, PrintAction, GroundState)
	table.AddOne(0x7F, GroundState, ExecuteAction, GroundState)

	// EscapeIntermediate
	table.AddRange(0x00, 0x17, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
	table.AddOne(0x19, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
	table.AddRange(0x1C, 0x1F, EscapeIntermediateState, ExecuteAction, EscapeIntermediateState)
	table.AddRange(0x20, 0x2F, EscapeIntermediateState, CollectAction, EscapeIntermediateState)
	table.AddOne(0x7F, EscapeIntermediateState, IgnoreAction, EscapeIntermediateState)
	// EscapeIntermediate -> Ground
	table.AddRange(0x30, 0x7E, EscapeIntermediateState, DispatchAction, GroundState)

	// Escape
	table.AddRange(0x00, 0x17, EscapeState, ExecuteAction, EscapeState)
	table.AddOne(0x19, EscapeState, ExecuteAction, EscapeState)
	table.AddRange(0x1C, 0x1F, EscapeState, ExecuteAction, EscapeState)
	table.AddOne(0x7F, EscapeState, IgnoreAction, EscapeState)
	// Escape -> Ground
	table.AddRange(0x30, 0x4F, EscapeState, DispatchAction, GroundState)
	table.AddRange(0x51, 0x57, EscapeState, DispatchAction, GroundState)
	table.AddOne(0x59, EscapeState, DispatchAction, GroundState)
	table.AddOne(0x5A, EscapeState, DispatchAction, GroundState)
	table.AddOne(0x5C, EscapeState, DispatchAction, GroundState)
	table.AddRange(0x60, 0x7E, EscapeState, DispatchAction, GroundState)
	// Escape -> Escape_intermediate
	table.AddRange(0x20, 0x2F, EscapeState, CollectAction, EscapeIntermediateState)
	// Escape -> Sos_pm_apc_string
	table.AddOne('X', EscapeState, StartAction, SosStringState) // SOS
	table.AddOne('^', EscapeState, StartAction, PmStringState)  // PM
	table.AddOne('_', EscapeState, StartAction, ApcStringState) // APC
	// Escape -> Dcs_entry
	table.AddOne('P', EscapeState, ClearAction, DcsEntryState)
	// Escape -> Csi_entry
	table.AddOne('[', EscapeState, ClearAction, CsiEntryState)
	// Escape -> Osc_string
	table.AddOne(']', EscapeState, StartAction, OscStringState)

	// Sos_pm_apc_string
	for _, state := range r(SosStringState, ApcStringState) {
		table.AddRange(0x00, 0x17, state, PutAction, state)
		table.AddOne(0x19, state, PutAction, state)
		table.AddRange(0x1C, 0x1F, state, PutAction, state)
		table.AddRange(0x20, 0x7F, state, PutAction, state)
		// ESC, ST, CAN, and SUB terminate the sequence
		table.AddOne(0x1B, state, DispatchAction, EscapeState)
		table.AddOne(0x9C, state, DispatchAction, GroundState)
		table.AddMany([]byte{0x18, 0x1A}, state, IgnoreAction, GroundState)
	}

	// Dcs_entry
	table.AddRange(0x00, 0x07, DcsEntryState, IgnoreAction, DcsEntryState)
	table.AddRange(0x0E, 0x17, DcsEntryState, IgnoreAction, DcsEntryState)
	table.AddOne(0x19, DcsEntryState, IgnoreAction, DcsEntryState)
	table.AddRange(0x1C, 0x1F, DcsEntryState, IgnoreAction, DcsEntryState)
	table.AddOne(0x7F, DcsEntryState, IgnoreAction, DcsEntryState)
	// Dcs_entry -> Dcs_intermediate
	table.AddRange(0x20, 0x2F, DcsEntryState, CollectAction, DcsIntermediateState)
	// Dcs_entry -> Dcs_param
	table.AddRange(0x30, 0x3B, DcsEntryState, ParamAction, DcsParamState)
	table.AddRange(0x3C, 0x3F, DcsEntryState, MarkerAction, DcsParamState)
	// Dcs_entry -> Dcs_passthrough
	table.AddRange(0x08, 0x0D, DcsEntryState, PutAction, DcsStringState) // Follows ECMA-48 ยง 8.3.27
	// XXX: allows passing ESC (not a ECMA-48 standard) this to allow for
	// passthrough of ANSI sequences like in Screen or Tmux passthrough mode.
	table.AddOne(0x1B, DcsEntryState, PutAction, DcsStringState)
	table.AddRange(0x40, 0x7E, DcsEntryState, StartAction, DcsStringState)

	// Dcs_intermediate
	table.AddRange(0x00, 0x17, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
	table.AddOne(0x19, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
	table.AddRange(0x1C, 0x1F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
	table.AddRange(0x20, 0x2F, DcsIntermediateState, CollectAction, DcsIntermediateState)
	table.AddOne(0x7F, DcsIntermediateState, IgnoreAction, DcsIntermediateState)
	// Dcs_intermediate -> Dcs_passthrough
	table.AddRange(0x30, 0x3F, DcsIntermediateState, StartAction, DcsStringState)
	table.AddRange(0x40, 0x7E, DcsIntermediateState, StartAction, DcsStringState)

	// Dcs_param
	table.AddRange(0x00, 0x17, DcsParamState, IgnoreAction, DcsParamState)
	table.AddOne(0x19, DcsParamState, IgnoreAction, DcsParamState)
	table.AddRange(0x1C, 0x1F, DcsParamState, IgnoreAction, DcsParamState)
	table.AddRange(0x30, 0x3B, DcsParamState, ParamAction, DcsParamState)
	table.AddOne(0x7F, DcsParamState, IgnoreAction, DcsParamState)
	table.AddRange(0x3C, 0x3F, DcsParamState, IgnoreAction, DcsParamState)
	// Dcs_param -> Dcs_intermediate
	table.AddRange(0x20, 0x2F, DcsParamState, CollectAction, DcsIntermediateState)
	// Dcs_param -> Dcs_passthrough
	table.AddRange(0x40, 0x7E, DcsParamState, StartAction, DcsStringState)

	// Dcs_passthrough
	table.AddRange(0x00, 0x17, DcsStringState, PutAction, DcsStringState)
	table.AddOne(0x19, DcsStringState, PutAction, DcsStringState)
	table.AddRange(0x1C, 0x1F, DcsStringState, PutAction, DcsStringState)
	table.AddRange(0x20, 0x7E, DcsStringState, PutAction, DcsStringState)
	table.AddOne(0x7F, DcsStringState, PutAction, DcsStringState)
	table.AddRange(0x80, 0xFF, DcsStringState, PutAction, DcsStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF
	// ST, CAN, SUB, and ESC terminate the sequence
	table.AddOne(0x1B, DcsStringState, DispatchAction, EscapeState)
	table.AddOne(0x9C, DcsStringState, DispatchAction, GroundState)
	table.AddMany([]byte{0x18, 0x1A}, DcsStringState, IgnoreAction, GroundState)

	// Csi_param
	table.AddRange(0x00, 0x17, CsiParamState, ExecuteAction, CsiParamState)
	table.AddOne(0x19, CsiParamState, ExecuteAction, CsiParamState)
	table.AddRange(0x1C, 0x1F, CsiParamState, ExecuteAction, CsiParamState)
	table.AddRange(0x30, 0x3B, CsiParamState, ParamAction, CsiParamState)
	table.AddOne(0x7F, CsiParamState, IgnoreAction, CsiParamState)
	table.AddRange(0x3C, 0x3F, CsiParamState, IgnoreAction, CsiParamState)
	// Csi_param -> Ground
	table.AddRange(0x40, 0x7E, CsiParamState, DispatchAction, GroundState)
	// Csi_param -> Csi_intermediate
	table.AddRange(0x20, 0x2F, CsiParamState, CollectAction, CsiIntermediateState)

	// Csi_intermediate
	table.AddRange(0x00, 0x17, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
	table.AddOne(0x19, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
	table.AddRange(0x1C, 0x1F, CsiIntermediateState, ExecuteAction, CsiIntermediateState)
	table.AddRange(0x20, 0x2F, CsiIntermediateState, CollectAction, CsiIntermediateState)
	table.AddOne(0x7F, CsiIntermediateState, IgnoreAction, CsiIntermediateState)
	// Csi_intermediate -> Ground
	table.AddRange(0x40, 0x7E, CsiIntermediateState, DispatchAction, GroundState)
	// Csi_intermediate -> Csi_ignore
	table.AddRange(0x30, 0x3F, CsiIntermediateState, IgnoreAction, GroundState)

	// Csi_entry
	table.AddRange(0x00, 0x17, CsiEntryState, ExecuteAction, CsiEntryState)
	table.AddOne(0x19, CsiEntryState, ExecuteAction, CsiEntryState)
	table.AddRange(0x1C, 0x1F, CsiEntryState, ExecuteAction, CsiEntryState)
	table.AddOne(0x7F, CsiEntryState, IgnoreAction, CsiEntryState)
	// Csi_entry -> Ground
	table.AddRange(0x40, 0x7E, CsiEntryState, DispatchAction, GroundState)
	// Csi_entry -> Csi_intermediate
	table.AddRange(0x20, 0x2F, CsiEntryState, CollectAction, CsiIntermediateState)
	// Csi_entry -> Csi_param
	table.AddRange(0x30, 0x3B, CsiEntryState, ParamAction, CsiParamState)
	table.AddRange(0x3C, 0x3F, CsiEntryState, MarkerAction, CsiParamState)

	// Osc_string
	table.AddRange(0x00, 0x06, OscStringState, IgnoreAction, OscStringState)
	table.AddRange(0x08, 0x17, OscStringState, IgnoreAction, OscStringState)
	table.AddOne(0x19, OscStringState, IgnoreAction, OscStringState)
	table.AddRange(0x1C, 0x1F, OscStringState, IgnoreAction, OscStringState)
	table.AddRange(0x20, 0xFF, OscStringState, PutAction, OscStringState) // Allow Utf8 characters by extending the printable range from 0x7F to 0xFF

	// ST, CAN, SUB, ESC, and BEL terminate the sequence
	table.AddOne(0x1B, OscStringState, DispatchAction, EscapeState)
	table.AddOne(0x07, OscStringState, DispatchAction, GroundState)
	table.AddOne(0x9C, OscStringState, DispatchAction, GroundState)
	table.AddMany([]byte{0x18, 0x1A}, OscStringState, IgnoreAction, GroundState)

	return table
}