# All files / src/parser index.js

100% Statements 32/32
100% Branches 2/2
100% Functions 3/3
100% Lines 32/32

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109

    24x
    24x
    24x
    24x

    24x

    24x
    24x
    24x
    24x

    97x

    97x

    12371x

    6234x

    88x

    97x
    97x

    12331x

    12331x
    60x

    12331x
    12331x

    7x

    485x

    97x
    97x

    97x
    99x
    99x

    97x

    97x

    90x

    90x

     /**
     * Syntax Analysis
     *
     * The parser below creates the "bare" Abstract Syntax Tree.
     */

    // @flow
    import invariant from 'invariant';
    import Syntax, { tokens } from 'walt-syntax';
    import moo from 'moo';
    import curry from 'curry';
    // $FlowFixMe
    import coreGrammar from './grammar/grammar.ne';
    // $FlowFixMe
    import defaultArgsGrammar from '../syntax-sugar/default-arguments.ne';
    import { Parser, Grammar } from 'nearley';
    import helpers from './grammar/helpers';
    import nodes from './grammar/nodes';
    import type { NodeType } from '../flow/types';

    type GrammarType = {
      Lexer: any,
      ParserRules: Object[],
      ParserStart: string,
    };
    type MakeGrammar = () => GrammarType;

    /**
     * Returns a custom lexer. This wrapper API is necessary to ignore comments
     * in all of the subsequent compiler phases, unfortunately.
     *
     * TODO: Maybe consider adding comment nodes back to the AST. IIRC this causes
     *       lots of ambiguous grammar for whatever reason.
     */
    function makeLexer() {
      const mooLexer = moo.compile(tokens);

      return {
        current: null,
        lines: [],
        get line() {
          return mooLexer.line;
        },
        get col() {
          return mooLexer.col;
        },
        save() {
          return mooLexer.save();
        },
        reset(chunk, info) {
          this.lines = chunk.split('\n');
          return mooLexer.reset(chunk, info);
        },
        next() {
          // It's a cruel and unusual punishment to implement comments with nearly
          let token = mooLexer.next();
          // Drop all comment tokens found
          while (token && token.type === 'comment') {
            token = mooLexer.next();
          }
          this.current = token;
          return this.current;
        },
        formatError(token) {
          return mooLexer.formatError(token);
        },
        has(name) {
          return mooLexer.has(name);
        },
      };
    }

    export default curry(function parse(
      extraGrammar: MakeGrammar[],
      source: string
    ): NodeType {
      const grammarList = [coreGrammar, defaultArgsGrammar, ...extraGrammar];
      const context = {
        lexer: makeLexer(),
        nodes,
        helpers,
        Syntax,
      };

      // All Grammar plugins are factories resulting in an object which must contain
      // a "ParserRules" array which will be added to the base grammar.
      const grammar = grammarList.slice(1).reduce((acc: any, value: Function) => {
        const extra = value.call(context);
        return {
          ...acc,
          ParserRules: acc.ParserRules.concat(extra.ParserRules),
        };
      }, grammarList[0].call(context));

      const parser = new Parser(Grammar.fromCompiled(grammar));

      parser.feed(source);

      // This is a safeguard against ambiguous syntax that may be generated by blending
      // multiple different grammars together. If there is more than one was to parse
      // something then we did something wrong and we hard exit the compiler pipeline.
      invariant(
        parser.results.length === 1,
        `Ambiguous syntax number of productions: ${parser.results.length}`
      );

      return parser.results[0];
    });