Информация об изменениях

Сообщение Re[15]: Простой скрипт внутри приложения (в виде строки) от 24.11.2021 13:06

Изменено 24.11.2021 13:16 Pauel

Re[15]: Простой скрипт внутри приложения (в виде строки)
Здравствуйте, Sinclair, Вы писали:

S>Эмм. А что там особенно извратного? Пишется всё примерно так же, как и слышится. Стоит один раз увидеть реализацию, как всё становится предельно очевидно. По одной продукции на приоритет:

S>
S>expr = add
S>add = mul '+' mul | mul '-' mul | mul 
S>mul = power '*' power | power '/' power | power '%' power | power
S>power = atom '^' atom | atom
S>atom = number | var | '-' atom | '(' expr ')'
S>number = [0-9]+
S>var = [a-zA-Z][a-zA-Z0-9]*
S>

S>Всё. Вот вам выражения с тремя четырьмя приоритетами, 7 операторов. Хотите, чтобы я дописал семантические действия? И сравним, что проще — Пратт или PEG.

А ты не мог бы выдать этот пег для сравнения?

Накидал recursive descent парсер, только мне пришлось изменить немного грамматику, а то у меня не хотело есть например 2+2+2+2+2. Не уверен, насколько это эквивалентно твоей

/*
expr = add
add = mul (('+'|'-') mul)* 
mul = power (('*'|'/'|'%') power)*
power = atom ('^' atom)*
atom = number | var | '-' atom | '(' expr ')'
number = [0-9]+
var = [a-zA-Z][a-zA-Z0-9]*
*/

const digit = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const letterLower = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
const letterUpper = letterLower.map(x => x.toUpperCase());
const letter = [...letterLower, ...letterUpper];
const letterOrDigit = [...letter, ...digit];

function parse(source) {
    let currentPosition = 0;

    function next(...tokens) {
        const t = peek(...tokens);

        if (t != null) {
            currentPosition += t.length;
            return t;
        }

        return null;
    }

    function peek(...tokens) {
        for (const t of tokens) {
            if (source.startsWith(t, currentPosition)) {
                return t;
            }
        }
        return null;
    }

    function revert(toPosition) {
        currentPosition = toPosition;
    }

    function expr() {
        return add();
    }

    function add() {
        let left = mul();

        while (true) {
            let operator = next('+', '-');
            if (operator == null) {
                break;
            }
            const right = mul();
            left = {type: 'add', operator, left, right};
        }

        return left;
    }

    function mul() {
        let left = power();

        while (true) {
            let operator = next('*', '/', '%');
            if (operator == null) {
                break;
            }
            const right = power();
            left = {type: 'mul', operator, left, right};
        }

        return left;
    }

    function power() {
        let left = atom();

        while (true) {
            let operator = next('^');
            if (operator == null) {
                break;
            }
            const right = atom();
            left = {type: 'power', operator, left, right};
        }

        return left;
    }

    function atom() {
        return number() || variable() || negation() || grouping();
    }

    function grouping() {
        const savePosition = currentPosition;

        if (peek('(') == null) {
            return null;
        }
        next('(');
        const e = expr();
        if (e == null) {
            revert(savePosition);
            return null;
        }
        if (next(')') == null) {
            revert(savePosition);
        }
        return e;
    }

    function negation() {
        if (peek('-')) {
            next('-');

            const right = atom();

            return {type: 'negation', right};
        }

        return null;
    }

    function variable() {
        const input = [];

        const start = next(...letter);

        if (start == null) {
            return null;
        }

        input.push(start);

        while (true) {
            const x = next(...letterOrDigit);

            if (x == null) {
                break;
            }

            input.push(x);
        }

        return input.length === 0 ? null : {type: 'variable', name: input.join('')};
    }

    function number() {
        const input = [];

        while (true) {
            const d = next(...digit);

            if (d == null) {
                break;
            }

            input.push(d);
        }

        return input.length === 0 ? null : {type: 'number', value: input.join('')};
    }

    return expr();
}

console.log(JSON.stringify(parse(process.argv[2]), null, 4));
Re[15]: Простой скрипт внутри приложения (в виде строки)
Здравствуйте, Sinclair, Вы писали:

