fix typing and generation-based matching

This commit is contained in:
xeals 2023-04-27 10:38:19 +10:00
parent 6d92cb4deb
commit 70141b162f
Signed by: xeals
GPG Key ID: A498C7AF27EC6B5C

67
calc.js
View File

@ -1,12 +1,13 @@
import calc, { import {
calculate, calculate,
Generations,
Field, Field,
Generations,
Move, Move,
Pokemon, Pokemon,
Result,
} from "@ajhyndman/smogon-calc"; } from "@ajhyndman/smogon-calc";
import assert from "assert"; import assert from "assert";
import chev, { Lexer } from "chevrotain"; import { createToken, Lexer } from "chevrotain";
const gen = Generations.get(9); const gen = Generations.get(9);
@ -16,65 +17,61 @@ const gen = Generations.get(9);
* @returns {Lexer} * @returns {Lexer}
*/ */
function buildLexer() { function buildLexer() {
const Boost = chev.createToken({ name: "Boost", pattern: /[+-]\d+/ }); const Boost = createToken({ name: "Boost", pattern: /[+-]\d+/ });
const EV = chev.createToken({ name: "EV", pattern: /\d+[+-]?/ }); const EV = createToken({ name: "EV", pattern: /\d+[+-]?/ });
const Stat = chev.createToken({ const Stat = createToken({
name: "Stat", name: "Stat",
pattern: /HP|Atk|Def|SpA|SpD|Spe/, pattern: /HP|Atk|Def|SpA|SpD|Spe/,
}); });
const Item = chev.createToken({ const Item = createToken({
name: "Item", name: "Item",
pattern: new RegExp(calc.ITEMS.flatMap((gen) => gen).join("|")), pattern: new RegExp([...gen.items].map((i) => i.name).join("|")),
}); });
const Ability = chev.createToken({ const Ability = createToken({
name: "Ability", name: "Ability",
pattern: new RegExp(calc.ABILITIES.flatMap((gen) => gen).join("|")), pattern: new RegExp([...gen.abilities].map((a) => a.name).join("|")),
}); });
const Pokemon = chev.createToken({ const Pokemon = createToken({
name: "Pokemon", name: "Pokemon",
pattern: new RegExp( pattern: new RegExp([...gen.species].map((s) => s.name).join("|")),
calc.SPECIES.flatMap((gen) => Object.keys(gen)).join("|")
),
}); });
const Move = chev.createToken({ const Move = createToken({
name: "Move", name: "Move",
pattern: new RegExp( pattern: new RegExp([...gen.moves].map((m) => m.name).join("|")),
calc.MOVES.flatMap((gen) => Object.keys(gen)).join("|")
),
}); });
const whitespace = chev.createToken({ const whitespace = createToken({
name: "Whitespace", name: "Whitespace",
pattern: /\s+/, pattern: /\s+/,
group: Lexer.SKIPPED, group: Lexer.SKIPPED,
}); });
const div = chev.createToken({ const div = createToken({
name: "div", name: "div",
pattern: "/", pattern: "/",
group: Lexer.SKIPPED, group: Lexer.SKIPPED,
}); });
const vs = chev.createToken({ const vs = createToken({
name: "vs", name: "vs",
pattern: /vs\.?/, pattern: /vs\.?/,
}); });
const terrainEnter = chev.createToken({ const terrainEnter = createToken({
name: "terrainEnter", name: "terrainEnter",
pattern: "in", pattern: "in",
push_mode: "terrain_mode", push_mode: "terrain_mode",
}); });
const Weather = chev.createToken({ const Weather = createToken({
name: "Weather", name: "Weather",
pattern: /Sun|Rain|Sand|Snow/, pattern: /Sun|Rain|Sand|Snow/,
}); });
const Terrain = chev.createToken({ const Terrain = createToken({
name: "Terrain", name: "Terrain",
pattern: /(Electric|Grassy|Misty|Psychic) Terrain/, pattern: /(Electric|Grassy|Misty|Psychic) Terrain/,
}); });
const screenEnter = chev.createToken({ const screenEnter = createToken({
name: "screenEnter", name: "screenEnter",
pattern: "through", pattern: "through",
push_mode: "screen_mode", push_mode: "screen_mode",
@ -117,7 +114,7 @@ function* iterate(arr) {
/** /**
* @param {string} type token type * @param {string} type token type
* @param {chev.IToken} token token * @param {import("chevrotain").IToken} token token
* @return {string} matched token * @return {string} matched token
*/ */
function unwrapToken(type, token) { function unwrapToken(type, token) {
@ -155,7 +152,7 @@ const NEG_NATURES = {
* through Light Screen * through Light Screen
* *
* @param {string} line textual line * @param {string} line textual line
* @return {calc.Result} calculation result * @return {Result} calculation result
*/ */
function parseAndCalculate(line) { function parseAndCalculate(line) {
const lexer = buildLexer(); const lexer = buildLexer();
@ -169,14 +166,14 @@ function parseAndCalculate(line) {
/** @type {string} */ /** @type {string} */
var defender; var defender;
/** @type {calc.State.Pokemon} */ /** @type {import("@ajhyndman/smogon-calc").State.Pokemon} */
var attackerOpts = {}; var attackerOpts = {};
/** @type {calc.State.Pokemon} */ /** @type {import("@ajhyndman/smogon-calc").State.Pokemon} */
var defenderOpts = {}; var defenderOpts = {};
/** @type {string} */ /** @type {string} */
var move; var move;
/** @type {calc.State.Field} */ /** @type {import("@ajhyndman/smogon-calc").State.Field} */
var field = {}; var field = {};
// Tokenising state. // Tokenising state.
@ -216,7 +213,7 @@ function parseAndCalculate(line) {
} }
break; break;
case "Stat": case "Stat":
throw Error("Impossibel state: bare Stat"); throw Error("Impossible state: bare Stat");
case "Item": case "Item":
opts().item = unwrapToken("Item", item); opts().item = unwrapToken("Item", item);
break; break;
@ -269,9 +266,11 @@ function parseAndCalculate(line) {
} }
// Pre-checking before the calculator throws unreadable errors. // Pre-checking before the calculator throws unreadable errors.
if (!gen.species.get(attacker)) throw Error(`No species ${attacker}`); if (!gen.species.get(attacker.toLowerCase()))
if (!gen.species.get(defender)) throw Error(`No species ${attacker}`); throw Error(`No species named ${attacker}`);
if (!gen.moves.get(move)) throw Error(`No move ${move}`); if (!gen.species.get(defender.toLowerCase()))
throw Error(`No species named ${defender}`);
if (!gen.moves.get(move.toLowerCase())) throw Error(`No move named ${move}`);
return calculate( return calculate(
gen, gen,