<commit_before>// ################################################################################################################# //
//
// Grammar
//
// ################################################################################################################# //

Yate.Grammar = {};

// ----------------------------------------------------------------------------------------------------------------- //

// TOKENS

Yate.Grammar.tokens = {
    QNAME: /^[a-zA-Z_][a-zA-Z0-9_-]*/,
    ESC: /^["'\\nt]/,
    NUMBER: /^[0-9]+(\.[0-9]+)?/,
    '/': /^\/(?!\/)/,
    '|': /^\|(?!\|)/,
    '=': /^=(?!=)/,
    '{': /^{(?!{)/,
    '}': /^}(?!})/
};

// ----------------------------------------------------------------------------------------------------------------- //

// KEYWORDS

Yate.Grammar.keywords = [ 'match', 'function', 'for', 'if', 'else', 'apply', 'key', 'nodeset', 'boolean', 'scalar' ];

// ----------------------------------------------------------------------------------------------------------------- //

// RULES

Yate.Grammar.rules = {};

// ----------------------------------------------------------------------------------------------------------------- //
// Blocks
// ----------------------------------------------------------------------------------------------------------------- //

// stylesheet = block

Yate.Grammar.rules.stylesheet = {

    rule: function(ast) {
        ast.$body = this.match('block');

        if (!this.isEOF()) {
            this.error('EOF expected');
        }

    },

    options: {
        skipper: 'default_'
    }

};

// ----------------------------------------------------------------------------------------------------------------- //

// block = ( template | function_ | key | var_ | blockExpr )*

Yate.Grammar.rules.block = function(ast) {
    while (!( this.isEOF() || this.testAny([ '}', ')' ]) )) { // Блок верхнего уровня (stylesheet) заканчивается с концом файла.
                                                              // Вложенные блоки заканчиваются закрывающей скобкой '}' или ')'.
        if (this.isEOL()) { // Пропускаем пустые строки.
            this.eol();
            continue;
        }

        if (this.test('MATCH')) {
            ast.$templates.add( this.match('template') );
        } else if (this.test('FUNCTION')) {
            ast.$defs.add( this.match('function_') );
        } else if (this.test('KEY')) {
            ast.$defs.add( this.match('key') );
        } else if (this.testAll([ 'QNAME', '=' ])) {
            ast.$defs.add( this.match('var_') );
        } else {
            ast.$exprs.add( this.match('blockExpr') );
        }

        if (!this.isEOL()) { // Если после выражения или определения нет перевода строки, то это конец блока.
            break;
        }
        this.eol();
    }

};

// ----------------------------------------------------------------------------------------------------------------- //

// body = '{' block '}' | expr

Yate.Grammar.rules.body = function() {
    if (this.test('{')) {
        this.match('{');
        var body = this.match('block');
        this.match('}');
        return body;
    } else {
        var expr = this.match('expr');
        return Yate.AST.make('block', expr);
    }
};

// ----------------------------------------------------------------------------------------------------------------- //
// Declarations: templates, functions, vars
// ----------------------------------------------------------------------------------------------------------------- //

// template = 'match' jpath ( argList )? ( templateMode )? body

Yate.Grammar.rules.template = function(ast) {
    this.match('MATCH');
    ast.$jpath = this.match('jpath');
    if (this.test('(')) {
        ast.$args = this.match('argList');
    }
    ast.$mode = this.match('templateMode')
    ast.$body = this.match('body');
};

// templateMode = ':' QNAME | ''

Yate.Grammar.rules.templateMode = function(ast) {
    if (this.test(':')) {
        this.match(':');
        ast.$value = this.match('QNAME');
    } else {
        ast.$value = '';
    }
};

// argList = '(' ( argListItem ( ',' argListItem )* )? ')'

Yate.Grammar.rules.argList = function(ast) {
    this.match('(');
    if (this.test('argListItem')) {
        ast.add(this.match('argListItem'));
        while (this.test(',')) {
            this.match(',');
            ast.add(this.match('argListItem'));
        }
    }
    this.match(')');
};

Yate.Grammar.rules.argListItem = function(ast) {
    var r;
    if (r = this.testAny([ 'NODESET', 'BOOLEAN', 'SCALAR' ])) {
        ast.$typedef = this.match(r);
    }
    ast.$name = this.match('QNAME');
    if (this.test('=')) {
        this.match('=');
        ast.$default = this.match('inlineExpr');
    }
};

// ----------------------------------------------------------------------------------------------------------------- //

// function_ = 'function' QNAME argList body

Yate.Grammar.rules.function_ = function(ast) {
    this.match('FUNCTION');
    var name = ast.$name = this.match('QNAME');
    ast.$args = this.match('argList');
    ast.$body = this.match('body');
};

// ----------------------------------------------------------------------------------------------------------------- //

// var_ = QNAME '=' expr

Yate.Grammar.rules.var_ = function(ast) {
    ast.$name = this.match('QNAME');
    this.match('=');
    var value = this.match('expr');
    ast.$value = (value.is('inlineList')) ? value : Yate.AST.make('block', value);
};

// key = 'key' QNAME '(' inlineExpr ',' inlineExpr ')' body

Yate.Grammar.rules.key = function(ast) {
    this.match('KEY');
    ast.$name = this.match('QNAME');
    this.match('(');
    ast.$nodes = this.match('inlineExpr');
    this.match(',');
    ast.$use = this.match('inlineExpr');
    this.match(')');
    ast.$body = this.match('body');
};

// ----------------------------------------------------------------------------------------------------------------- //

// EXPRESSIONS

// ----------------------------------------------------------------------------------------------------------------- //

// expr = inlineExpr | blockExpr

Yate.Grammar.rules.expr = function() {
    return (this.test('blockExpr')) ? this.match('blockExpr') : this.match('inlineExpr');
    // return (this.test('inlineExpr')) ? this.match('inlineExpr') : this.match('blockExpr');
};

// ----------------------------------------------------------------------------------------------------------------- //

// BLOCK EXPRESSIONS

// ----------------------------------------------------------------------------------------------------------------- //

// blockExpr = if_ | for_ | apply | xmlLine | attr | blockComplex | inlineList

Yate.Grammar.rules.blockExpr = function() {
    var r;

    if (this.test('IF')) {
        r = this.match('if_');
    } else if (this.test('FOR')) {
        r = this.match('for_');
    } else if (this.test('APPLY')) {
        r = this.match('apply');
    } else if (this.test('@')) {
        r = this.match('attr');
    } else if (this.test('<')) {
        r = this.match('xmlLine');
    /*
    } else if (this.test('pair')) {
        r = this.match('pair');
    */
    } else if (this.test('inlineList')) {
        r = this.match('inlineList');
    } else if (this.test('(')) {
        r = this.match('blockComplex');
    }

    return r;
};

// ----------------------------------------------------------------------------------------------------------------- //

// if_ = 'if' '(' inlineExpr ')' body ( 'else' body )?

Yate.Grammar.rules.if_ = function(ast) {
    this.match('IF');
    this.match('(');
    ast.$condition = this.match('inlineExpr');
    this.match(')');
    ast.$then = this.match('body');
    if (this.test('ELSE')) {
        this.match('ELSE');
        ast.$else = this.match('body');
    }
};

// ----------------------------------------------------------------------------------------------------------------- //

// for_ = 'for' '(' inlineExpr ')' body

Yate.Grammar.rules.for_ = function(ast) {
    this.match('FOR');
    this.match('(');
    ast.$expr = this.match('inlineExpr');
    this.match(')');
    ast.$body = this.match('body');
};

// ----------------------------------------------------------------------------------------------------------------- //

// apply = 'apply' inlineExpr ( callArgs )? ( templateMode )?

Yate.Grammar.rules.apply = function(ast) {
    this.match('APPLY');
    ast.$expr = this.match('inlineExpr');
    if (this.test('(')) {
        ast.$args = this.match('callArgs');
    }
    ast.$mode = this.match('templateMode');
};

// callArgs = '(' ( inlineExpr ( ',' inlineExpr )* )? ')'

Yate.Grammar.rules.callArgs = function(ast) {
    this.match('(');
    if (this.test('inlineExpr')) {
        ast.add( this.match('inlineExpr') );
        while (this.test(',')) {
            this.match(',');
            ast.add( this.match('inlineExpr') );
        }
    }
    this.match(')');
};

// ----------------------------------------------------------------------------------------------------------------- //

// attr = '@' QNAME ( '=' | '+=' | '-=' ) expr

Yate.Grammar.rules.attr = function(ast) {
    this.match('@');
    ast.$name = this.match('QNAME');
    var r;
    if (r = this.testAny([ '+=', '-=', '=' ])) {
        this.match(r);
        ast.$op = r;
        ast.$expr = this.match('expr');
    } else {<commit_msg>Правило expr всегда возвращало blockExpr, так что оно не нужно<commit_after>// ################################################################################################################# //
//
// Grammar
//
// ################################################################################################################# //

Yate.Grammar = {};

// ----------------------------------------------------------------------------------------------------------------- //

// TOKENS

Yate.Grammar.tokens = {
    QNAME: /^[a-zA-Z_][a-zA-Z0-9_-]*/,
    ESC: /^["'\\nt]/,
    NUMBER: /^[0-9]+(\.[0-9]+)?/,
    '/': /^\/(?!\/)/,
    '|': /^\|(?!\|)/,
    '=': /^=(?!=)/,
    '{': /^{(?!{)/,
    '}': /^}(?!})/
};

// ----------------------------------------------------------------------------------------------------------------- //

// KEYWORDS

Yate.Grammar.keywords = [ 'match', 'function', 'for', 'if', 'else', 'apply', 'key', 'nodeset', 'boolean', 'scalar' ];

// ----------------------------------------------------------------------------------------------------------------- //

// RULES

Yate.Grammar.rules = {};

// ----------------------------------------------------------------------------------------------------------------- //
// Blocks
// ----------------------------------------------------------------------------------------------------------------- //

// stylesheet = block

Yate.Grammar.rules.stylesheet = {

    rule: function(ast) {
        ast.$body = this.match('block');

        if (!this.isEOF()) {
            this.error('EOF expected');
        }

    },

    options: {
        skipper: 'default_'
    }

};

// ----------------------------------------------------------------------------------------------------------------- //

// block = ( template | function_ | key | var_ | blockExpr )*

Yate.Grammar.rules.block = function(ast) {
    while (!( this.isEOF() || this.testAny([ '}', ')' ]) )) { // Блок верхнего уровня (stylesheet) заканчивается с концом файла.
                                                              // Вложенные блоки заканчиваются закрывающей скобкой '}' или ')'.
        if (this.isEOL()) { // Пропускаем пустые строки.
            this.eol();
            continue;
        }

        if (this.test('MATCH')) {
            ast.$templates.add( this.match('template') );
        } else if (this.test('FUNCTION')) {
            ast.$defs.add( this.match('function_') );
        } else if (this.test('KEY')) {
            ast.$defs.add( this.match('key') );
        } else if (this.testAll([ 'QNAME', '=' ])) {
            ast.$defs.add( this.match('var_') );
        } else {
            ast.$exprs.add( this.match('blockExpr') );
        }

        if (!this.isEOL()) { // Если после выражения или определения нет перевода строки, то это конец блока.
            break;
        }
        this.eol();
    }

};

// ----------------------------------------------------------------------------------------------------------------- //

// body = '{' block '}' | expr

Yate.Grammar.rules.body = function() {
    if (this.test('{')) {
        this.match('{');
        var body = this.match('block');
        this.match('}');
        return body;
    } else {
        var expr = this.match('blockExpr');
        return Yate.AST.make('block', expr);
    }
};

// ----------------------------------------------------------------------------------------------------------------- //
// Declarations: templates, functions, vars
// ----------------------------------------------------------------------------------------------------------------- //

// template = 'match' jpath ( argList )? ( templateMode )? body

Yate.Grammar.rules.template = function(ast) {
    this.match('MATCH');
    ast.$jpath = this.match('jpath');
    if (this.test('(')) {
        ast.$args = this.match('argList');
    }
    ast.$mode = this.match('templateMode')
    ast.$body = this.match('body');
};

// templateMode = ':' QNAME | ''

Yate.Grammar.rules.templateMode = function(ast) {
    if (this.test(':')) {
        this.match(':');
        ast.$value = this.match('QNAME');
    } else {
        ast.$value = '';
    }
};

// argList = '(' ( argListItem ( ',' argListItem )* )? ')'

Yate.Grammar.rules.argList = function(ast) {
    this.match('(');
    if (this.test('argListItem')) {
        ast.add(this.match('argListItem'));
        while (this.test(',')) {
            this.match(',');
            ast.add(this.match('argListItem'));
        }
    }
    this.match(')');
};

Yate.Grammar.rules.argListItem = function(ast) {
    var r;
    if (r = this.testAny([ 'NODESET', 'BOOLEAN', 'SCALAR' ])) {
        ast.$typedef = this.match(r);
    }
    ast.$name = this.match('QNAME');
    if (this.test('=')) {
        this.match('=');
        ast.$default = this.match('inlineExpr');
    }
};

// ----------------------------------------------------------------------------------------------------------------- //

// function_ = 'function' QNAME argList body

Yate.Grammar.rules.function_ = function(ast) {
    this.match('FUNCTION');
    var name = ast.$name = this.match('QNAME');
    ast.$args = this.match('argList');
    ast.$body = this.match('body');
};

// ----------------------------------------------------------------------------------------------------------------- //

// var_ = QNAME '=' expr

Yate.Grammar.rules.var_ = function(ast) {
    ast.$name = this.match('QNAME');
    this.match('=');
    var value = this.match('blockExpr');
    ast.$value = (value.is('inlineList')) ? value : Yate.AST.make('block', value);
};

// key = 'key' QNAME '(' inlineExpr ',' inlineExpr ')' body

Yate.Grammar.rules.key = function(ast) {
    this.match('KEY');
    ast.$name = this.match('QNAME');
    this.match('(');
    ast.$nodes = this.match('inlineExpr');
    this.match(',');
    ast.$use = this.match('inlineExpr');
    this.match(')');
    ast.$body = this.match('body');
};

// ----------------------------------------------------------------------------------------------------------------- //

// EXPRESSIONS

// ----------------------------------------------------------------------------------------------------------------- //

// BLOCK EXPRESSIONS

// ----------------------------------------------------------------------------------------------------------------- //

// blockExpr = if_ | for_ | apply | xmlLine | attr | blockComplex | inlineList

Yate.Grammar.rules.blockExpr = function() {
    var r;

    if (this.test('IF')) {
        r = this.match('if_');
    } else if (this.test('FOR')) {
        r = this.match('for_');
    } else if (this.test('APPLY')) {
        r = this.match('apply');
    } else if (this.test('@')) {
        r = this.match('attr');
    } else if (this.test('<')) {
        r = this.match('xmlLine');
    /*
    } else if (this.test('pair')) {
        r = this.match('pair');
    */
    } else if (this.test('inlineList')) {
        r = this.match('inlineList');
    } else if (this.test('(')) {
        r = this.match('blockComplex');
    }

    return r;
};

// ----------------------------------------------------------------------------------------------------------------- //

// if_ = 'if' '(' inlineExpr ')' body ( 'else' body )?

Yate.Grammar.rules.if_ = function(ast) {
    this.match('IF');
    this.match('(');
    ast.$condition = this.match('inlineExpr');
    this.match(')');
    ast.$then = this.match('body');
    if (this.test('ELSE')) {
        this.match('ELSE');
        ast.$else = this.match('body');
    }
};

// ----------------------------------------------------------------------------------------------------------------- //

// for_ = 'for' '(' inlineExpr ')' body

Yate.Grammar.rules.for_ = function(ast) {
    this.match('FOR');
    this.match('(');
    ast.$expr = this.match('inlineExpr');
    this.match(')');
    ast.$body = this.match('body');
};

// ----------------------------------------------------------------------------------------------------------------- //

// apply = 'apply' inlineExpr ( callArgs )? ( templateMode )?

Yate.Grammar.rules.apply = function(ast) {
    this.match('APPLY');
    ast.$expr = this.match('inlineExpr');
    if (this.test('(')) {
        ast.$args = this.match('callArgs');
    }
    ast.$mode = this.match('templateMode');
};

// callArgs = '(' ( inlineExpr ( ',' inlineExpr )* )? ')'

Yate.Grammar.rules.callArgs = function(ast) {
    this.match('(');
    if (this.test('inlineExpr')) {
        ast.add( this.match('inlineExpr') );
        while (this.test(',')) {
            this.match(',');
            ast.add( this.match('inlineExpr') );
        }
    }
    this.match(')');
};

// ----------------------------------------------------------------------------------------------------------------- //

// attr = '@' QNAME ( '=' | '+=' | '-=' ) expr

Yate.Grammar.rules.attr = function(ast) {
    this.match('@');
    ast.$name = this.match('QNAME');
    var r;
    if (r = this.testAny([ '+=', '-=', '=' ])) {
        this.match(r);
        ast.$op = r;
        ast.$expr = this.match('blockExpr');
    } else {
{'fraction_non_alphanumeric': 0.42542244367417675, 'fraction_numerical': 0.0006499133448873484, 'mean_word_length': 1.1460948396094839, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4283045', 'n_tokens_mistral': 5099, 'n_tokens_neox': 5096, 'n_words': 804}