S>Эмм. А что там особенно извратного? Пишется всё примерно так же, как и слышится. Стоит один раз увидеть реализацию, как всё становится предельно очевидно. По одной продукции на приоритет:

S>
S>expr = add
S>add = mul '+' mul | mul '-' mul | mul 
S>mul = power '*' power | power '/' power | power '%' power | power
S>power = atom '^' atom | atom
S>atom = number | var | '-' atom | '(' expr ')'
S>number = [0-9]+
S>var = [a-zA-Z][a-zA-Z0-9]*
S>

S>Всё. Вот вам выражения с тремя четырьмя приоритетами, 7 операторов. Хотите, чтобы я дописал семантические действия? И сравним, что проще — Пратт или PEG.

А ты не мог бы выдать этот пег для сравнения?

Накидал recursive descent парсер, только мне пришлось изменить немного грамматику, а то у меня не хотело есть например 2+2+2+2+2. Не уверен, насколько это эквивалентно твоей

/*
expr = add
add = mul (('+'|'-') mul)* 
mul = power (('*'|'/'|'%') power)*
power = atom ('^' atom)*
atom = number | var | '-' atom | '(' expr ')'
number = [0-9]+
var = [a-zA-Z][a-zA-Z0-9]*
*/

const digit = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const letterLower = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
const letterUpper = letterLower.map(x => x.toUpperCase());
const letter = [...letterLower, ...letterUpper];
const letterOrDigit = [...letter, ...digit];

function parse(source) {
    let currentPosition = 0;

    function next(...tokens) {
        const t = peek(...tokens);

        if (t != null) {
            currentPosition += t.length;
            return t;
        }

        return null;
    }

    function peek(...tokens) {
        for (const t of tokens) {
            if (source.startsWith(t, currentPosition)) {
                return t;
            }
        }
        return null;
    }

    function revert(toPosition) {
        currentPosition = toPosition;
    }

    function expr() {
        return add();
    }

    function add() {
        const savePosition = currentPosition;
        let left = mul();

        while (true) {
            let operator = next('+', '-');
            if (operator == null) {
                break;
            }
            const right = mul();
            if (right == null) {
                revert(savePosition);
                return null;
            }
            left = {type: 'add', operator, left, right};
        }

        return left;
    }

    function mul() {
        const savePosition = currentPosition;
        let left = power();

        while (true) {
            let operator = next('*', '/', '%');
            if (operator == null) {
                break;
            }
            const right = power();
            if (right == null) {
                revert(savePosition);
                return null;
            }
            left = {type: 'mul', operator, left, right};
        }

        return left;
    }

    function power() {
        const savePosition = currentPosition;
        let left = atom();

        while (true) {
            let operator = next('^');
            if (operator == null) {
                break;
            }
            const right = atom();
            if (right == null) {
                revert(savePosition);
                return null;
            }
            left = {type: 'power', operator, left, right};
        }

        return left;
    }

    function atom() {
        return number() || variable() || negation() || grouping();
    }

    function grouping() {
        const savePosition = currentPosition;

        if (peek('(') == null) {
            return null;
        }
        next('(');
        const e = expr();
        if (e == null) {
            revert(savePosition);
            return null;
        }
        if (next(')') == null) {
            revert(savePosition);
        }
        return e;
    }

    function negation() {
        if (peek('-')) {
            next('-');

            const right = atom();

            return {type: 'negation', right};
        }

        return null;
    }

    function variable() {
        const input = [];

        const start = next(...letter);

        if (start == null) {
            return null;
        }

        input.push(start);

        while (true) {
            const x = next(...letterOrDigit);

            if (x == null) {
                break;
            }

            input.push(x);
        }

        return input.length === 0 ? null : {type: 'variable', name: input.join('')};
    }

    function number() {
        const input = [];

        while (true) {
            const d = next(...digit);

            if (d == null) {
                break;
            }

            input.push(d);
        }

        return input.length === 0 ? null : {type: 'number', value: input.join('')};
    }

    return expr();
}

console.log(JSON.stringify(parse(process.argv[2]), null, 4));