mirror of
https://github.com/IvarK/AntimatterDimensionsSourceCode.git
synced 2024-11-10 06:02:13 +00:00
Untangle the automator
This commit is contained in:
parent
897364950d
commit
740d807373
@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import { blockifyTextAutomator } from "@/core/automator";
|
||||
import ModalWrapper from "@/components/modals/ModalWrapper";
|
||||
|
||||
export default {
|
||||
@ -124,7 +125,7 @@ export default {
|
||||
if (this.isBlock) {
|
||||
const newTemplateBlock = {
|
||||
name: `Template: ${this.name}`,
|
||||
blocks: AutomatorGrammar.blockifyTextAutomator(this.templateScript.script).blocks
|
||||
blocks: blockifyTextAutomator(this.templateScript.script).blocks
|
||||
};
|
||||
AutomatorData.blockTemplates.push(newTemplateBlock);
|
||||
GameUI.notify.info("Custom template block created");
|
||||
@ -227,4 +228,4 @@ export default {
|
||||
.o-load-preset-button-margin {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { AutomatorBackend } from "@/core/globals";
|
||||
import { hasCompilationErrors } from "@/core/automator";
|
||||
|
||||
import ModalWrapperChoice from "@/components/modals/ModalWrapperChoice";
|
||||
|
||||
@ -108,7 +108,7 @@ export default {
|
||||
this.importedPresets = parsed.presets;
|
||||
this.importedConstants = parsed.constants;
|
||||
this.lineCount = this.scriptContent.split("\n").length;
|
||||
this.hasErrors = AutomatorGrammar.compile(this.scriptContent).errors.length !== 0;
|
||||
this.hasErrors = hasCompilationErrors(this.scriptContent);
|
||||
this.isValid = true;
|
||||
},
|
||||
importSave() {
|
||||
|
@ -2,6 +2,7 @@
|
||||
import draggable from "vuedraggable";
|
||||
|
||||
import AutomatorBlockSingleRow from "./AutomatorBlockSingleRow";
|
||||
import { blockifyTextAutomator } from "@/core/automator";
|
||||
|
||||
export default {
|
||||
name: "AutomatorBlockEditor",
|
||||
@ -104,13 +105,13 @@ export const BlockAutomator = {
|
||||
},
|
||||
|
||||
updateEditor(scriptText) {
|
||||
const lines = AutomatorGrammar.blockifyTextAutomator(scriptText).blocks;
|
||||
const lines = blockifyTextAutomator(scriptText).blocks;
|
||||
this.lines = lines;
|
||||
return lines;
|
||||
},
|
||||
|
||||
hasUnparsableCommands(text) {
|
||||
const blockified = AutomatorGrammar.blockifyTextAutomator(text);
|
||||
const blockified = blockifyTextAutomator(text);
|
||||
return blockified.validatedBlocks !== blockified.visitedBlocks;
|
||||
},
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script>
|
||||
import { validateLine } from "@/core/automator";
|
||||
|
||||
export default {
|
||||
name: "AutomatorBlockSingleInput",
|
||||
props: {
|
||||
@ -194,10 +196,10 @@ export default {
|
||||
const clone = Object.assign({}, this.b);
|
||||
clone.nest = [];
|
||||
lines = BlockAutomator.parseLines([clone]);
|
||||
validator = AutomatorGrammar.validateLine(lines.join("\n"));
|
||||
validator = validateLine(lines.join("\n"));
|
||||
} else {
|
||||
lines = BlockAutomator.parseLines([this.b]);
|
||||
validator = AutomatorGrammar.validateLine(lines[0]);
|
||||
validator = validateLine(lines[0]);
|
||||
}
|
||||
|
||||
// Yes, the odd structure of this check is intentional. Something odd happens within parseLines under certain
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script>
|
||||
import { forbiddenConstantPatterns } from "@/core/automator";
|
||||
|
||||
export default {
|
||||
name: "AutomatorDefineSingleEntry",
|
||||
props: {
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import { AUTOMATOR_TYPE } from "@/core/automator/automator-backend";
|
||||
import AutomatorBlocks from "./AutomatorBlocks";
|
||||
import AutomatorButton from "./AutomatorButton";
|
||||
import AutomatorDataTransferPage from "./AutomatorDataTransferPage";
|
||||
@ -121,8 +122,8 @@ export default {
|
||||
created() {
|
||||
this.on$(GAME_EVENT.GAME_LOAD, () => this.onGameLoad());
|
||||
this.on$(GAME_EVENT.AUTOMATOR_SAVE_CHANGED, () => this.onGameLoad());
|
||||
this.updateCurrentScriptID();
|
||||
this.updateScriptList();
|
||||
this.on$(GAME_EVENT.AUTOMATOR_TYPE_CHANGED, () => this.openMatchingAutomatorTypeDocs());
|
||||
this.onGameLoad();
|
||||
},
|
||||
destroyed() {
|
||||
this.fullScreen = false;
|
||||
@ -154,6 +155,7 @@ export default {
|
||||
onGameLoad() {
|
||||
this.updateCurrentScriptID();
|
||||
this.updateScriptList();
|
||||
this.fixAutomatorTypeDocs();
|
||||
},
|
||||
updateScriptList() {
|
||||
this.scripts = Object.values(player.reality.automator.scripts).map(script => ({
|
||||
@ -185,6 +187,21 @@ export default {
|
||||
if (!this.isBlock && AutomatorTextUI.editor) AutomatorTextUI.editor.performLint();
|
||||
});
|
||||
},
|
||||
fixAutomatorTypeDocs() {
|
||||
const automator = player.reality.automator;
|
||||
if (automator.currentInfoPane === AutomatorPanels.COMMANDS && automator.type === AUTOMATOR_TYPE.BLOCK) {
|
||||
this.openMatchingAutomatorTypeDocs();
|
||||
}
|
||||
if (automator.currentInfoPane === AutomatorPanels.BLOCKS && automator.type === AUTOMATOR_TYPE.TEXT) {
|
||||
this.openMatchingAutomatorTypeDocs();
|
||||
}
|
||||
},
|
||||
openMatchingAutomatorTypeDocs() {
|
||||
const automator = player.reality.automator;
|
||||
automator.currentInfoPane = automator.type === AUTOMATOR_TYPE.BLOCK
|
||||
? AutomatorPanels.BLOCKS
|
||||
: AutomatorPanels.COMMANDS;
|
||||
},
|
||||
rename() {
|
||||
this.editingName = true;
|
||||
this.$nextTick(() => {
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script>
|
||||
import { blockifyTextAutomator } from "@/core/automator";
|
||||
|
||||
export default {
|
||||
name: "AutomatorModeSwitch",
|
||||
data() {
|
||||
@ -68,7 +70,7 @@ export default {
|
||||
(BlockAutomator.hasUnparsableCommands(currScript) || AutomatorData.currentErrors().length !== 0);
|
||||
|
||||
if (player.options.confirmations.switchAutomatorMode && (hasTextErrors || AutomatorBackend.isRunning)) {
|
||||
const blockified = AutomatorGrammar.blockifyTextAutomator(currScript);
|
||||
const blockified = blockifyTextAutomator(currScript);
|
||||
|
||||
// We explicitly pass in 0 for lostBlocks if converting from block to text since nothing is ever lost in that
|
||||
// conversion direction
|
||||
|
@ -1,21 +1,4 @@
|
||||
import { AutomatorPanels } from "@/components/tabs/automator/AutomatorDocs";
|
||||
|
||||
/** @abstract */
|
||||
class AutomatorCommandInterface {
|
||||
constructor(id) {
|
||||
AutomatorCommandInterface.all[id] = this;
|
||||
}
|
||||
|
||||
/** @abstract */
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
run(command) { throw new NotImplementedError(); }
|
||||
}
|
||||
|
||||
AutomatorCommandInterface.all = [];
|
||||
|
||||
export function AutomatorCommand(id) {
|
||||
return AutomatorCommandInterface.all[id];
|
||||
}
|
||||
import { compile } from "./compiler";
|
||||
|
||||
export const AUTOMATOR_COMMAND_STATUS = Object.freeze({
|
||||
NEXT_INSTRUCTION: 0,
|
||||
@ -150,7 +133,7 @@ export class AutomatorScript {
|
||||
}
|
||||
|
||||
compile() {
|
||||
this._compiled = AutomatorGrammar.compile(this.text).compiled;
|
||||
this._compiled = compile(this.text).compiled;
|
||||
}
|
||||
|
||||
static create(name, content = "") {
|
||||
@ -215,7 +198,7 @@ export const AutomatorData = {
|
||||
},
|
||||
recalculateErrors() {
|
||||
const toCheck = this.currentScriptText();
|
||||
this.cachedErrors = AutomatorGrammar.compile(toCheck).errors;
|
||||
this.cachedErrors = compile(toCheck).errors;
|
||||
this.cachedErrors.sort((a, b) => a.startLine - b.startLine);
|
||||
},
|
||||
currentErrors() {
|
||||
@ -1012,18 +995,15 @@ export const AutomatorBackend = {
|
||||
// This saves the script after converting it.
|
||||
BlockAutomator.parseTextFromBlocks();
|
||||
player.reality.automator.type = AUTOMATOR_TYPE.TEXT;
|
||||
if (player.reality.automator.currentInfoPane === AutomatorPanels.BLOCKS) {
|
||||
player.reality.automator.currentInfoPane = AutomatorPanels.COMMANDS;
|
||||
}
|
||||
} else {
|
||||
const toConvert = AutomatorTextUI.editor.getDoc().getValue();
|
||||
// Needs to be called to update the lines prop in the BlockAutomator object
|
||||
BlockAutomator.updateEditor(toConvert);
|
||||
AutomatorBackend.saveScript(scriptID, toConvert);
|
||||
player.reality.automator.type = AUTOMATOR_TYPE.BLOCK;
|
||||
player.reality.automator.currentInfoPane = AutomatorPanels.BLOCKS;
|
||||
}
|
||||
AutomatorHighlighter.clearAllHighlightedLines();
|
||||
EventHub.ui.dispatch(GAME_EVENT.AUTOMATOR_TYPE_CHANGED);
|
||||
},
|
||||
|
||||
clearEditor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AutomatorGrammar } from "./parser";
|
||||
import { AutomatorLexer } from "./lexer";
|
||||
|
||||
import { lexer, tokenIds } from "./lexer";
|
||||
import { compile } from "./compiler";
|
||||
import { parser } from "./parser";
|
||||
|
||||
function walkSuggestion(suggestion, prefix, output) {
|
||||
const hasAutocomplete = suggestion.$autocomplete &&
|
||||
@ -8,14 +8,14 @@ function walkSuggestion(suggestion, prefix, output) {
|
||||
const isUnlocked = suggestion.$unlocked ? suggestion.$unlocked() : true;
|
||||
if (hasAutocomplete && isUnlocked) output.add(suggestion.$autocomplete);
|
||||
for (const s of suggestion.categoryMatches) {
|
||||
walkSuggestion(AutomatorLexer.tokenIds[s], prefix, output);
|
||||
walkSuggestion(tokenIds[s], prefix, output);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
CodeMirror.registerHelper("lint", "automato", (contents, _, editor) => {
|
||||
const doc = editor.getDoc();
|
||||
const errors = AutomatorGrammar.compile(contents, true).errors;
|
||||
const errors = compile(contents, true).errors;
|
||||
return errors.map(e => ({
|
||||
message: e.info,
|
||||
severity: "error",
|
||||
@ -32,9 +32,9 @@ CodeMirror.registerHelper("hint", "anyword", editor => {
|
||||
while (start && /\w/u.test(line.charAt(start - 1)))--start;
|
||||
const lineStart = line.slice(0, start);
|
||||
const currentPrefix = line.slice(start, end);
|
||||
const lineLex = AutomatorLexer.lexer.tokenize(lineStart);
|
||||
const lineLex = lexer.tokenize(lineStart);
|
||||
if (lineLex.errors.length > 0) return undefined;
|
||||
const rawSuggestions = AutomatorGrammar.parser.computeContentAssist("command", lineLex.tokens);
|
||||
const rawSuggestions = parser.computeContentAssist("command", lineLex.tokens);
|
||||
const suggestions = new Set();
|
||||
for (const s of rawSuggestions) {
|
||||
if (s.ruleStack[1] === "badCommand") continue;
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { AutomatorLexer } from "./lexer";
|
||||
import { standardizeAutomatorValues, tokenMap as T } from "./lexer";
|
||||
|
||||
/**
|
||||
* Note: the $ shorthand for the parser object is required by Chevrotain. Don't mess with it.
|
||||
*/
|
||||
|
||||
const T = AutomatorLexer.tokenMap;
|
||||
const presetSplitter = /name[ \t]+(.+$)/ui;
|
||||
const idSplitter = /id[ \t]+(\d)/ui;
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { lexer, tokenMap as T } from "./lexer";
|
||||
import { AutomatorCommands } from "./automator-commands";
|
||||
import { AutomatorGrammar } from "./parser";
|
||||
import { AutomatorLexer } from "./lexer";
|
||||
import { parser } from "./parser";
|
||||
|
||||
const parser = AutomatorGrammar.parser;
|
||||
const BaseVisitor = parser.getBaseCstVisitorConstructorWithDefaults();
|
||||
|
||||
class Validator extends BaseVisitor {
|
||||
@ -20,7 +19,7 @@ class Validator extends BaseVisitor {
|
||||
};
|
||||
}
|
||||
|
||||
const lexResult = AutomatorLexer.lexer.tokenize(rawText);
|
||||
const lexResult = lexer.tokenize(rawText);
|
||||
const tokens = lexResult.tokens;
|
||||
parser.input = tokens;
|
||||
this.parseResult = parser.script();
|
||||
@ -362,7 +361,6 @@ class Validator extends BaseVisitor {
|
||||
this.addError(ctx, "Missing comparison operator (<, >, <=, >=)", "Insert the appropriate comparison operator");
|
||||
return;
|
||||
}
|
||||
const T = AutomatorLexer.tokenMap;
|
||||
if (ctx.ComparisonOperator[0].tokenType === T.OpEQ || ctx.ComparisonOperator[0].tokenType === T.EqualSign) {
|
||||
this.addError(ctx, "Please use an inequality comparison (>, <, >=, <=)",
|
||||
"Comparisons cannot be done with equality, only with inequality operators");
|
||||
@ -529,7 +527,7 @@ class Blockifier extends BaseVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
function compile(input, validateOnly = false) {
|
||||
export function compile(input, validateOnly = false) {
|
||||
// The lexer and codemirror choke on the last line of the script, so we pad it with an invisible newline
|
||||
const script = `${input}\n `;
|
||||
const validator = new Validator(script);
|
||||
@ -542,9 +540,12 @@ function compile(input, validateOnly = false) {
|
||||
compiled,
|
||||
};
|
||||
}
|
||||
AutomatorGrammar.compile = compile;
|
||||
|
||||
function blockifyTextAutomator(input) {
|
||||
export function hasCompilationErrors(input) {
|
||||
return compile(input, true).errors.length !== 0;
|
||||
}
|
||||
|
||||
export function blockifyTextAutomator(input) {
|
||||
const validator = new Validator(input);
|
||||
const blockifier = new Blockifier();
|
||||
const blocks = blockifier.visit(validator.parseResult);
|
||||
@ -584,11 +585,8 @@ function blockifyTextAutomator(input) {
|
||||
|
||||
return { blocks, validatedBlocks, visitedBlocks };
|
||||
}
|
||||
AutomatorGrammar.blockifyTextAutomator = blockifyTextAutomator;
|
||||
|
||||
function validateLine(input) {
|
||||
export function validateLine(input) {
|
||||
const validator = new Validator(input);
|
||||
return validator;
|
||||
}
|
||||
|
||||
AutomatorGrammar.validateLine = validateLine;
|
||||
|
@ -1,5 +1,14 @@
|
||||
import "./compiler";
|
||||
import "./automator-codemirror";
|
||||
|
||||
export { AutomatorGrammar } from "./parser";
|
||||
export { forbiddenConstantPatterns, standardizeAutomatorValues } from "./lexer";
|
||||
export {
|
||||
forbiddenConstantPatterns
|
||||
} from "./lexer";
|
||||
|
||||
export {
|
||||
blockifyTextAutomator,
|
||||
hasCompilationErrors,
|
||||
validateLine
|
||||
} from "./compiler";
|
||||
|
||||
export * from "./automator-backend";
|
||||
export * from "./automator-points";
|
||||
|
@ -342,7 +342,7 @@ const Pipe = createToken({ name: "Pipe", pattern: /\|/, label: "|" });
|
||||
const Dash = createToken({ name: "Dash", pattern: /-/, label: "-" });
|
||||
|
||||
// The order here is the order the lexer looks for tokens in.
|
||||
const automatorTokens = [
|
||||
export const automatorTokens = [
|
||||
HSpace, StringLiteral, StringLiteralSingleQuote, Comment, EOL,
|
||||
ComparisonOperator, ...tokenLists.ComparisonOperator,
|
||||
LCurly, RCurly, Comma, EqualSign, Pipe, Dash,
|
||||
@ -362,19 +362,19 @@ RCurly.LABEL = "'}'";
|
||||
NumberLiteral.LABEL = "Number";
|
||||
Comma.LABEL = "❟";
|
||||
|
||||
const lexer = new Lexer(automatorTokens, {
|
||||
export const lexer = new Lexer(automatorTokens, {
|
||||
positionTracking: "full",
|
||||
ensureOptimizations: true
|
||||
});
|
||||
|
||||
// The lexer uses an ID system that's separate from indices into the token array
|
||||
const tokenIds = [];
|
||||
export const tokenIds = [];
|
||||
for (const token of lexer.lexerDefinition) {
|
||||
tokenIds[token.tokenTypeIdx] = token;
|
||||
}
|
||||
|
||||
// We use this while building up the grammar
|
||||
const tokenMap = automatorTokens.mapToObject(e => e.name, e => e);
|
||||
export const tokenMap = automatorTokens.mapToObject(e => e.name, e => e);
|
||||
|
||||
const automatorCurrencyNames = tokenLists.AutomatorCurrency.map(i => i.$autocomplete.toUpperCase());
|
||||
|
||||
@ -405,12 +405,3 @@ export const forbiddenConstantPatterns = lexer.lexerDefinition
|
||||
.filter(p => !ignoredPatterns.includes(p.name))
|
||||
.map(p => p.PATTERN.source)
|
||||
.flatMap(p => ((p.includes("(") || p.includes(")")) ? p : p.split("[ \\t]+")));
|
||||
|
||||
export const AutomatorLexer = {
|
||||
lexer,
|
||||
tokens: automatorTokens,
|
||||
tokenIds,
|
||||
tokenMap,
|
||||
standardizeAutomatorValues,
|
||||
forbiddenConstantPatterns,
|
||||
};
|
||||
|
@ -1,14 +1,12 @@
|
||||
import { EOF, Parser } from "chevrotain";
|
||||
|
||||
import { automatorTokens, tokenMap as T } from "./lexer";
|
||||
import { AutomatorCommands } from "./automator-commands";
|
||||
import { AutomatorLexer } from "./lexer";
|
||||
|
||||
const T = AutomatorLexer.tokenMap;
|
||||
|
||||
// ----------------- parser -----------------
|
||||
class AutomatorParser extends Parser {
|
||||
constructor() {
|
||||
super(AutomatorLexer.tokens, {
|
||||
super(automatorTokens, {
|
||||
recoveryEnabled: true,
|
||||
outputCst: true,
|
||||
nodeLocationTracking: "full",
|
||||
@ -130,10 +128,4 @@ class AutomatorParser extends Parser {
|
||||
}
|
||||
}
|
||||
|
||||
const parser = new AutomatorParser();
|
||||
|
||||
export const AutomatorGrammar = {
|
||||
parser,
|
||||
// This field is filled in by automator-validate.js
|
||||
validate: null,
|
||||
};
|
||||
export const parser = new AutomatorParser();
|
||||
|
@ -100,6 +100,7 @@ window.GAME_EVENT = {
|
||||
OFFLINE_CURRENCY_GAINED: "OFFLINE_CURRENCY_GAINED",
|
||||
SAVE_CONVERTED_FROM_PREVIOUS_VERSION: "SAVE_CONVERTED_FROM_PREVIOUS_VERSION",
|
||||
REALITY_FIRST_UNLOCKED: "REALITY_FIRST_UNLOCKED",
|
||||
AUTOMATOR_TYPE_CHANGED: "AUTOMATOR_TYPE_CHANGED",
|
||||
AUTOMATOR_SAVE_CHANGED: "AUTOMATOR_SAVE_CHANGED",
|
||||
AUTOMATOR_CONSTANT_CHANGED: "AUTOMATOR_CONSTANT_CHANGED",
|
||||
PELLE_STRIKE_UNLOCKED: "PELLE_STRIKE_UNLOCKED",
|
||||
|
@ -1,7 +1,6 @@
|
||||
export * from "./glyph-effects";
|
||||
export * from "./player";
|
||||
|
||||
export * from "./automator/automator-backend";
|
||||
export * from "./performance-stats";
|
||||
export * from "./currency";
|
||||
export * from "./cache";
|
||||
@ -38,7 +37,6 @@ export * from "./celestials/pelle/game-end";
|
||||
export * from "./celestials/celestials";
|
||||
|
||||
export * from "./automator";
|
||||
export * from "./automator/automator-points";
|
||||
|
||||
export * from "./player-progress";
|
||||
export * from "./modal";
|
||||
|
Loading…
Reference in New Issue
Block a user