skeletik

Tiny lexical analyzer

Usage no npm install needed!

<script type="module">
  import skeletik from 'https://cdn.skypack.dev/skeletik';
</script>

README

Skeletik

Tiny lexical analyzer

npm i --save-dev skeletik

WARN: This is just a DRAFT!

Usage

// Tokens
const T_GROUP = 'group';
const T_NUMBER = 'number';
const T_BINARY = 'binary';

// Priority of operations
const PRIORITY = {
    '/': 1,
    '*': 1,
};

// Бинарные опреации
const BINARY = {
    '/': function (a, b) { return a / b },
    '*': function (a, b) { return a * b },
    '+': function (a, b) { return a + b },
    '-': function (a, b) { return a - b },
};

// Create parser
const ast = {};
const parse = skeletik({
    'number': ['0-9'],
    'binary': ['+', '-', '/', '*'],
}, {
    // Inited state
    '': {
        'number'(lex, bone) {
            const chr = lex.getChar();
            const last = bone.last;

            if (last && (last.type === T_NUMBER || !last.prev || last.prev.type === T_BINARY)) {
                last.type = T_NUMBER;
                last.raw += chr;
            } else {
                bone.add(T_NUMBER, chr);
            }
        },

        'binary'(lex, bone) {
            bone.add(T_BINARY, lex.getChar());
        },

        '(': (lex, bone) => bone.add(T_GROUP).last,
        ')': (lex, bone) => bone.parent,
    },
});

function calculator(expr) {
    const root = parse(expr);

    return (function _calc(root) {
        const stack = [];
        const ops = [];

        for (let i = 0; i < root.length; i++) {
            const bone = root.nodes[i];

            if (bone.type === T_BINARY) {
                if (PRIORITY[bone.raw]) {
                    stack[stack.length - 1] = BINARY[bone.raw](stack[stack.length - 1], +bone.next.raw);
                    i++;
                } else {
                    ops.push(bone.raw);
                }
            } else {
                stack.push(bone.type === T_GROUP ? _calc(bone) : +bone.raw);
            }
        }

        let results = stack.pop();

        while (ops.length) {
            results = BINARY[ops.pop()](results, stack.pop());
        }

        return results;
    })(root);
};

const str = '(1 + 2) * 4 / -3 + 1';
console.log('results:', calculator(str), eval(str) === calculator(str));