UNPKG

puzzlescript

Version:

Play PuzzleScript games in your terminal!

444 lines (384 loc) 9.72 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-env jasmine */ const colors_1 = require("../colors"); const parser_1 = __importDefault(require("./parser")); function checkGrammar(code) { // check that it does not throw an Error const ast = parser_1.default.parseToAST(code); expect(ast).toMatchSnapshot(); } function checkParse(code) { return parser_1.default.parse(code); } function checkParseRule(code, varNames) { // Now check if the semantics parsed const legendItems = varNames.map((varName) => { return `${varName} = testObject`; }); return checkParse(` title checkParseRule === OBJECTS === testObject transparent === LEGEND === ${legendItems.join('\n')} === COLLISIONLAYERS === testObject ==== RULES ==== ${code} `); } function parseRule(code, varNames) { // Add a header checkGrammar(` title checkGrammar === RULES === ${code} `); return checkParseRule(code, varNames); } describe('rules', () => { it('parses a simple rule', () => { parseRule('[ z ] -> [ ]', ['z']); }); it('parses a simple rule 2', () => { parseRule('[ z ] -> [ z ]', ['z']); }); it('parses a simple rule without whitespace', () => { parseRule('[z]->[z]', ['z']); }); it('parses a rule with multiple cells', () => { parseRule('[ z | x ] -> [ | ]', ['z', 'x']); }); it('parses a rule with multiple layers', () => { parseRule('[ z x ] -> [ ]', ['z', 'x']); }); it('parses a rule with ellpisis', () => { parseRule('[ z | ... | x | z ] -> [ RANDOM z | ... | x | ]', ['z', 'x']); }); it('parses a rule with a period for a variable name', () => { parseRule('[.] -> []', ['.']); }); it('parses a rule with a _ for a variable name', () => { parseRule('[z_x] -> []', ['z_x']); }); describe('Cell Modifiers', () => { it('parses a rule with directions', () => { parseRule('[ACTION z ] -> [ ]', ['z']); parseRule('[^ z ] -> [ ]', ['z']); parseRule('[v z ] -> [ ]', ['z']); // This needs to be the down arrow, not a variable parseRule('[> z ] -> [ ]', ['z']); parseRule('[< z ] -> [ ]', ['z']); parseRule('[LEFT z ] -> [ ]', ['z']); parseRule('[RIGHT z ] -> [ ]', ['z']); parseRule('[UP z ] -> [ ]', ['z']); parseRule('[DOWN z ] -> [ ]', ['z']); parseRule('[STATIONARY z ] -> [ ]', ['z']); parseRule('[MOVING z ] -> [ ]', ['z']); parseRule('[VERTICAL z ] -> [ ]', ['z']); parseRule('[HORIZONTAL z ] -> [ ]', ['z']); parseRule('[PERPENDICULAR z ] -> [ ]', ['z']); parseRule('[ORTHOGONAL z ] -> [ ]', ['z']); parseRule('[RANDOMDIR z ] -> [ ]', ['z']); }); it('parses a rule with modifiers', () => { parseRule('[ NO z ] -> [ ]', ['z']); }); it('parses a rule with a variable that begins with the name of a modifier', () => { parseRule('[ stationaryz ] -> [ stationaryz ]', ['stationaryz']); }); it('parses a rule with modifiers 2', () => { parseRule('[ STATIONARY z ] -> [ z ]', ['z']); }); }); describe('Commands', () => { it('parses a rule with a command inside the brackets (these should be moved up to the Action Commands)', () => { parseRule('[z]->[SFX0]', ['z']); parseRule('[z]->[z SFX1]', ['z']); parseRule('[z]->[z winter]', ['z', 'winter']); parseRule('[z|] -> [|z AGAIN]', ['z']); }); }); describe('General Formatting', () => { it('parses an almost-empty file', () => { parser_1.default.parseToAST(`title foo`); }); it('parses when there are empty blocks', () => { parser_1.default.parseToAST(` title foo === objects === === legend === === rules === === levels === `); }); it('parses object shortcut characters', () => { parser_1.default.parseToAST(` title foo === OBJECTS === player Z transparent === RULES === [P]->[] === LEVELS === P`); }); it('Correctly parses weird variable names', () => { checkGrammar(` title foo === LEGEND === Bush? = Player . = Player ==== RULES ==== [Bush?] -> [Bush?] [.] -> [.] `); }); it('Looks up color palettes using a string or an index', () => { const { data: data1 } = checkParse(` title foo color_palette gameboycolour === OBJECTS === player yellow `); expect(data1.objects[0].getPixels(1, 1)[0][0].toHex().toLowerCase()).toBe((0, colors_1.lookupColorPalette)('gameboycolour', 'yellow').toLowerCase()); const { data: data2 } = checkParse(` title foo color_palette 2 === OBJECTS === player yellow `); expect(data2.objects[0].getPixels(1, 1)[0][0].toHex().toLowerCase()).toBe((0, colors_1.lookupColorPalette)('gameboycolour', 'yellow').toLowerCase()); }); it('Supports characters that would be invalid in one scope but are valid in another scope', () => { checkParse(` title foo === OBJECTS === hello . transparent hello2 ] transparent hello3 ♡ transparent ======= LEGEND ======= [ = hello , = hello2 д = hello3 sfx11 = hello3 (this is a valid variable name since there are only 10 SFX) ======= COLLISIONLAYERS ======= hello,hello2 hello3 ======= RULES ======= [.]->[.] [♡]->[♡] `); }); it('Supports objects named using only numbers (mirror-isles)', () => { checkParse(` title foo === OBJECTS === Table Yellow Red White (12121 21212 12121 21212 0...0) ..... .121. .212. .121. .0.0. ChairRightLoose Yellow Red `); checkParse(` title foo === OBJECTS === player yellow 00000 00000 00000 00000 00000 00 yellow 01 yellow `); }); }); it('Converts an invalid color to a Transparent one', () => { const { data } = checkParse(` title foo === OBJECTS === player someInvalidColorName === COLLISIONLAYERS === player `); expect(data.objects[0].getPixels(5, 5)[0][0].isTransparent()).toBe(true); // expect(message).toBe('Invalid color name. "someinvalidcolorname" is not a valid color. Using "transparent" instead') // expect(gameNode.__getSourceLineAndColumn()).toBeTruthy() }); it('Sets the collision layer for nested sprites', () => { const { data } = checkParse(` title foo === OBJECTS === player TRANSPARENT === LEGEND === x = player y = x === COLLISIONLAYERS === y `); // just make sure it doesn't throw an exception const player = data._getSpriteByName('player'); expect(player && player.getCollisionLayer().id).toBeGreaterThan(0); }); it('Denotes if a rule is late, rigid, or again', () => { const { data } = checkParse(` title foo === OBJECTS === player someInvalidColorName === COLLISIONLAYERS === player === RULES === LATE [ Player ] -> [] RIGID [ Player ] -> [] [ Player ] -> [] AGAIN LATE RIGID [ Player ] -> [] AGAIN [ Player ] -> [] [ Player ] -> [ Player again] (from "Rose") `); function expector(rule, late, rigid) { expect(rule.getChildRules()[0].isLate()).toBe(late); expect(rule.hasRigid()).toBe(rigid); } expector(data.rules[0], true, false); expector(data.rules[1], false, true); expector(data.rules[2], false, false); expector(data.rules[3], true, true); expector(data.rules[4], false, false); expector(data.rules[5], false, false); }); describe('RANDOM keyword propagation', () => { it('marks a rule as being RANDOM', () => { const { data } = parseRule('RANDOM [.] -> []', ['.']); expect(data.rules[0].isRandom).toBe(true); }); it('marks a rule Group as being RANDOM', () => { const { data } = parseRule(` RANDOM [.] -> [] + [Player] -> []`, ['.', 'Player']); expect(data.rules[0].isRandom).toBe(true); }); it('does not mark a rule Loop as being RANDOM', () => { const { data } = parseRule(` STARTLOOP RANDOM [.] -> [] + [Player] -> [] ENDLOOP `, ['.', 'Player']); expect(data.rules[0].isRandom).toBe(false); }); it('properly groups loops and groups', () => { const { data } = parseRule(` RIGHT [ ] -> [ ] STARTLOOP (1st rulegroup) RIGHT [ ] -> [ ] + RIGHT [ ] -> [ ] + RIGHT [ ] -> [ ] + RIGHT [ ] -> [ ] ( 2nd rulegroup) RIGHT [ ] -> [ ] + RIGHT [ ] -> [ ] RIGHT [ ] -> [ ] ENDLOOP RIGHT [ ] -> [ ] `, ['.', 'Player']); expect(data.rules.length).toBe(3); // startloop expect(data.rules[1].getChildRules().length).toBe(3); // 1st rulegroup expect(data.rules[1].getChildRules()[0].getChildRules().length).toBe(4); // 2nd rulegroup expect(data.rules[1].getChildRules()[1].getChildRules().length).toBe(2); }); }); describe('Expected Failures', () => { // Something using collisionLayers. Like A and B are in the same layer and we try to run either: `[ A B ] -> []` or `[A] -> [A B]` // Check that the magic objects `Background` and `Player` are set to something }); }); //# sourceMappingURL=parser.spec.js.map