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}