From 9bd7c490035b5c4bd16629452db391d4a55c43a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 2 Jun 2019 23:10:59 +0200 Subject: [PATCH 1/4] remove transition table Array fallback, cleanup tests --- src/core/parser/EscapeSequenceParser.test.ts | 1666 +++++++++--------- src/core/parser/EscapeSequenceParser.ts | 4 +- 2 files changed, 810 insertions(+), 860 deletions(-) diff --git a/src/core/parser/EscapeSequenceParser.test.ts b/src/core/parser/EscapeSequenceParser.test.ts index 553050c8..d550b1d5 100644 --- a/src/core/parser/EscapeSequenceParser.test.ts +++ b/src/core/parser/EscapeSequenceParser.test.ts @@ -117,52 +117,23 @@ const states: number[] = [ let state: any; // parser with Uint8Array based transition table -const parserUint = new TestEscapeSequenceParser(VT500_TRANSITION_TABLE); -parserUint.setPrintHandler(testTerminal.print.bind(testTerminal)); -parserUint.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { +const testParser = new TestEscapeSequenceParser(); +testParser.setPrintHandler(testTerminal.print.bind(testTerminal)); +testParser.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { testTerminal.actionCSI(collect, params, String.fromCharCode(flag)); }); -parserUint.setEscHandlerFallback((collect: string, flag: number) => { +testParser.setEscHandlerFallback((collect: string, flag: number) => { testTerminal.actionESC(collect, String.fromCharCode(flag)); }); -parserUint.setExecuteHandlerFallback((code: number) => { +testParser.setExecuteHandlerFallback((code: number) => { testTerminal.actionExecute(String.fromCharCode(code)); }); -parserUint.setOscHandlerFallback((identifier: number, data: string) => { +testParser.setOscHandlerFallback((identifier: number, data: string) => { if (identifier === -1) testTerminal.actionOSC(data); // handle error condition silently else testTerminal.actionOSC('' + identifier + ';' + data); }); -parserUint.setDcsHandlerFallback(new DcsTest()); +testParser.setDcsHandlerFallback(new DcsTest()); -// array based transition table -const VT500_TRANSITION_TABLE_ARRAY = new TransitionTable(VT500_TRANSITION_TABLE.table.length); -VT500_TRANSITION_TABLE_ARRAY.table = new Array(VT500_TRANSITION_TABLE.table.length); -for (let i = 0; i < VT500_TRANSITION_TABLE.table.length; ++i) { - VT500_TRANSITION_TABLE_ARRAY.table[i] = VT500_TRANSITION_TABLE.table[i]; -} - -// parser with array based transition table -const parserArray = new TestEscapeSequenceParser(VT500_TRANSITION_TABLE_ARRAY); -parserArray.setPrintHandler(testTerminal.print.bind(testTerminal)); -parserArray.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { - testTerminal.actionCSI(collect, params, String.fromCharCode(flag)); -}); -parserArray.setEscHandlerFallback((collect: string, flag: number) => { - testTerminal.actionESC(collect, String.fromCharCode(flag)); -}); -parserArray.setExecuteHandlerFallback((code: number) => { - testTerminal.actionExecute(String.fromCharCode(code)); -}); -parserArray.setOscHandlerFallback((identifier: number, data: string) => { - if (identifier === -1) testTerminal.actionOSC(data); // handle error condition silently - else testTerminal.actionOSC('' + identifier + ';' + data); -}); -parserArray.setDcsHandlerFallback(new DcsTest()); - -interface IRun { - tableType: string; - parser: TestEscapeSequenceParser; -} // translate string based parse calls into typed array based function parse(parser: TestEscapeSequenceParser, data: string): void { @@ -172,908 +143,889 @@ function parse(parser: TestEscapeSequenceParser, data: string): void { } describe('EscapeSequenceParser', function (): void { - let parser: TestEscapeSequenceParser; - const runs: IRun[] = [ - { tableType: 'Uint8Array', parser: parserUint }, - { tableType: 'Array', parser: parserArray } - ]; - runs.forEach(function (run: IRun): void { - describe('Parser init and methods / ' + run.tableType, function (): void { - before(function(): void { - parser = run.parser; - }); - it('constructor', function (): void { - let p: EscapeSequenceParser = new EscapeSequenceParser(); - chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE); - p = new EscapeSequenceParser(VT500_TRANSITION_TABLE); - chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE); - const tansitions: TransitionTable = new TransitionTable(10); - p = new EscapeSequenceParser(tansitions); - chai.expect(p.TRANSITIONS).equal(tansitions); - }); - it('inital states', function (): void { - chai.expect(parser.initialState).equal(ParserState.GROUND); - chai.expect(parser.currentState).equal(ParserState.GROUND); - chai.expect(parser.osc).equal(''); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); - }); - it('reset states', function (): void { - parser.currentState = 124; - parser.osc = '#'; - parser.params = [123]; - parser.collect = '#'; + const parser = testParser; + describe('Parser init and methods', function (): void { + it('constructor', function (): void { + let p: EscapeSequenceParser = new EscapeSequenceParser(); + chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE); + p = new EscapeSequenceParser(VT500_TRANSITION_TABLE); + chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE); + const tansitions: TransitionTable = new TransitionTable(10); + p = new EscapeSequenceParser(tansitions); + chai.expect(p.TRANSITIONS).equal(tansitions); + }); + it('inital states', function (): void { + chai.expect(parser.initialState).equal(ParserState.GROUND); + chai.expect(parser.currentState).equal(ParserState.GROUND); + chai.expect(parser.osc).equal(''); + chai.expect(parser.params).eql([0]); + chai.expect(parser.collect).equal(''); + }); + it('reset states', function (): void { + parser.currentState = 124; + parser.osc = '#'; + parser.params = [123]; + parser.collect = '#'; - parser.reset(); - chai.expect(parser.currentState).equal(ParserState.GROUND); - chai.expect(parser.osc).equal(''); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); - }); + parser.reset(); + chai.expect(parser.currentState).equal(ParserState.GROUND); + chai.expect(parser.osc).equal(''); + chai.expect(parser.params).eql([0]); + chai.expect(parser.collect).equal(''); }); }); - runs.forEach(function (run: IRun): void { - describe('state transitions and actions / ' + run.tableType, function (): void { - before(function(): void { - parser = run.parser; - }); - it('state GROUND execute action', function (): void { + describe('state transitions and actions', function (): void { + it('state GROUND execute action', function (): void { + parser.reset(); + testTerminal.clear(); + let exes = r(0x00, 0x18); + exes = exes.concat(['\x19']); + exes = exes.concat(r(0x1c, 0x20)); + for (let i = 0; i < exes.length; ++i) { + parser.currentState = ParserState.GROUND; + parse(parser, exes[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); - let exes = r(0x00, 0x18); - exes = exes.concat(['\x19']); - exes = exes.concat(r(0x1c, 0x20)); + } + }); + it('state GROUND print action', function (): void { + parser.reset(); + testTerminal.clear(); + const printables = r(0x20, 0x7f); // NOTE: DEL excluded + for (let i = 0; i < printables.length; ++i) { + parser.currentState = ParserState.GROUND; + parse(parser, printables[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([['print', printables[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('trans ANYWHERE --> GROUND with actions', function (): void { + const exes = [ + '\x18', '\x1a', + '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87', '\x88', + '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f', + '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x99', '\x9a' + ]; + const exceptions: { [key: number]: { [key: string]: any[] } } = { + 8: { '\x18': [], '\x1a': [] } // simply abort osc state + }; + parser.reset(); + testTerminal.clear(); + for (state in states) { for (let i = 0; i < exes.length; ++i) { - parser.currentState = ParserState.GROUND; - parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([['exe', exes[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state GROUND print action', function (): void { - parser.reset(); - testTerminal.clear(); - const printables = r(0x20, 0x7f); // NOTE: DEL excluded - for (let i = 0; i < printables.length; ++i) { - parser.currentState = ParserState.GROUND; - parse(parser, printables[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([['print', printables[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans ANYWHERE --> GROUND with actions', function (): void { - const exes = [ - '\x18', '\x1a', - '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87', '\x88', - '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f', - '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x99', '\x9a' - ]; - const exceptions: { [key: number]: { [key: string]: any[] } } = { - 8: { '\x18': [], '\x1a': [] } // simply abort osc state - }; - parser.reset(); - testTerminal.clear(); - for (state in states) { - for (let i = 0; i < exes.length; ++i) { - parser.currentState = state; - parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare((state in exceptions ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]); - parser.reset(); - testTerminal.clear(); - } - parse(parser, '\x9c'); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans ANYWHERE --> ESCAPE with clear', function (): void { - parser.reset(); - for (state in states) { parser.currentState = state; - parser.osc = '#'; - parser.params = [23]; - parser.collect = '#'; - parse(parser, '\x1b'); - chai.expect(parser.currentState).equal(ParserState.ESCAPE); - chai.expect(parser.osc).equal(''); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); - parser.reset(); - } - }); - it('state ESCAPE execute rules', function (): void { - parser.reset(); - testTerminal.clear(); - let exes = r(0x00, 0x18); - exes = exes.concat(['\x19']); - exes = exes.concat(r(0x1c, 0x20)); - for (let i = 0; i < exes.length; ++i) { - parser.currentState = ParserState.ESCAPE; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE); - testTerminal.compare([['exe', exes[i]]]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare((state in exceptions ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } - }); - it('state ESCAPE ignore', function (): void { + parse(parser, '\x9c'); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([]); parser.reset(); testTerminal.clear(); - parser.currentState = ParserState.ESCAPE; - parse(parser, '\x7f'); + } + }); + it('trans ANYWHERE --> ESCAPE with clear', function (): void { + parser.reset(); + for (state in states) { + parser.currentState = state; + parser.osc = '#'; + parser.params = [23]; + parser.collect = '#'; + parse(parser, '\x1b'); chai.expect(parser.currentState).equal(ParserState.ESCAPE); - testTerminal.compare([]); + chai.expect(parser.osc).equal(''); + chai.expect(parser.params).eql([0]); + chai.expect(parser.collect).equal(''); parser.reset(); - testTerminal.clear(); - }); - it('trans ESCAPE --> GROUND with ecs_dispatch action', function (): void { - parser.reset(); - testTerminal.clear(); - let dispatches = r(0x30, 0x50); - dispatches = dispatches.concat(r(0x51, 0x58)); - dispatches = dispatches.concat(['\x59', '\x5a']); // excluded \x5c - dispatches = dispatches.concat(r(0x60, 0x7f)); - for (let i = 0; i < dispatches.length; ++i) { - parser.currentState = ParserState.ESCAPE; - parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([['esc', '', dispatches[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans ESCAPE --> ESCAPE_INTERMEDIATE with collect action', function (): void { - parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.ESCAPE; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('state ESCAPE_INTERMEDIATE execute rules', function (): void { - parser.reset(); - testTerminal.clear(); - let exes = r(0x00, 0x18); - exes = exes.concat(['\x19']); - exes = exes.concat(r(0x1c, 0x20)); - for (let i = 0; i < exes.length; ++i) { - parser.currentState = ParserState.ESCAPE_INTERMEDIATE; - parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); - testTerminal.compare([['exe', exes[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state ESCAPE_INTERMEDIATE ignore', function (): void { - parser.reset(); - testTerminal.clear(); - parser.currentState = ParserState.ESCAPE_INTERMEDIATE; - parse(parser, '\x7f'); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); - testTerminal.compare([]); - parser.reset(); - testTerminal.clear(); - }); - it('state ESCAPE_INTERMEDIATE collect action', function (): void { - parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.ESCAPE_INTERMEDIATE; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('trans ESCAPE_INTERMEDIATE --> GROUND with esc_dispatch action', function (): void { - parser.reset(); - testTerminal.clear(); - const collect = r(0x30, 0x7f); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.ESCAPE_INTERMEDIATE; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - // '\x5c' --> ESC + \ (7bit ST) parser does not expose this as it already got handled - testTerminal.compare((collect[i] === '\x5c') ? [] : [['esc', '', collect[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans ANYWHERE/ESCAPE --> CSI_ENTRY with clear', function (): void { - parser.reset(); - // C0 + } + }); + it('state ESCAPE execute rules', function (): void { + parser.reset(); + testTerminal.clear(); + let exes = r(0x00, 0x18); + exes = exes.concat(['\x19']); + exes = exes.concat(r(0x1c, 0x20)); + for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.ESCAPE; + parse(parser, exes[i]); + chai.expect(parser.currentState).equal(ParserState.ESCAPE); + testTerminal.compare([['exe', exes[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('state ESCAPE ignore', function (): void { + parser.reset(); + testTerminal.clear(); + parser.currentState = ParserState.ESCAPE; + parse(parser, '\x7f'); + chai.expect(parser.currentState).equal(ParserState.ESCAPE); + testTerminal.compare([]); + parser.reset(); + testTerminal.clear(); + }); + it('trans ESCAPE --> GROUND with ecs_dispatch action', function (): void { + parser.reset(); + testTerminal.clear(); + let dispatches = r(0x30, 0x50); + dispatches = dispatches.concat(r(0x51, 0x58)); + dispatches = dispatches.concat(['\x59', '\x5a']); // excluded \x5c + dispatches = dispatches.concat(r(0x60, 0x7f)); + for (let i = 0; i < dispatches.length; ++i) { + parser.currentState = ParserState.ESCAPE; + parse(parser, dispatches[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([['esc', '', dispatches[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('trans ESCAPE --> ESCAPE_INTERMEDIATE with collect action', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.ESCAPE; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); + parser.reset(); + } + }); + it('state ESCAPE_INTERMEDIATE execute rules', function (): void { + parser.reset(); + testTerminal.clear(); + let exes = r(0x00, 0x18); + exes = exes.concat(['\x19']); + exes = exes.concat(r(0x1c, 0x20)); + for (let i = 0; i < exes.length; ++i) { + parser.currentState = ParserState.ESCAPE_INTERMEDIATE; + parse(parser, exes[i]); + chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); + testTerminal.compare([['exe', exes[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('state ESCAPE_INTERMEDIATE ignore', function (): void { + parser.reset(); + testTerminal.clear(); + parser.currentState = ParserState.ESCAPE_INTERMEDIATE; + parse(parser, '\x7f'); + chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); + testTerminal.compare([]); + parser.reset(); + testTerminal.clear(); + }); + it('state ESCAPE_INTERMEDIATE collect action', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.ESCAPE_INTERMEDIATE; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); + parser.reset(); + } + }); + it('trans ESCAPE_INTERMEDIATE --> GROUND with esc_dispatch action', function (): void { + parser.reset(); + testTerminal.clear(); + const collect = r(0x30, 0x7f); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.ESCAPE_INTERMEDIATE; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + // '\x5c' --> ESC + \ (7bit ST) parser does not expose this as it already got handled + testTerminal.compare((collect[i] === '\x5c') ? [] : [['esc', '', collect[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('trans ANYWHERE/ESCAPE --> CSI_ENTRY with clear', function (): void { + parser.reset(); + // C0 + parser.currentState = ParserState.ESCAPE; + parser.osc = '#'; + parser.params = [123]; + parser.collect = '#'; + parse(parser, '['); + chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); + chai.expect(parser.osc).equal(''); + chai.expect(parser.params).eql([0]); + chai.expect(parser.collect).equal(''); + parser.reset(); + // C1 + for (state in states) { + parser.currentState = state; parser.osc = '#'; parser.params = [123]; parser.collect = '#'; - parse(parser, '['); + parse(parser, '\x9b'); chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); chai.expect(parser.osc).equal(''); chai.expect(parser.params).eql([0]); chai.expect(parser.collect).equal(''); parser.reset(); - // C1 - for (state in states) { - parser.currentState = state; - parser.osc = '#'; - parser.params = [123]; - parser.collect = '#'; - parse(parser, '\x9b'); - chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); - chai.expect(parser.osc).equal(''); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); - parser.reset(); - } - }); - it('state CSI_ENTRY execute rules', function (): void { - parser.reset(); - testTerminal.clear(); - let exes = r(0x00, 0x18); - exes = exes.concat(['\x19']); - exes = exes.concat(r(0x1c, 0x20)); - for (let i = 0; i < exes.length; ++i) { - parser.currentState = ParserState.CSI_ENTRY; - parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); - testTerminal.compare([['exe', exes[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state CSI_ENTRY ignore', function (): void { - parser.reset(); - testTerminal.clear(); + } + }); + it('state CSI_ENTRY execute rules', function (): void { + parser.reset(); + testTerminal.clear(); + let exes = r(0x00, 0x18); + exes = exes.concat(['\x19']); + exes = exes.concat(r(0x1c, 0x20)); + for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; - parse(parser, '\x7f'); + parse(parser, exes[i]); chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); - testTerminal.compare([]); + testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); - }); - it('trans CSI_ENTRY --> GROUND with csi_dispatch action', function (): void { - parser.reset(); - const dispatches = r(0x40, 0x7f); - for (let i = 0; i < dispatches.length; ++i) { - parser.currentState = ParserState.CSI_ENTRY; - parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([['csi', '', [0], dispatches[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans CSI_ENTRY --> CSI_PARAM with param/collect actions', function (): void { - parser.reset(); - const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; - const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; - for (let i = 0; i < params.length; ++i) { - parser.currentState = ParserState.CSI_ENTRY; - parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); - parser.reset(); - } + } + }); + it('state CSI_ENTRY ignore', function (): void { + parser.reset(); + testTerminal.clear(); + parser.currentState = ParserState.CSI_ENTRY; + parse(parser, '\x7f'); + chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); + testTerminal.compare([]); + parser.reset(); + testTerminal.clear(); + }); + it('trans CSI_ENTRY --> GROUND with csi_dispatch action', function (): void { + parser.reset(); + const dispatches = r(0x40, 0x7f); + for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; - parse(parser, '\x3b'); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([0, 0]); - parser.reset(); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.CSI_ENTRY; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('state CSI_PARAM execute rules', function (): void { + parse(parser, dispatches[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([['csi', '', [0], dispatches[i]]]); parser.reset(); testTerminal.clear(); - let exes = r(0x00, 0x18); - exes = exes.concat(['\x19']); - exes = exes.concat(r(0x1c, 0x20)); - for (let i = 0; i < exes.length; ++i) { - parser.currentState = ParserState.CSI_PARAM; - parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - testTerminal.compare([['exe', exes[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state CSI_PARAM param action', function (): void { + } + }); + it('trans CSI_ENTRY --> CSI_PARAM with param/collect actions', function (): void { + parser.reset(); + const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; + const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; + for (let i = 0; i < params.length; ++i) { + parser.currentState = ParserState.CSI_ENTRY; + parse(parser, params[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); parser.reset(); - const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; - for (let i = 0; i < params.length; ++i) { - parser.currentState = ParserState.CSI_PARAM; - parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); - parser.reset(); - } + } + parser.currentState = ParserState.CSI_ENTRY; + parse(parser, '\x3b'); + chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + chai.expect(parser.params).eql([0, 0]); + parser.reset(); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.CSI_ENTRY; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + chai.expect(parser.collect).equal(collect[i]); + parser.reset(); + } + }); + it('state CSI_PARAM execute rules', function (): void { + parser.reset(); + testTerminal.clear(); + let exes = r(0x00, 0x18); + exes = exes.concat(['\x19']); + exes = exes.concat(r(0x1c, 0x20)); + for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_PARAM; - parse(parser, '\x3b'); + parse(parser, exes[i]); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([0, 0]); - parser.reset(); - }); - it('state CSI_PARAM ignore', function (): void { + testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); + } + }); + it('state CSI_PARAM param action', function (): void { + parser.reset(); + const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; + for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.CSI_PARAM; - parse(parser, '\x7f'); + parse(parser, params[i]); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - testTerminal.compare([]); + chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); + parser.reset(); + } + parser.currentState = ParserState.CSI_PARAM; + parse(parser, '\x3b'); + chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + chai.expect(parser.params).eql([0, 0]); + parser.reset(); + }); + it('state CSI_PARAM ignore', function (): void { + parser.reset(); + testTerminal.clear(); + parser.currentState = ParserState.CSI_PARAM; + parse(parser, '\x7f'); + chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + testTerminal.compare([]); + parser.reset(); + testTerminal.clear(); + }); + it('trans CSI_PARAM --> GROUND with csi_dispatch action', function (): void { + parser.reset(); + const dispatches = r(0x40, 0x7f); + for (let i = 0; i < dispatches.length; ++i) { + parser.currentState = ParserState.CSI_PARAM; + parser.params = [0, 1]; + parse(parser, dispatches[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); parser.reset(); testTerminal.clear(); - }); - it('trans CSI_PARAM --> GROUND with csi_dispatch action', function (): void { - parser.reset(); - const dispatches = r(0x40, 0x7f); - for (let i = 0; i < dispatches.length; ++i) { - parser.currentState = ParserState.CSI_PARAM; - parser.params = [0, 1]; - parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans CSI_ENTRY --> CSI_INTERMEDIATE with collect action', function (): void { - parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.CSI_ENTRY; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('trans CSI_PARAM --> CSI_INTERMEDIATE with collect action', function (): void { - parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.CSI_PARAM; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('state CSI_INTERMEDIATE execute rules', function (): void { - parser.reset(); - testTerminal.clear(); - let exes = r(0x00, 0x18); - exes = exes.concat(['\x19']); - exes = exes.concat(r(0x1c, 0x20)); - for (let i = 0; i < exes.length; ++i) { - parser.currentState = ParserState.CSI_INTERMEDIATE; - parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); - testTerminal.compare([['exe', exes[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state CSI_INTERMEDIATE collect', function (): void { - parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.CSI_INTERMEDIATE; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('state CSI_INTERMEDIATE ignore', function (): void { - parser.reset(); - testTerminal.clear(); - parser.currentState = ParserState.CSI_INTERMEDIATE; - parse(parser, '\x7f'); + } + }); + it('trans CSI_ENTRY --> CSI_INTERMEDIATE with collect action', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.CSI_ENTRY; + parse(parser, collect[i]); chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); + parser.reset(); + } + }); + it('trans CSI_PARAM --> CSI_INTERMEDIATE with collect action', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.CSI_PARAM; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); + parser.reset(); + } + }); + it('state CSI_INTERMEDIATE execute rules', function (): void { + parser.reset(); + testTerminal.clear(); + let exes = r(0x00, 0x18); + exes = exes.concat(['\x19']); + exes = exes.concat(r(0x1c, 0x20)); + for (let i = 0; i < exes.length; ++i) { + parser.currentState = ParserState.CSI_INTERMEDIATE; + parse(parser, exes[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); + testTerminal.compare([['exe', exes[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('state CSI_INTERMEDIATE collect', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.CSI_INTERMEDIATE; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); + parser.reset(); + } + }); + it('state CSI_INTERMEDIATE ignore', function (): void { + parser.reset(); + testTerminal.clear(); + parser.currentState = ParserState.CSI_INTERMEDIATE; + parse(parser, '\x7f'); + chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); + testTerminal.compare([]); + parser.reset(); + testTerminal.clear(); + }); + it('trans CSI_INTERMEDIATE --> GROUND with csi_dispatch action', function (): void { + parser.reset(); + const dispatches = r(0x40, 0x7f); + for (let i = 0; i < dispatches.length; ++i) { + parser.currentState = ParserState.CSI_INTERMEDIATE; + parser.params = [0, 1]; + parse(parser, dispatches[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('trans CSI_ENTRY --> CSI_IGNORE', function (): void { + parser.reset(); + parser.currentState = ParserState.CSI_ENTRY; + parse(parser, '\x3a'); + chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); + parser.reset(); + }); + it('trans CSI_PARAM --> CSI_IGNORE', function (): void { + parser.reset(); + const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f']; + for (let i = 0; i < chars.length; ++i) { + parser.currentState = ParserState.CSI_PARAM; + parse(parser, '\x3b' + chars[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); + chai.expect(parser.params).eql([0, 0]); + parser.reset(); + } + }); + it('trans CSI_INTERMEDIATE --> CSI_IGNORE', function (): void { + parser.reset(); + const chars = r(0x30, 0x40); + for (let i = 0; i < chars.length; ++i) { + parser.currentState = ParserState.CSI_INTERMEDIATE; + parse(parser, chars[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); + chai.expect(parser.params).eql([0]); + parser.reset(); + } + }); + it('state CSI_IGNORE execute rules', function (): void { + parser.reset(); + testTerminal.clear(); + let exes = r(0x00, 0x18); + exes = exes.concat(['\x19']); + exes = exes.concat(r(0x1c, 0x20)); + for (let i = 0; i < exes.length; ++i) { + parser.currentState = ParserState.CSI_IGNORE; + parse(parser, exes[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); + testTerminal.compare([['exe', exes[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('state CSI_IGNORE ignore', function (): void { + parser.reset(); + testTerminal.clear(); + let ignored = r(0x20, 0x40); + ignored = ignored.concat(['\x7f']); + for (let i = 0; i < ignored.length; ++i) { + parser.currentState = ParserState.CSI_IGNORE; + parse(parser, ignored[i]); + chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); - }); - it('trans CSI_INTERMEDIATE --> GROUND with csi_dispatch action', function (): void { - parser.reset(); - const dispatches = r(0x40, 0x7f); - for (let i = 0; i < dispatches.length; ++i) { - parser.currentState = ParserState.CSI_INTERMEDIATE; - parser.params = [0, 1]; - parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans CSI_ENTRY --> CSI_IGNORE', function (): void { - parser.reset(); - parser.currentState = ParserState.CSI_ENTRY; - parse(parser, '\x3a'); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - parser.reset(); - }); - it('trans CSI_PARAM --> CSI_IGNORE', function (): void { - parser.reset(); - const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f']; - for (let i = 0; i < chars.length; ++i) { - parser.currentState = ParserState.CSI_PARAM; - parse(parser, '\x3b' + chars[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - chai.expect(parser.params).eql([0, 0]); - parser.reset(); - } - }); - it('trans CSI_INTERMEDIATE --> CSI_IGNORE', function (): void { - parser.reset(); - const chars = r(0x30, 0x40); - for (let i = 0; i < chars.length; ++i) { - parser.currentState = ParserState.CSI_INTERMEDIATE; - parse(parser, chars[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - chai.expect(parser.params).eql([0]); - parser.reset(); - } - }); - it('state CSI_IGNORE execute rules', function (): void { + } + }); + it('trans CSI_IGNORE --> GROUND', function (): void { + parser.reset(); + const dispatches = r(0x40, 0x7f); + for (let i = 0; i < dispatches.length; ++i) { + parser.currentState = ParserState.CSI_IGNORE; + parser.params = [0, 1]; + parse(parser, dispatches[i]); + chai.expect(parser.currentState).equal(ParserState.GROUND); + testTerminal.compare([]); parser.reset(); testTerminal.clear(); - let exes = r(0x00, 0x18); - exes = exes.concat(['\x19']); - exes = exes.concat(r(0x1c, 0x20)); - for (let i = 0; i < exes.length; ++i) { - parser.currentState = ParserState.CSI_IGNORE; - parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - testTerminal.compare([['exe', exes[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state CSI_IGNORE ignore', function (): void { + } + }); + it('trans ANYWHERE/ESCAPE --> SOS_PM_APC_STRING', function (): void { + parser.reset(); + // C0 + let initializers = ['\x58', '\x5e', '\x5f']; + for (let i = 0; i < initializers.length; ++i) { + parse(parser, '\x1b' + initializers[i]); + chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); parser.reset(); - testTerminal.clear(); - let ignored = r(0x20, 0x40); - ignored = ignored.concat(['\x7f']); - for (let i = 0; i < ignored.length; ++i) { - parser.currentState = ParserState.CSI_IGNORE; - parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - testTerminal.compare([]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans CSI_IGNORE --> GROUND', function (): void { - parser.reset(); - const dispatches = r(0x40, 0x7f); - for (let i = 0; i < dispatches.length; ++i) { - parser.currentState = ParserState.CSI_IGNORE; - parser.params = [0, 1]; - parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare([]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans ANYWHERE/ESCAPE --> SOS_PM_APC_STRING', function (): void { - parser.reset(); - // C0 - let initializers = ['\x58', '\x5e', '\x5f']; + } + // C1 + for (state in states) { + parser.currentState = state; + initializers = ['\x98', '\x9e', '\x9f']; for (let i = 0; i < initializers.length; ++i) { - parse(parser, '\x1b' + initializers[i]); + parse(parser, initializers[i]); chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); parser.reset(); } - // C1 - for (state in states) { - parser.currentState = state; - initializers = ['\x98', '\x9e', '\x9f']; - for (let i = 0; i < initializers.length; ++i) { - parse(parser, initializers[i]); - chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); - parser.reset(); - } - } - }); - it('state SOS_PM_APC_STRING ignore rules', function (): void { + } + }); + it('state SOS_PM_APC_STRING ignore rules', function (): void { + parser.reset(); + let ignored = r(0x00, 0x18); + ignored = ignored.concat(['\x19']); + ignored = ignored.concat(r(0x1c, 0x20)); + ignored = ignored.concat(r(0x20, 0x80)); + for (let i = 0; i < ignored.length; ++i) { + parser.currentState = ParserState.SOS_PM_APC_STRING; + parse(parser, ignored[i]); + chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); parser.reset(); - let ignored = r(0x00, 0x18); - ignored = ignored.concat(['\x19']); - ignored = ignored.concat(r(0x1c, 0x20)); - ignored = ignored.concat(r(0x20, 0x80)); - for (let i = 0; i < ignored.length; ++i) { - parser.currentState = ParserState.SOS_PM_APC_STRING; - parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); - parser.reset(); - } - }); - it('trans ANYWHERE/ESCAPE --> OSC_STRING', function (): void { - parser.reset(); - // C0 - parse(parser, '\x1b]'); + } + }); + it('trans ANYWHERE/ESCAPE --> OSC_STRING', function (): void { + parser.reset(); + // C0 + parse(parser, '\x1b]'); + chai.expect(parser.currentState).equal(ParserState.OSC_STRING); + parser.reset(); + // C1 + for (state in states) { + parser.currentState = state; + parse(parser, '\x9d'); chai.expect(parser.currentState).equal(ParserState.OSC_STRING); parser.reset(); - // C1 - for (state in states) { - parser.currentState = state; - parse(parser, '\x9d'); - chai.expect(parser.currentState).equal(ParserState.OSC_STRING); - parser.reset(); - } - }); - it('state OSC_STRING ignore rules', function (): void { + } + }); + it('state OSC_STRING ignore rules', function (): void { + parser.reset(); + const ignored = [ + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', /*'\x07',*/ '\x08', + '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', + '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f']; + for (let i = 0; i < ignored.length; ++i) { + parser.currentState = ParserState.OSC_STRING; + parse(parser, ignored[i]); + chai.expect(parser.currentState).equal(ParserState.OSC_STRING); + chai.expect(parser.osc).equal(''); parser.reset(); - const ignored = [ - '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', /*'\x07',*/ '\x08', - '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', - '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f']; - for (let i = 0; i < ignored.length; ++i) { - parser.currentState = ParserState.OSC_STRING; - parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.OSC_STRING); - chai.expect(parser.osc).equal(''); - parser.reset(); - } - }); - it('state OSC_STRING put action', function (): void { + } + }); + it('state OSC_STRING put action', function (): void { + parser.reset(); + const puts = r(0x20, 0x80); + for (let i = 0; i < puts.length; ++i) { + parser.currentState = ParserState.OSC_STRING; + parse(parser, puts[i]); + chai.expect(parser.currentState).equal(ParserState.OSC_STRING); + chai.expect(parser.osc).equal(puts[i]); parser.reset(); - const puts = r(0x20, 0x80); - for (let i = 0; i < puts.length; ++i) { - parser.currentState = ParserState.OSC_STRING; - parse(parser, puts[i]); - chai.expect(parser.currentState).equal(ParserState.OSC_STRING); - chai.expect(parser.osc).equal(puts[i]); - parser.reset(); - } - }); - it('state DCS_ENTRY', function (): void { - parser.reset(); - // C0 - parse(parser, '\x1bP'); + } + }); + it('state DCS_ENTRY', function (): void { + parser.reset(); + // C0 + parse(parser, '\x1bP'); + chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); + parser.reset(); + // C1 + for (state in states) { + parser.currentState = state; + parse(parser, '\x90'); chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); parser.reset(); - // C1 - for (state in states) { - parser.currentState = state; - parse(parser, '\x90'); - chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); - parser.reset(); - } - }); - it('state DCS_ENTRY ignore rules', function (): void { - parser.reset(); - const ignored = [ - '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', - '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', - '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; - for (let i = 0; i < ignored.length; ++i) { - parser.currentState = ParserState.DCS_ENTRY; - parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); - parser.reset(); - } - }); - it('state DCS_ENTRY --> DCS_PARAM with param/collect actions', function (): void { - parser.reset(); - const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; - const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; - for (let i = 0; i < params.length; ++i) { - parser.currentState = ParserState.DCS_ENTRY; - parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); - parser.reset(); - } + } + }); + it('state DCS_ENTRY ignore rules', function (): void { + parser.reset(); + const ignored = [ + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', + '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', + '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; + for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; - parse(parser, '\x3b'); + parse(parser, ignored[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); + parser.reset(); + } + }); + it('state DCS_ENTRY --> DCS_PARAM with param/collect actions', function (): void { + parser.reset(); + const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; + const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; + for (let i = 0; i < params.length; ++i) { + parser.currentState = ParserState.DCS_ENTRY; + parse(parser, params[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.params).eql([0, 0]); + chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); parser.reset(); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.DCS_ENTRY; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('state DCS_PARAM ignore rules', function (): void { + } + parser.currentState = ParserState.DCS_ENTRY; + parse(parser, '\x3b'); + chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); + chai.expect(parser.params).eql([0, 0]); + parser.reset(); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.DCS_ENTRY; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); + chai.expect(parser.collect).equal(collect[i]); parser.reset(); - const ignored = [ - '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', - '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', - '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; - for (let i = 0; i < ignored.length; ++i) { - parser.currentState = ParserState.DCS_PARAM; - parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - parser.reset(); - } - }); - it('state DCS_PARAM param action', function (): void { - parser.reset(); - const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; - for (let i = 0; i < params.length; ++i) { - parser.currentState = ParserState.DCS_PARAM; - parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); - parser.reset(); - } + } + }); + it('state DCS_PARAM ignore rules', function (): void { + parser.reset(); + const ignored = [ + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', + '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', + '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; + for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_PARAM; - parse(parser, '\x3b'); + parse(parser, ignored[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); + parser.reset(); + } + }); + it('state DCS_PARAM param action', function (): void { + parser.reset(); + const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; + for (let i = 0; i < params.length; ++i) { + parser.currentState = ParserState.DCS_PARAM; + parse(parser, params[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); + chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); + parser.reset(); + } + parser.currentState = ParserState.DCS_PARAM; + parse(parser, '\x3b'); + chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); + chai.expect(parser.params).eql([0, 0]); + parser.reset(); + }); + it('trans DCS_ENTRY --> DCS_IGNORE', function (): void { + parser.reset(); + parser.currentState = ParserState.DCS_ENTRY; + parse(parser, '\x3a'); + chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); + parser.reset(); + }); + it('trans DCS_PARAM --> DCS_IGNORE', function (): void { + parser.reset(); + const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f']; + for (let i = 0; i < chars.length; ++i) { + parser.currentState = ParserState.DCS_PARAM; + parse(parser, '\x3b' + chars[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); chai.expect(parser.params).eql([0, 0]); parser.reset(); - }); - it('trans DCS_ENTRY --> DCS_IGNORE', function (): void { - parser.reset(); - parser.currentState = ParserState.DCS_ENTRY; - parse(parser, '\x3a'); + } + }); + it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { + parser.reset(); + const chars = r(0x30, 0x40); + for (let i = 0; i < chars.length; ++i) { + parser.currentState = ParserState.DCS_INTERMEDIATE; + parse(parser, chars[i]); chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); parser.reset(); - }); - it('trans DCS_PARAM --> DCS_IGNORE', function (): void { + } + }); + it('state DCS_IGNORE ignore rules', function (): void { + parser.reset(); + let ignored = [ + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', + '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', + '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; + ignored = ignored.concat(r(0x20, 0x80)); + for (let i = 0; i < ignored.length; ++i) { + parser.currentState = ParserState.DCS_IGNORE; + parse(parser, ignored[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); parser.reset(); - const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f']; - for (let i = 0; i < chars.length; ++i) { - parser.currentState = ParserState.DCS_PARAM; - parse(parser, '\x3b' + chars[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); - chai.expect(parser.params).eql([0, 0]); - parser.reset(); - } - }); - it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { + } + }); + it('trans DCS_ENTRY --> DCS_INTERMEDIATE with collect action', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.DCS_ENTRY; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); parser.reset(); - const chars = r(0x30, 0x40); - for (let i = 0; i < chars.length; ++i) { - parser.currentState = ParserState.DCS_INTERMEDIATE; - parse(parser, chars[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); - parser.reset(); - } - }); - it('state DCS_IGNORE ignore rules', function (): void { + } + }); + it('trans DCS_PARAM --> DCS_INTERMEDIATE with collect action', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.DCS_PARAM; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); parser.reset(); - let ignored = [ - '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', - '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', - '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; - ignored = ignored.concat(r(0x20, 0x80)); - for (let i = 0; i < ignored.length; ++i) { - parser.currentState = ParserState.DCS_IGNORE; - parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); - parser.reset(); - } - }); - it('trans DCS_ENTRY --> DCS_INTERMEDIATE with collect action', function (): void { + } + }); + it('state DCS_INTERMEDIATE ignore rules', function (): void { + parser.reset(); + const ignored = [ + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', + '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', + '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; + for (let i = 0; i < ignored.length; ++i) { + parser.currentState = ParserState.DCS_INTERMEDIATE; + parse(parser, ignored[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.DCS_ENTRY; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('trans DCS_PARAM --> DCS_INTERMEDIATE with collect action', function (): void { + } + }); + it('state DCS_INTERMEDIATE collect action', function (): void { + parser.reset(); + const collect = r(0x20, 0x30); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.DCS_INTERMEDIATE; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); + chai.expect(parser.collect).equal(collect[i]); parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.DCS_PARAM; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('state DCS_INTERMEDIATE ignore rules', function (): void { + } + }); + it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { + parser.reset(); + const chars = r(0x30, 0x40); + for (let i = 0; i < chars.length; ++i) { + parser.currentState = ParserState.DCS_INTERMEDIATE; + parse(parser, '\x20' + chars[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); + chai.expect(parser.collect).equal('\x20'); parser.reset(); - const ignored = [ - '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', - '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', - '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; - for (let i = 0; i < ignored.length; ++i) { - parser.currentState = ParserState.DCS_INTERMEDIATE; - parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); - parser.reset(); - } - }); - it('state DCS_INTERMEDIATE collect action', function (): void { - parser.reset(); - const collect = r(0x20, 0x30); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.DCS_INTERMEDIATE; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); - parser.reset(); - } - }); - it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { - parser.reset(); - const chars = r(0x30, 0x40); - for (let i = 0; i < chars.length; ++i) { - parser.currentState = ParserState.DCS_INTERMEDIATE; - parse(parser, '\x20' + chars[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); - chai.expect(parser.collect).equal('\x20'); - parser.reset(); - } - }); - it('trans DCS_ENTRY --> DCS_PASSTHROUGH with hook', function (): void { - parser.reset(); - testTerminal.clear(); - const collect = r(0x40, 0x7f); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.DCS_ENTRY; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); - testTerminal.compare([['dcs hook', '', [0], collect[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans DCS_PARAM --> DCS_PASSTHROUGH with hook', function (): void { - parser.reset(); - testTerminal.clear(); - const collect = r(0x40, 0x7f); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.DCS_PARAM; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); - testTerminal.compare([['dcs hook', '', [0], collect[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('trans DCS_INTERMEDIATE --> DCS_PASSTHROUGH with hook', function (): void { - parser.reset(); - testTerminal.clear(); - const collect = r(0x40, 0x7f); - for (let i = 0; i < collect.length; ++i) { - parser.currentState = ParserState.DCS_INTERMEDIATE; - parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); - testTerminal.compare([['dcs hook', '', [0], collect[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state DCS_PASSTHROUGH put action', function (): void { - parser.reset(); - testTerminal.clear(); - let puts = r(0x00, 0x18); - puts = puts.concat(['\x19']); - puts = puts.concat(r(0x1c, 0x20)); - puts = puts.concat(r(0x20, 0x7f)); - for (let i = 0; i < puts.length; ++i) { - parser.currentState = ParserState.DCS_PASSTHROUGH; - parser.mockActiveDcsHandler(); - parse(parser, puts[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); - testTerminal.compare([['dcs put', puts[i]]]); - parser.reset(); - testTerminal.clear(); - } - }); - it('state DCS_PASSTHROUGH ignore', function (): void { - parser.reset(); - testTerminal.clear(); - parser.currentState = ParserState.DCS_PASSTHROUGH; - parse(parser, '\x7f'); + } + }); + it('trans DCS_ENTRY --> DCS_PASSTHROUGH with hook', function (): void { + parser.reset(); + testTerminal.clear(); + const collect = r(0x40, 0x7f); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.DCS_ENTRY; + parse(parser, collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); - testTerminal.compare([]); + testTerminal.compare([['dcs hook', '', [0], collect[i]]]); parser.reset(); testTerminal.clear(); - }); + } + }); + it('trans DCS_PARAM --> DCS_PASSTHROUGH with hook', function (): void { + parser.reset(); + testTerminal.clear(); + const collect = r(0x40, 0x7f); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.DCS_PARAM; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + testTerminal.compare([['dcs hook', '', [0], collect[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('trans DCS_INTERMEDIATE --> DCS_PASSTHROUGH with hook', function (): void { + parser.reset(); + testTerminal.clear(); + const collect = r(0x40, 0x7f); + for (let i = 0; i < collect.length; ++i) { + parser.currentState = ParserState.DCS_INTERMEDIATE; + parse(parser, collect[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + testTerminal.compare([['dcs hook', '', [0], collect[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('state DCS_PASSTHROUGH put action', function (): void { + parser.reset(); + testTerminal.clear(); + let puts = r(0x00, 0x18); + puts = puts.concat(['\x19']); + puts = puts.concat(r(0x1c, 0x20)); + puts = puts.concat(r(0x20, 0x7f)); + for (let i = 0; i < puts.length; ++i) { + parser.currentState = ParserState.DCS_PASSTHROUGH; + parser.mockActiveDcsHandler(); + parse(parser, puts[i]); + chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + testTerminal.compare([['dcs put', puts[i]]]); + parser.reset(); + testTerminal.clear(); + } + }); + it('state DCS_PASSTHROUGH ignore', function (): void { + parser.reset(); + testTerminal.clear(); + parser.currentState = ParserState.DCS_PASSTHROUGH; + parse(parser, '\x7f'); + chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + testTerminal.compare([]); + parser.reset(); + testTerminal.clear(); }); }); - runs.forEach(function (run: IRun): void { - let test: Function; - describe('escape sequence examples / ' + run.tableType, function (): void { - before(function(): void { - parser = run.parser; - test = function(s: string, value: any, noReset: any): void { - if (!noReset) { - parser.reset(); - testTerminal.clear(); - } - parse(parser, s); - testTerminal.compare(value); - }; - }); - it('CSI with print and execute', function (): void { - test('\x1b[<31;5mHello World! öäü€\nabc', - [ - ['csi', '<', [31, 5], 'm'], - ['print', 'Hello World! öäü€'], - ['exe', '\n'], - ['print', 'abc'] - ], null); - }); - it('OSC', function (): void { - test('\x1b]0;abc123€öäü\x07', [ - ['osc', '0;abc123€öäü'] + function test(s: string, value: any, noReset: any): void { + if (!noReset) { + parser.reset(); + testTerminal.clear(); + } + parse(parser, s); + testTerminal.compare(value); + } + + describe('escape sequence examples', function (): void { + it('CSI with print and execute', function (): void { + test('\x1b[<31;5mHello World! öäü€\nabc', + [ + ['csi', '<', [31, 5], 'm'], + ['print', 'Hello World! öäü€'], + ['exe', '\n'], + ['print', 'abc'] ], null); - }); - it('single DCS', function (): void { - test('\x1bP1;2;3+$abc;de\x9c', [ - ['dcs hook', '+$', [1, 2, 3], 'a'], - ['dcs put', 'bc;de'], - ['dcs unhook'] - ], null); - }); - it('multi DCS', function (): void { - test('\x1bP1;2;3+$abc;de', [ - ['dcs hook', '+$', [1, 2, 3], 'a'], - ['dcs put', 'bc;de'] - ], null); - testTerminal.clear(); - test('abc\x9c', [ - ['dcs put', 'abc'], - ['dcs unhook'] - ], true); - }); - it('print + DCS(C1)', function (): void { - test('abc\x901;2;3+$abc;de\x9c', [ - ['print', 'abc'], - ['dcs hook', '+$', [1, 2, 3], 'a'], - ['dcs put', 'bc;de'], - ['dcs unhook'] - ], null); - }); - it('print + PM(C1) + print', function (): void { - test('abc\x98123tzf\x9cdefg', [ - ['print', 'abc'], - ['print', 'defg'] - ], null); - }); - it('print + OSC(C1) + print', function (): void { - test('abc\x9d123tzf\x9cdefg', [ - ['print', 'abc'], - ['osc', '123tzf'], - ['print', 'defg'] - ], null); - }); - it('error recovery', function (): void { - test('\x1b[1€abcdefg\x9b<;c', [ - ['print', 'abcdefg'], - ['csi', '<', [0, 0], 'c'] - ], null); - }); - it('7bit ST should be swallowed', function(): void { - test('abc\x9d123tzf\x1b\\defg', [ - ['print', 'abc'], - ['osc', '123tzf'], - ['print', 'defg'] - ], null); - }); + }); + it('OSC', function (): void { + test('\x1b]0;abc123€öäü\x07', [ + ['osc', '0;abc123€öäü'] + ], null); + }); + it('single DCS', function (): void { + test('\x1bP1;2;3+$abc;de\x9c', [ + ['dcs hook', '+$', [1, 2, 3], 'a'], + ['dcs put', 'bc;de'], + ['dcs unhook'] + ], null); + }); + it('multi DCS', function (): void { + test('\x1bP1;2;3+$abc;de', [ + ['dcs hook', '+$', [1, 2, 3], 'a'], + ['dcs put', 'bc;de'] + ], null); + testTerminal.clear(); + test('abc\x9c', [ + ['dcs put', 'abc'], + ['dcs unhook'] + ], true); + }); + it('print + DCS(C1)', function (): void { + test('abc\x901;2;3+$abc;de\x9c', [ + ['print', 'abc'], + ['dcs hook', '+$', [1, 2, 3], 'a'], + ['dcs put', 'bc;de'], + ['dcs unhook'] + ], null); + }); + it('print + PM(C1) + print', function (): void { + test('abc\x98123tzf\x9cdefg', [ + ['print', 'abc'], + ['print', 'defg'] + ], null); + }); + it('print + OSC(C1) + print', function (): void { + test('abc\x9d123tzf\x9cdefg', [ + ['print', 'abc'], + ['osc', '123tzf'], + ['print', 'defg'] + ], null); + }); + it('error recovery', function (): void { + test('\x1b[1€abcdefg\x9b<;c', [ + ['print', 'abcdefg'], + ['csi', '<', [0, 0], 'c'] + ], null); + }); + it('7bit ST should be swallowed', function (): void { + test('abc\x9d123tzf\x1b\\defg', [ + ['print', 'abc'], + ['osc', '123tzf'], + ['print', 'defg'] + ], null); }); }); diff --git a/src/core/parser/EscapeSequenceParser.ts b/src/core/parser/EscapeSequenceParser.ts index 7929b2f0..ec663aa1 100644 --- a/src/core/parser/EscapeSequenceParser.ts +++ b/src/core/parser/EscapeSequenceParser.ts @@ -38,9 +38,7 @@ export class TransitionTable { public table: Uint8Array | number[]; constructor(length: number) { - this.table = (typeof Uint8Array === 'undefined') - ? new Array(length) - : new Uint8Array(length); + this.table = new Uint8Array(length); } /** From 4f412cb6b0988668dadad6827b1952e877c8d8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 3 Jun 2019 00:11:25 +0200 Subject: [PATCH 2/4] use fill for default transition; create r from blueprint --- src/core/parser/EscapeSequenceParser.ts | 64 ++++++++++++------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/src/core/parser/EscapeSequenceParser.ts b/src/core/parser/EscapeSequenceParser.ts index ec663aa1..e7612925 100644 --- a/src/core/parser/EscapeSequenceParser.ts +++ b/src/core/parser/EscapeSequenceParser.ts @@ -7,6 +7,7 @@ import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceP import { Disposable } from 'common/Lifecycle'; import { utf32ToString } from 'core/input/TextDecoder'; import { IDisposable } from 'common/Types'; +import { fill } from 'common/TypedArrayUtils'; interface IHandlerCollection { [key: string]: T[]; @@ -15,19 +16,6 @@ interface IHandlerCollection { type CsiHandler = (params: number[], collect: string) => boolean | void; type OscHandler = (data: string) => boolean | void; -/** - * Returns an array filled with numbers between the low and high parameters (right exclusive). - * @param low The low number. - * @param high The high number. - */ -function r(low: number, high: number): number[] { - let c = high - low; - const arr = new Array(c); - while (c--) { - arr[c] = --high; - } - return arr; -} /** * Transition table for EscapeSequenceParser. @@ -35,12 +23,21 @@ function r(low: number, high: number): number[] { * currentState << 8 | characterCode --> action << 4 | nextState */ export class TransitionTable { - public table: Uint8Array | number[]; + public table: Uint8Array; constructor(length: number) { this.table = new Uint8Array(length); } + /** + * Set default transition. + * @param action default action + * @param next default next state + */ + public setDefault(action: ParserAction, next: ParserState): void { + fill(this.table, action << 4 | next); + } + /** * Add a transition to the transition table. * @param code input character code @@ -48,8 +45,8 @@ export class TransitionTable { * @param action parser action to be done * @param next next parser state */ - add(code: number, state: ParserState, action: ParserAction, next: ParserState): void { - this.table[state << 8 | code] = (action << 4) | next; + public add(code: number, state: ParserState, action: ParserAction, next: ParserState): void { + this.table[state << 8 | code] = action << 4 | next; } /** @@ -59,24 +56,18 @@ export class TransitionTable { * @param action parser action to be done * @param next next parser state */ - addMany(codes: number[], state: ParserState, action: ParserAction, next: ParserState): void { + public addMany(codes: number[], state: ParserState, action: ParserAction, next: ParserState): void { for (let i = 0; i < codes.length; i++) { - this.add(codes[i], state, action, next); + this.table[state << 8 | codes[i]] = action << 4 | next; } } } -/** - * Default definitions for the VT500_TRANSITION_TABLE. - */ -const PRINTABLES = r(0x20, 0x7f); -const EXECUTABLES = r(0x00, 0x18); -EXECUTABLES.push(0x19); -EXECUTABLES.push.apply(EXECUTABLES, r(0x1c, 0x20)); -// Pseudo-character placeholder for printable non-ascii characters. +// Pseudo-character placeholder for printable non-ascii characters (unicode). const NON_ASCII_PRINTABLE = 0xA0; + /** * VT500 compatible transition table. * Taken from https://vt100.net/emu/dec_ansi_parser. @@ -84,16 +75,21 @@ const NON_ASCII_PRINTABLE = 0xA0; export const VT500_TRANSITION_TABLE = (function (): TransitionTable { const table: TransitionTable = new TransitionTable(4095); + // range macro + const blueprint = Array.apply(null, Array(256)).map((unused: any, i: number) => i); + const r = (start: number, end: number) => blueprint.slice(start, end); + + // Default definitions. + const PRINTABLES = r(0x20, 0x7f); + const EXECUTABLES = r(0x00, 0x18); + EXECUTABLES.push(0x19); + EXECUTABLES.push.apply(EXECUTABLES, r(0x1c, 0x20)); + const states: number[] = r(ParserState.GROUND, ParserState.DCS_PASSTHROUGH + 1); let state: any; - // table with default transition - for (state in states) { - // NOTE: table lookup is capped at 0xa0 in parse to keep the table small - for (let code = 0; code <= NON_ASCII_PRINTABLE; ++code) { - table.add(code, state, ParserAction.ERROR, ParserState.GROUND); - } - } + // set default transition + table.setDefault(ParserAction.ERROR, ParserState.GROUND); // printables table.addMany(PRINTABLES, ParserState.GROUND, ParserAction.PRINT, ParserState.GROUND); // global anywhere rules @@ -396,7 +392,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP let osc = this._osc; let collect = this._collect; let params = this._params; - const table: Uint8Array | number[] = this.TRANSITIONS.table; + const table: Uint8Array = this.TRANSITIONS.table; let dcsHandler: IDcsHandler | null = this._activeDcsHandler; let callback: Function | null = null; From e1243473e810c823075f3ed2fbcef5222d8e7b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 3 Jun 2019 00:28:32 +0200 Subject: [PATCH 3/4] use NON_ASCII_PRINTABLE, cleanup ERROR action --- src/core/parser/EscapeSequenceParser.test.ts | 4 +- src/core/parser/EscapeSequenceParser.ts | 61 ++++++-------------- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/src/core/parser/EscapeSequenceParser.test.ts b/src/core/parser/EscapeSequenceParser.test.ts index d550b1d5..6f789343 100644 --- a/src/core/parser/EscapeSequenceParser.test.ts +++ b/src/core/parser/EscapeSequenceParser.test.ts @@ -976,9 +976,9 @@ describe('EscapeSequenceParser', function (): void { ], null); }); it('single DCS', function (): void { - test('\x1bP1;2;3+$abc;de\x9c', [ + test('\x1bP1;2;3+$aäbc;däe\x9c', [ ['dcs hook', '+$', [1, 2, 3], 'a'], - ['dcs put', 'bc;de'], + ['dcs put', 'äbc;däe'], ['dcs unhook'] ], null); }); diff --git a/src/core/parser/EscapeSequenceParser.ts b/src/core/parser/EscapeSequenceParser.ts index e7612925..d62ccaa7 100644 --- a/src/core/parser/EscapeSequenceParser.ts +++ b/src/core/parser/EscapeSequenceParser.ts @@ -189,7 +189,12 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { table.addMany(PRINTABLES, ParserState.DCS_PASSTHROUGH, ParserAction.DCS_PUT, ParserState.DCS_PASSTHROUGH); table.add(0x7f, ParserState.DCS_PASSTHROUGH, ParserAction.IGNORE, ParserState.DCS_PASSTHROUGH); table.addMany([0x1b, 0x9c], ParserState.DCS_PASSTHROUGH, ParserAction.DCS_UNHOOK, ParserState.GROUND); + // special handling of unicode chars + table.add(NON_ASCII_PRINTABLE, ParserState.GROUND, ParserAction.PRINT, ParserState.GROUND); table.add(NON_ASCII_PRINTABLE, ParserState.OSC_STRING, ParserAction.OSC_PUT, ParserState.OSC_STRING); + table.add(NON_ASCII_PRINTABLE, ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.CSI_IGNORE); + table.add(NON_ASCII_PRINTABLE, ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE); + table.add(NON_ASCII_PRINTABLE, ParserState.DCS_PASSTHROUGH, ParserAction.DCS_PUT, ParserState.DCS_PASSTHROUGH); return table; })(); @@ -385,7 +390,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP parse(data: Uint32Array, length: number): void { let code = 0; let transition = 0; - let error = false; let currentState = this.currentState; let print = -1; let dcs = -1; @@ -443,47 +447,20 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP } break; case ParserAction.ERROR: - // chars higher than 0x9f are handled by this action - // to keep the transition table small - if (code > 0x9f) { - switch (currentState) { - case ParserState.GROUND: - print = (~print) ? print : i; - break; - case ParserState.CSI_IGNORE: - transition |= ParserState.CSI_IGNORE; - break; - case ParserState.DCS_IGNORE: - transition |= ParserState.DCS_IGNORE; - break; - case ParserState.DCS_PASSTHROUGH: - dcs = (~dcs) ? dcs : i; - transition |= ParserState.DCS_PASSTHROUGH; - break; - default: - error = true; - } - } else { - error = true; - } - // if we end up here a real error happened - if (error) { - const inject: IParsingState = this._errorHandler( - { - position: i, - code, - currentState, - print, - dcs, - osc, - collect, - params, - abort: false - }); - if (inject.abort) return; - // TODO: inject return values - error = false; - } + const inject: IParsingState = this._errorHandler( + { + position: i, + code, + currentState, + print, + dcs, + osc, + collect, + params, + abort: false + }); + if (inject.abort) return; + // inject values: currently not implemented break; case ParserAction.CSI_DISPATCH: // Trigger CSI Handler From 9442d6e7ff82903c876f000731f15d26ec8c379f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 3 Jun 2019 01:23:54 +0200 Subject: [PATCH 4/4] enum for table packing --- src/core/parser/EscapeSequenceParser.ts | 29 ++++++++++++++++--------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/core/parser/EscapeSequenceParser.ts b/src/core/parser/EscapeSequenceParser.ts index d62ccaa7..36cf850b 100644 --- a/src/core/parser/EscapeSequenceParser.ts +++ b/src/core/parser/EscapeSequenceParser.ts @@ -16,11 +16,19 @@ interface IHandlerCollection { type CsiHandler = (params: number[], collect: string) => boolean | void; type OscHandler = (data: string) => boolean | void; +/** + * Table values are generated like this: + * index: currentState << TableValue.INDEX_STATE_SHIFT | charCode + * value: action << TableValue.TRANSITION_ACTION_SHIFT | nextState + */ +const enum TableAccess { + TRANSITION_ACTION_SHIFT = 4, + TRANSITION_STATE_MASK = 15, + INDEX_STATE_SHIFT = 8 +} /** * Transition table for EscapeSequenceParser. - * NOTE: data in the underlying table is packed like this: - * currentState << 8 | characterCode --> action << 4 | nextState */ export class TransitionTable { public table: Uint8Array; @@ -35,7 +43,7 @@ export class TransitionTable { * @param next default next state */ public setDefault(action: ParserAction, next: ParserState): void { - fill(this.table, action << 4 | next); + fill(this.table, action << TableAccess.TRANSITION_ACTION_SHIFT | next); } /** @@ -46,7 +54,7 @@ export class TransitionTable { * @param next next parser state */ public add(code: number, state: ParserState, action: ParserAction, next: ParserState): void { - this.table[state << 8 | code] = action << 4 | next; + this.table[state << TableAccess.INDEX_STATE_SHIFT | code] = action << TableAccess.TRANSITION_ACTION_SHIFT | next; } /** @@ -58,7 +66,7 @@ export class TransitionTable { */ public addMany(codes: number[], state: ParserState, action: ParserAction, next: ParserState): void { for (let i = 0; i < codes.length; i++) { - this.table[state << 8 | codes[i]] = action << 4 | next; + this.table[state << TableAccess.INDEX_STATE_SHIFT | codes[i]] = action << TableAccess.TRANSITION_ACTION_SHIFT | next; } } } @@ -75,8 +83,9 @@ const NON_ASCII_PRINTABLE = 0xA0; export const VT500_TRANSITION_TABLE = (function (): TransitionTable { const table: TransitionTable = new TransitionTable(4095); - // range macro - const blueprint = Array.apply(null, Array(256)).map((unused: any, i: number) => i); + // range macro for byte + const BYTE_VALUES = 256; + const blueprint = Array.apply(null, Array(BYTE_VALUES)).map((unused: any, i: number) => i); const r = (start: number, end: number) => blueprint.slice(start, end); // Default definitions. @@ -420,8 +429,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP } // normal transition & action lookup - transition = table[currentState << 8 | (code < 0xa0 ? code : NON_ASCII_PRINTABLE)]; - switch (transition >> 4) { + transition = table[currentState << TableAccess.INDEX_STATE_SHIFT | (code < 0xa0 ? code : NON_ASCII_PRINTABLE)]; + switch (transition >> TableAccess.TRANSITION_ACTION_SHIFT) { case ParserAction.PRINT: print = (~print) ? print : i; break; @@ -570,7 +579,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP dcs = -1; break; } - currentState = transition & 15; + currentState = transition & TableAccess.TRANSITION_STATE_MASK; } // push leftover pushable buffers to terminal