mirror of
https://github.com/We-Dont-Byte/Mind_Reader.git
synced 2025-02-04 10:38:42 +00:00
@@ -5,10 +5,12 @@ import { runTests } from '@vscode/test-electron';
|
||||
async function main() {
|
||||
try {
|
||||
// The folder containing package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath: string = path.resolve(__dirname, '../../');
|
||||
|
||||
// The path to the test runner script
|
||||
const extensionTestsPath: string = path.resolve(__dirname, './suite/index');
|
||||
// Passed to `--extensionTestsPath`
|
||||
const extensionTestsPath: string = path.resolve(__dirname, './suites/index');
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import * as assert from 'assert';
|
||||
import * as vscode from 'vscode';
|
||||
import { after } from 'mocha';
|
||||
|
||||
suite('Dummy Test Suite', () => {
|
||||
after(() => {
|
||||
vscode.window.showInformationMessage('All tests passed!');
|
||||
});
|
||||
|
||||
test('Dummy Test', () => {
|
||||
assert.strictEqual(0 === 0, true);
|
||||
});
|
||||
});
|
||||
@@ -35,4 +35,4 @@ export function run(): Promise<void> {
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
279
src/test/suites/pylex/lexer.test.ts
Normal file
279
src/test/suites/pylex/lexer.test.ts
Normal file
@@ -0,0 +1,279 @@
|
||||
import * as assert from 'assert';
|
||||
import * as vscode from 'vscode';
|
||||
import { after } from 'mocha';
|
||||
|
||||
import Lexer from '../../../pylex/lexer';
|
||||
import LineToken from '../../../pylex/token';
|
||||
import { EOFTOKEN, Symbol } from '../../../pylex/token';
|
||||
|
||||
suite('Lexer Test Suite', () => {
|
||||
after(() => {
|
||||
vscode.window.showInformationMessage('All tests passed!');
|
||||
});
|
||||
|
||||
test('Empty String', () => {
|
||||
let l: Lexer = new Lexer(undefined);
|
||||
assert.deepStrictEqual(l.currToken(), EOFTOKEN);
|
||||
});
|
||||
|
||||
test('Undefined', () => {
|
||||
let l: Lexer = new Lexer('');
|
||||
assert.deepStrictEqual(l.currToken(), EOFTOKEN);
|
||||
});
|
||||
|
||||
test('Whitespace', () => {
|
||||
let l: Lexer = new Lexer(' \t\t'.repeat(4).repeat(4));
|
||||
assert.deepStrictEqual(l.currToken(), EOFTOKEN);
|
||||
});
|
||||
|
||||
test('Non-Whitespace with no construct', () => {
|
||||
let l: Lexer = new Lexer('foobar');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.INDENT, 0, 0));
|
||||
});
|
||||
|
||||
test('getIndent() accuracy, spaces', () => {
|
||||
for (var i = 0; i < 100; i++) {
|
||||
let l: Lexer = new Lexer(' '.repeat(i) + 'foobar');
|
||||
assert.strictEqual(l.currToken().indentLevel, i);
|
||||
}
|
||||
});
|
||||
|
||||
test('getIndent() accuracy, tabs', () => {
|
||||
for (var i = 0; i < 100; i++) {
|
||||
let l: Lexer = new Lexer('\t'.repeat(i) + 'foobar', {size: 4, hard: true});
|
||||
assert.strictEqual(l.currToken().indentLevel, i);
|
||||
}
|
||||
});
|
||||
|
||||
test('getIndent() accuracy, spaces with incomplete tab', () => {
|
||||
for (var i = 0; i < 100; i++) {
|
||||
for (var j = 1; j <= 3; j++) {
|
||||
let l: Lexer = new Lexer(' '.repeat(i) + ' '.repeat(j) + 'foobar', {size: 4, hard: false});
|
||||
assert.strictEqual(l.currToken().indentLevel, i+1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('class definition', () => {
|
||||
let l: Lexer = new Lexer('class Foobar(object):');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.CLASS, 0, 0, 'Foobar'));
|
||||
});
|
||||
|
||||
test('function definition', () => {
|
||||
let l: Lexer = new Lexer('def Barbaz(this, that, andTheOther):');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.FUNCTION, 0, 0, 'Barbaz'));
|
||||
});
|
||||
|
||||
test('if statement', () => {
|
||||
let l: Lexer = new Lexer('if True and bar == baz:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.IF, 0, 0, 'True and bar == baz'));
|
||||
});
|
||||
|
||||
test('elif statement', () => {
|
||||
let l: Lexer = new Lexer('elif name == "bar" and True:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.ELIF, 0, 0, 'name == "bar" and True'));
|
||||
});
|
||||
|
||||
test('else statement', () => {
|
||||
let l: Lexer = new Lexer('else:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.ELSE, 0, 0));
|
||||
});
|
||||
|
||||
test('for loop', () => {
|
||||
let l: Lexer = new Lexer('for pickle in pickleJars:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.FOR, 0, 0, 'pickle in pickleJars'));
|
||||
});
|
||||
|
||||
test('while loop', () => {
|
||||
let l: Lexer = new Lexer('while numCookies < capacity:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.WHILE, 0, 0, 'numCookies < capacity'));
|
||||
});
|
||||
|
||||
test('try statement', () => {
|
||||
let l: Lexer = new Lexer('try:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.TRY, 0, 0));
|
||||
});
|
||||
|
||||
test('except statement with attr', () => {
|
||||
let l: Lexer = new Lexer('except NameError:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EXCEPT, 0, 0, 'NameError'));
|
||||
});
|
||||
|
||||
test('except statement with no attr', () => {
|
||||
let l: Lexer = new Lexer('except:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EXCEPT, 0, 0));
|
||||
});
|
||||
|
||||
test('finally statement', () => {
|
||||
let l: Lexer = new Lexer('finally:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.FINALLY, 0, 0));
|
||||
});
|
||||
|
||||
test('with statement', () => {
|
||||
let l: Lexer = new Lexer('with open(file) as f:');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.WITH, 0, 0, 'open(file) as f'));
|
||||
});
|
||||
|
||||
test('restart()', () => {
|
||||
let l: Lexer = new Lexer('with open(file as f:');
|
||||
l.restart('if is_old():');
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.IF, 0, 0, 'is_old()'));
|
||||
});
|
||||
|
||||
test('next() ignores empty lines', () => {
|
||||
let lines: string[] = [
|
||||
'if wurst_available():',
|
||||
'',
|
||||
' eat_wurst()'
|
||||
];
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
l.next();
|
||||
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.INDENT, 2, 1));
|
||||
});
|
||||
|
||||
test('retract() ignores empty lines', () => {
|
||||
let lines: string[] = [
|
||||
'if wurst_available():',
|
||||
'',
|
||||
' eat_wurst()'
|
||||
];
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
l.next();
|
||||
|
||||
l.retract();
|
||||
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.IF, 0, 0, 'wurst_available()'));
|
||||
});
|
||||
|
||||
test('next() ignores whitespace lines', () => {
|
||||
let lines: string[] = [
|
||||
'if wurst_available():',
|
||||
' \t \t ',
|
||||
' eat_wurst()'
|
||||
];
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
l.next();
|
||||
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.INDENT, 2, 1));
|
||||
});
|
||||
|
||||
test('retract() ignores whitespace lines', () => {
|
||||
let lines: string[] = [
|
||||
'if wurst_available():',
|
||||
' \t \t ',
|
||||
' eat_wurst()'
|
||||
];
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
// Advance to end of input
|
||||
// Eliminates dependence on next()
|
||||
// skipping whitespace
|
||||
do {} while (l.next() !== EOFTOKEN);
|
||||
|
||||
l.retract(); // retract past EOFTOKEn
|
||||
l.retract();
|
||||
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.IF, 0, 0, 'wurst_available()'));
|
||||
});
|
||||
|
||||
test('next() ignores comment lines', () => {
|
||||
let lines: string[] = [
|
||||
'if wurst_available():',
|
||||
' \t # I hate testing \t',
|
||||
' eat_wurst()'
|
||||
];
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
l.next();
|
||||
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.INDENT, 2, 1));
|
||||
});
|
||||
|
||||
test('retract() ignores comment lines', () => {
|
||||
let lines: string[] = [
|
||||
'if wurst_available():',
|
||||
' \t # \t',
|
||||
' eat_wurst()'
|
||||
];
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
// Advance to end of input
|
||||
// Eliminates dependence on next()
|
||||
// skipping comment
|
||||
do {} while (l.next() !== EOFTOKEN);
|
||||
|
||||
l.retract(); // retract past EOFTOKEn
|
||||
l.retract();
|
||||
|
||||
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.IF, 0, 0, 'wurst_available()'));
|
||||
});
|
||||
|
||||
test('next() out of range', () => {
|
||||
let l: Lexer = new Lexer('foo = zaboomafoo');
|
||||
l.next();
|
||||
assert.throws(() => l.next());
|
||||
});
|
||||
|
||||
test('retract() out of range', () => {
|
||||
let l: Lexer = new Lexer('next_token = lexer.next()');
|
||||
assert.throws(() => l.retract());
|
||||
});
|
||||
|
||||
test('retract() validate argument', () => {
|
||||
let l: Lexer = new Lexer();
|
||||
|
||||
// Negative
|
||||
assert.throws(() => l.retract(-1));
|
||||
|
||||
// Zero, it doesn't make sense to retract 0 :P
|
||||
assert.throws(() => l.retract(0));
|
||||
|
||||
});
|
||||
|
||||
test('retract() 1-100', () => {
|
||||
let lines: string[] = Array.from(Array(100), (_, i) => 'line' + i);
|
||||
let reference: LineToken[] = lines.map((_, i) => {
|
||||
return new LineToken(Symbol.INDENT, i, 0);
|
||||
});
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
// advance to EOF
|
||||
do {} while (l.next() !== EOFTOKEN);
|
||||
|
||||
// try retract
|
||||
l.retract(i+1);
|
||||
|
||||
assert.deepStrictEqual(l.currToken(), reference[99-i]);
|
||||
}
|
||||
});
|
||||
|
||||
test('2 full lex and retract passes', () => {
|
||||
let lines: string[] = Array.from(Array(100), (_, i)=> 'line' + i);
|
||||
let reference: LineToken[] = lines.map((_, i) => {
|
||||
return new LineToken(Symbol.INDENT, i, 0);
|
||||
});
|
||||
|
||||
let l: Lexer = new Lexer(lines.join('\n'));
|
||||
|
||||
// Twice
|
||||
for (var _ of [0,1]) {
|
||||
// advance to EOF
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
assert.deepStrictEqual(l.currToken(), reference[i]);
|
||||
l.next();
|
||||
}
|
||||
|
||||
// retract to start
|
||||
for (var i = lines.length - 1; i >= 0; i--) {
|
||||
l.retract();
|
||||
assert.deepStrictEqual(l.currToken(), reference[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
121
src/test/suites/pylex/node.test.ts
Normal file
121
src/test/suites/pylex/node.test.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import * as assert from 'assert';
|
||||
import * as vscode from 'vscode';
|
||||
import { after } from 'mocha';
|
||||
import { deparent } from '../../util';
|
||||
|
||||
import LineToken from '../../../pylex/token';
|
||||
import { Symbol } from '../../../pylex/token';
|
||||
import LexNode from '../../../pylex/node';
|
||||
|
||||
suite('LexNode Test Suite', () => {
|
||||
after(() => {
|
||||
vscode.window.showInformationMessage('All tests passed!');
|
||||
});
|
||||
|
||||
test('children() of leaf', () => {
|
||||
let n: LexNode = new LexNode('leafLexNode', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.INDENT, 0, 0));
|
||||
assert.strictEqual(n.children(), null);
|
||||
});
|
||||
|
||||
test('children() of internal node', () => {
|
||||
let children: LexNode[] = [
|
||||
new LexNode('leafLexNode1', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 2, 1)),
|
||||
new LexNode('leafLexNode2', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 3, 1)),
|
||||
new LexNode('leafLexNode3', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 4, 1)),
|
||||
new LexNode('leafLexNode4', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 5, 1)),
|
||||
new LexNode('leafLexNode5', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 6, 1))
|
||||
];
|
||||
|
||||
let parent: LexNode = new LexNode(
|
||||
'internalLexNode',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.FUNCTION, 0, 0, 'foobar'),
|
||||
children
|
||||
);
|
||||
|
||||
assert.notStrictEqual(parent.children(), null);
|
||||
assert.notStrictEqual(parent.children(), []);
|
||||
assert.strictEqual(parent.children()!.length, children.length);
|
||||
for (var i = 0; i < children.length; i++) {}
|
||||
assert.strictEqual(parent.children()![i], children[i]);
|
||||
}
|
||||
);
|
||||
|
||||
test('adopt() to empty', () => {
|
||||
let children: LexNode[] = [
|
||||
new LexNode('leafLexNode1', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 2, 1)),
|
||||
new LexNode('leafLexNode2', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 3, 1)),
|
||||
new LexNode('leafLexNode3', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 4, 1)),
|
||||
new LexNode('leafLexNode4', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 5, 1)),
|
||||
new LexNode('leafLexNode5', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 6, 1))
|
||||
];
|
||||
|
||||
let testParent: LexNode = new LexNode(
|
||||
'internalLexNode',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.FUNCTION, 1, 0, 'foobar')
|
||||
);
|
||||
|
||||
let referenceParent: LexNode = new LexNode(
|
||||
'internalLexNode',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.FUNCTION, 1, 0, 'foobar'),
|
||||
children
|
||||
);
|
||||
|
||||
// parentify reference childdren
|
||||
referenceParent = new LexNode(
|
||||
referenceParent.label,
|
||||
referenceParent.collapsibleState,
|
||||
referenceParent.token,
|
||||
referenceParent.children()!.map(c => new LexNode(c.label, c.collapsibleState, c.token, null, referenceParent))
|
||||
);
|
||||
|
||||
testParent.adopt(children);
|
||||
|
||||
assert.deepStrictEqual(deparent(testParent), deparent(referenceParent));
|
||||
});
|
||||
|
||||
test('adopt() to !empty', () => {
|
||||
let children1: LexNode[] = [
|
||||
new LexNode('leafLexNode1', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 2, 1)),
|
||||
];
|
||||
|
||||
let children2: LexNode[] = [
|
||||
new LexNode('leafLexNode2', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 3, 1)),
|
||||
new LexNode('leafLexNode3', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 4, 1)),
|
||||
];
|
||||
|
||||
let testParent: LexNode = new LexNode(
|
||||
'internalLexNode',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.FUNCTION, 1, 0, 'foobar'),
|
||||
children1,
|
||||
);
|
||||
|
||||
let referenceParent: LexNode = new LexNode(
|
||||
'internalLexNode',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.FUNCTION, 1, 0, 'foobar'),
|
||||
children1.concat(children2),
|
||||
);
|
||||
|
||||
testParent.adopt(children2);
|
||||
|
||||
assert.deepStrictEqual(deparent(testParent), deparent(referenceParent));
|
||||
});
|
||||
|
||||
test('tooltip without line number', () => {
|
||||
let testTooltip: string | vscode.MarkdownString | undefined = new LexNode('leafLexNode', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, -1, -11)).tooltip;
|
||||
let referenceTooltip: string = "leafLexNode";
|
||||
assert.notStrictEqual(testTooltip, undefined);
|
||||
assert.strictEqual(testTooltip, referenceTooltip);
|
||||
});
|
||||
|
||||
test('tooltip with line number', () => {
|
||||
let testTooltip: string | vscode.MarkdownString | undefined = new LexNode('leafLexNode', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.WHILE, 6, 1)).tooltip;
|
||||
let referenceTooltip: string = "leafLexNode: 7"; // 7 because it's 0 indexed in memory, but editor lines start at 1
|
||||
assert.notStrictEqual(testTooltip, undefined);
|
||||
assert.strictEqual(testTooltip, referenceTooltip);
|
||||
});
|
||||
});
|
||||
239
src/test/suites/pylex/parser.test.ts
Normal file
239
src/test/suites/pylex/parser.test.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import * as assert from 'assert';
|
||||
import * as vscode from 'vscode';
|
||||
import { after } from 'mocha';
|
||||
import { deparent, root } from '../../util';
|
||||
|
||||
import Parser from '../../../pylex/parser';
|
||||
import LexNode from '../../../pylex/node';
|
||||
import LineToken from '../../../pylex/token';
|
||||
import { Symbol } from '../../../pylex/token';
|
||||
|
||||
type ParserTest = {
|
||||
name: string,
|
||||
input: string[],
|
||||
output: LexNode,
|
||||
};
|
||||
|
||||
const tests: ParserTest[] = [
|
||||
{
|
||||
name: 'No Input',
|
||||
input: [ ],
|
||||
output: root(null),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Single line without construct',
|
||||
input: [ 'foo = "Yellow M&Ms make me angry >:(' ],
|
||||
output: root(null),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Single line with construct',
|
||||
input: [ 'for x of y:' ],
|
||||
output: root([
|
||||
new LexNode(
|
||||
'for x of y',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.FOR, 0, 0, 'x of y')
|
||||
)
|
||||
]),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Sequential lines, without construct',
|
||||
input: [
|
||||
'bar = "Blue M&Ms make me happy <:)"',
|
||||
'reba = "A hard working gal"'
|
||||
],
|
||||
output: root(null),
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Sequential lines, with, then without construct',
|
||||
input: [
|
||||
'if radioshack:',
|
||||
' print radioshack.hours',
|
||||
'billy = "Scrubbly Bubbles!"'
|
||||
],
|
||||
output: root([
|
||||
new LexNode('if radioshack',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 0, 0, 'radioshack'))
|
||||
])
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Sequential lines, without, then with construct',
|
||||
input: [
|
||||
'billy = "Scrubbly Bubbles!"',
|
||||
'if radioshack:',
|
||||
' print radioshack.hours'
|
||||
],
|
||||
output: root([
|
||||
new LexNode('if radioshack',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 1, 0, 'radioshack'))
|
||||
])
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Sequential lines with constructs',
|
||||
input: [
|
||||
'if yummy:',
|
||||
' print("HOoray!")',
|
||||
'elif just_ok:',
|
||||
' print("Do you have anything else?")',
|
||||
'else:',
|
||||
' print("You really eat this?")',
|
||||
],
|
||||
output: root([
|
||||
new LexNode('if yummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 0, 0, 'yummy')),
|
||||
new LexNode('elif just_ok',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.ELIF, 2, 0, 'just_ok')),
|
||||
new LexNode('else',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.ELSE, 4, 0)),
|
||||
])
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Singly Nested Block',
|
||||
input: [
|
||||
'if yummy:',
|
||||
' if in_my_tummy:',
|
||||
' exclaim("Scrumdiddlyumptious!")'
|
||||
],
|
||||
output: root([
|
||||
new LexNode('if yummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 0, 0, 'yummy'),
|
||||
[
|
||||
new LexNode('if in_my_tummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'))
|
||||
]
|
||||
)
|
||||
])
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Singly Nested Block, then Block',
|
||||
input: [
|
||||
'if yummy:',
|
||||
' if in_my_tummy:',
|
||||
' exclaim("Scrumdiddlyumptious!")',
|
||||
'else:',
|
||||
' exclaim("DAESGUSTEN~)"'
|
||||
],
|
||||
output: root([
|
||||
new LexNode('if yummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 0, 0, 'yummy'),
|
||||
[
|
||||
new LexNode('if in_my_tummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'))
|
||||
]
|
||||
),
|
||||
new LexNode('else',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.ELSE, 3, 0),
|
||||
)
|
||||
])
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Doubly Nested Block',
|
||||
input: [
|
||||
'if yummy:',
|
||||
' if in_my_tummy:',
|
||||
' if looks_like_a_mummy:',
|
||||
' print("you have a spot on your tummy"',
|
||||
'else:',
|
||||
' print("Food is food...")'
|
||||
],
|
||||
output: root([
|
||||
new LexNode('if yummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 0, 0, 'yummy'),
|
||||
[
|
||||
new LexNode('if in_my_tummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
|
||||
[
|
||||
new LexNode('if looks_like_a_mummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy'))
|
||||
]
|
||||
)
|
||||
]
|
||||
),
|
||||
new LexNode('else',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.ELSE, 4, 0),
|
||||
)
|
||||
])
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Doubly Nested Block, with multiple indent resets',
|
||||
input: [
|
||||
'if yummy:',
|
||||
' if in_my_tummy:',
|
||||
' if looks_like_a_mummy:',
|
||||
' print("you have a spot on your tummy"',
|
||||
' else:',
|
||||
' print("eek! a zombie!)',
|
||||
' elif in_my_mouth:',
|
||||
' print("ill be in my tummy soon!"',
|
||||
'else:',
|
||||
' print("Food is food...")'
|
||||
],
|
||||
output: root([
|
||||
new LexNode('if yummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 0, 0, 'yummy'),
|
||||
[
|
||||
new LexNode('if in_my_tummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
|
||||
[
|
||||
new LexNode('if looks_like_a_mummy',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy')),
|
||||
new LexNode('else',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.ELSE, 4, 2))
|
||||
]
|
||||
),
|
||||
new LexNode('elif in_my_mouth',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.ELIF, 6, 1, 'in_my_mouth'))
|
||||
]
|
||||
),
|
||||
new LexNode('else',
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
new LineToken(Symbol.ELSE, 8, 0)
|
||||
)
|
||||
])
|
||||
}
|
||||
];
|
||||
|
||||
suite('Parser Test Suite', () => {
|
||||
after(() => {
|
||||
vscode.window.showInformationMessage('All tests passed!');
|
||||
});
|
||||
|
||||
for (var t of tests) {
|
||||
let currTest = t; // without this, all test calls get the last test
|
||||
test(currTest.name, () => {
|
||||
let result: LexNode = deparent(new Parser(currTest.input.join('\n')).parse());
|
||||
process.stdout.write(Object.entries(result).toString());
|
||||
|
||||
assert.deepStrictEqual(result, currTest.output);
|
||||
});
|
||||
}
|
||||
});
|
||||
57
src/test/util.ts
Normal file
57
src/test/util.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import LexNode from '../pylex/node';
|
||||
|
||||
/**
|
||||
* TODO: Eliminate need for me.
|
||||
* Recursively deparents a LexNode tree. Needed
|
||||
* because I wasn't able to iterate the circular parent-child
|
||||
* relationship by hand
|
||||
*/
|
||||
function deparent(root: null): null;
|
||||
function deparent(root: LexNode): LexNode;
|
||||
function deparent(root: any): any {
|
||||
if (root === null) {
|
||||
return root;
|
||||
} else {
|
||||
if (root.children() !== null) {
|
||||
return new LexNode(
|
||||
root.label,
|
||||
root.collapsibleState,
|
||||
root.token,
|
||||
root.children()!.map(deparent),
|
||||
);
|
||||
} else {
|
||||
return new LexNode(
|
||||
root.label,
|
||||
root.collapsibleState,
|
||||
root.token,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Roots" a list of lexNodes to match the parser
|
||||
*
|
||||
* Required to properly test the output of the parser,
|
||||
* since the parent child-relationship can't be modeled
|
||||
* exhaustively otherwise
|
||||
*/
|
||||
function root(nodes: LexNode[] | null): LexNode {
|
||||
return new LexNode(
|
||||
"root",
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
null,
|
||||
nodes,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
deparent,
|
||||
root,
|
||||
};
|
||||
Reference in New Issue
Block a user