recital / core / dist / lib / pipeline / 04-tokens / index.js
index.js
Raw
"use strict";
/**
 * Copyright (c) 2022 Amorphous
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.genTokenizedFragment = exports.genTokensForScene = void 0;
const eol_1 = __importDefault(require("eol"));
const moo_1 = __importDefault(require("moo"));
const pipe_1 = require("../../tools/pipe");
const split_preserve_whitespace_1 = require("../../tools/split-preserve-whitespace");
const softlines_1 = require("./plugins/softlines");
const command_shorthand_1 = require("../../tools/command-shorthand");
const at_shorthand_1 = require("../../tools/at-shorthand");
const expose_meta_1 = require("../../tools/expose-meta");
const genTokensForScene = (scene) => {
    const newFragments = [];
    for (let fragment of scene.fragments) {
        const newFragment = (0, exports.genTokenizedFragment)(fragment);
        newFragments.push(newFragment);
    }
    return Object.assign(Object.assign({}, scene), { fragments: newFragments });
};
exports.genTokensForScene = genTokensForScene;
const genTokenizedFragment = (fragment) => {
    const trimmedLineSections = (0, split_preserve_whitespace_1.createTrimWhitespaceTokens)(fragment.raw);
    const tokens = [];
    for (let section of trimmedLineSections) {
        if (section.type === 'none') {
            const newTokens = createTokensForTextSection(section.text);
            for (let t of newTokens) {
                tokens.push(t);
            }
            continue;
        }
        const newToken = {
            type: 'rawText',
            text: section.text,
            startTag: section.startTag,
            endTag: section.endTag,
        };
        tokens.push(newToken);
    }
    const newFragmentObject = Object.assign(Object.assign({}, fragment), { tokens });
    return newFragmentObject;
};
exports.genTokenizedFragment = genTokenizedFragment;
const createTokensForTextSection = (text) => {
    // first let's preprocess soft lines, so they don't get separated out.
    const preprocessedString = (0, pipe_1.pipe)(text, [softlines_1.preprocessSoftLines]);
    const tokens = [];
    const lexer = moo_1.default.states({
        main: {
            startCommand: { match: /^\$/, next: 'command' },
            startAt: { match: /^@/, next: 'at' },
            startMetaTag: {
                match: /(?<!\n)\n<!/,
                lineBreaks: true,
                push: 'meta',
            },
            emptyLine: { match: /\n/, lineBreaks: true },
            text: { match: /[^]+?/, lineBreaks: true },
        },
        command: {
            endCommand: { match: /\n/, lineBreaks: true, next: 'main' },
            commandText: { match: /[^]+?/, lineBreaks: true },
        },
        meta: {
            endMetaTag: {
                match: />/,
                pop: 1,
            },
            meta: {
                match: /[^]+?/,
                lineBreaks: true,
            },
        },
        at: {
            colonWithWS: { match: /: /, next: 'main' },
            colon: { match: /:/, next: 'main' },
            ws: { match: /[ \t]/, next: 'main' },
            shorthandText: { match: /.+?/ },
        },
    });
    lexer.reset(eol_1.default.lf(preprocessedString));
    let currentBuffer = '';
    let existingToken = null;
    const createParagraphToken = () => {
        if (existingToken) {
            existingToken.text = currentBuffer;
            tokens.push(existingToken);
            existingToken = null;
        }
        else {
            // create a new paragraph token
            const newToken = {
                type: 'paragraph',
                text: currentBuffer,
            };
            tokens.push(newToken);
        }
    };
    for (let token of Array.from(lexer)) {
        switch (token.type) {
            case 'text':
                currentBuffer += token.value;
                break;
            case 'emptyLine':
                if (currentBuffer.length) {
                    createParagraphToken();
                    // reset the buffer
                    currentBuffer = '';
                }
                break;
            // @-shorthand cases
            case 'startAt':
                currentBuffer = '@';
                break;
            case 'shorthandText':
                currentBuffer += token.value;
                break;
            case 'colonWithWS':
            case 'colon':
            case 'ws':
                const meta = (0, at_shorthand_1.separateAtShorthand)(currentBuffer);
                currentBuffer = '';
                existingToken = {
                    type: 'paragraph',
                    meta,
                    text: '',
                };
                (0, expose_meta_1.exposeMeta)(existingToken);
                break;
            // command cases
            case 'startCommand':
                currentBuffer = '$';
                break;
            case 'commandText':
                currentBuffer += token.value;
                break;
            case 'endCommand':
                // create a new command token from current buffer
                const commandToken = (0, command_shorthand_1.createCommandObject)(currentBuffer);
                tokens.push(commandToken);
                currentBuffer = '';
                break;
            // meta cases
            case 'startMetaTag':
                if (currentBuffer.length) {
                    createParagraphToken();
                }
                currentBuffer = '';
                break;
            case 'meta':
                currentBuffer += token.value;
                break;
            case 'endMetaTag':
                if (tokens.length === 0) {
                    throw new Error("Created a post-paragraph meta tag that doesn't modify a paragraph");
                }
                let modifiedToken = tokens[tokens.length - 1];
                if (!modifiedToken.meta) {
                    modifiedToken.meta = {};
                }
                modifiedToken.meta._ = currentBuffer;
                currentBuffer = '';
                break;
        }
    }
    // finish off the last token.
    if (currentBuffer.length) {
        createParagraphToken();
    }
    return tokens;
};