apple2js/test/js/applesoft/decompiler.spec.ts
2022-07-23 12:00:38 -07:00

232 lines
9.1 KiB
TypeScript

import ApplesoftDecompiler from 'js/applesoft/decompiler';
import ApplesoftCompiler from 'js/applesoft/compiler';
import RAM from 'js/ram';
import { Memory } from 'js/types';
function decompileFromMemory(ram: Memory): string {
const decompiler = ApplesoftDecompiler.decompilerFromMemory(ram);
return decompiler.list();
}
describe('ApplesoftDecompiler', () => {
it('decompiles one-line program from memory', () => {
const ram = new RAM(0x00, 0xff); // 64K
ApplesoftCompiler.compileToMemory(ram, '10 PRINT "Hello, World!"');
const program = decompileFromMemory(ram);
expect(program).toEqual(' 10 PRINT "Hello, World!"\n');
});
it('decompiles REM statements correctly', () => {
const ram = new RAM(0x00, 0xff); // 64K
ApplesoftCompiler.compileToMemory(ram, '10 REMNo space before\n20 REM with space');
const program = decompileFromMemory(ram);
expect(program).toEqual(' 10 REM No space before\n 20 REM with space\n');
});
it('lists a one-line program', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 PRINT "Hello, World!"');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.list();
expect(program).toEqual(' 10 PRINT "Hello, World!"\n');
});
it('correctly computes the base address when 0 is passed in', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 PRINT "Hello, World!"\n20 GOTO 10');
const decompiler = new ApplesoftDecompiler(compiler.program(), 0);
const program = decompiler.list();
expect(program).toEqual(' 10 PRINT "Hello, World!"\n 20 GOTO 10\n');
});
it('lists a program with a long line', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 PRINT "Hello, World!"\n'
+ '20 PRINT "Hello, again, with a much longer line this time."\n'
+ '30 REM1234567890123456789012345678901234567890');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.list();
expect(program).toEqual(' 10 PRINT "Hello, World!"\n'
+ ' 20 PRINT "Hello, again, with a \n'
+ ' much longer line this time."\n'
+ ' \n'
+ ' 30 REM 123456789012345678901234\n'
+ ' 5678901234567890\n');
});
it('lists a program with a long line Apple ][+-style', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 PRINT "Hello, World!"\n'
+ '20 PRINT "Hello, again, with a much longer line this time."\n'
+ '30 REM1234567890123456789012345678901234567890');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.list({ apple2: 'plus' });
expect(program).toEqual('10 PRINT "Hello, World!"\n'
+ '20 PRINT "Hello, again, with a m\n'
+ ' uch longer line this time."\n'
+ '30 REM 1234567890123456789012345\n'
+ ' 678901234567890\n');
});
it('lists a range of lines', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 PRINT "Hello, World!"\n'
+ '20 PRINT "Hello, again, with a much longer line this time."\n'
+ '30 REM1234567890123456789012345678901234567890');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.list({}, 10, 20);
expect(program).toEqual(' 10 PRINT "Hello, World!"\n'
+ ' 20 PRINT "Hello, again, with a \n'
+ ' much longer line this time."\n'
+ ' \n');
});
it('lists weird code correctly', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 NOT RACE A THEN B');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.list();
expect(program).toEqual(' 10 NOTRACE AT HENB\n');
});
it('lists 10ATOZ correctly', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10ATOZ');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.list();
expect(program).toEqual(' 10 A TO Z\n');
});
it('wraps correctly in 80-column mode', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 ?:?:?:?:?:?:?:?:?:?:?:?:?:?:?:?');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.list({ columns: 80 });
expect(program).toEqual(' 10 PRINT : PRINT : PRINT : PRINT : '
+ 'PRINT : PRINT : PRINT : PRINT : PRINT \n'
+ ' : PRINT : PRINT : PRINT : PRINT : PRINT : PRINT : '
+ 'PRINT \n');
});
it('decompiles compactly', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 ?:?:?:?:?:?:?:?:?:?:?:?:?:?:?:?');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'compact' });
expect(program).toEqual('10?:?:?:?:?:?:?:?:?:?:?:?:?:?:?:?');
});
it('when decompiling compactly, adds a space after the line', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 12345');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'compact' });
expect(program).toEqual('10 12345');
});
it('when decompiling compactly, adds a space after AT for token', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 AT NEXT');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'compact' });
expect(program).toEqual('10AT NEXT');
});
it('when decompiling compactly, adds a space after AT for literal', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 AT n');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'compact' });
expect(program).toEqual('10AT N');
});
it('when decompiling compactly, decompiles 10ATOZ correctly', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10ATOZ');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'compact' });
expect(program).toEqual('10ATOZ');
});
it('when decompiling compactly, adds a space to disambiguate tokens', () => {
const compiler = new ApplesoftCompiler();
compiler.compile([
'10 A THEN B',
'30 A TO Z',
'40 AT N',
'50 A TN',
'60 N O T R A C E',
'70 NOT RACE'].join('\n'));
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'compact' });
expect(program).toEqual([
'10ATHENB',
'30ATOZ',
'40AT N',
'50ATN',
'60NOTRACE',
'70NOTRACE'].join('\n'));
});
it('when decompiling prettily, formats reasonably well', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 FORI=1TO10:PRINTI:NEXT');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'pretty' });
expect(program).toEqual('10 FOR I = 1 TO 10 : PRINT I : NEXT');
});
it('when decompiling prettily, formats relations', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 IFA<BORA>=BORB<=AORB=A THEN');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'pretty' });
expect(program).toEqual('10 IF A < B OR A >= B OR B <= A OR B = AT HEN');
});
it('when decompiling prettily, decompiles 10ATOZ correctly', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10ATOZ');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'pretty' });
expect(program).toEqual('10 A TO Z');
});
it('when decompiling prettily, does not insert extra spaces in strings', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10A="::::":B=","');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'pretty' });
expect(program).toEqual('10 A = "::::" : B = ","');
});
it('when decompiling prettily, inserts space after comma', () => {
const compiler = new ApplesoftCompiler();
compiler.compile('10 HPLOTX,Y:GOTO10');
const decompiler = new ApplesoftDecompiler(compiler.program());
const program = decompiler.decompile({ style: 'pretty' });
expect(program).toEqual('10 HPLOT X, Y : GOTO 10');
});
});