1.0.0 • Published 3 years ago

j-simple-parser v1.0.0

Weekly downloads
-
License
MIT
Repository
-
Last release
3 years ago

A simple and easy to use text parsing javascript library, j-simple-parser comes with elegant parsers that you can use to build complex and more elegant parsers.

CORE PARSERS

Importing core parsers

const {
    string,
    any,
    char,
    firstChar,
    lastChar,
    remainingChars,
    digit,
    space,
    spaceOptional,
    letter,
    pattern,
    word,
    charsBefore,
    debug,
    newLine,
} = require('j-simple-parser');

Using core parsers

1).

console.log( string('WE', false).seq(any(space()).star()).parse('Welcome to Magabelab') ); 

Output

Success { start: 0, end: 7, value: 'Welcome' }

2).

console.log( any(char('g')).plus().parse('I am good') ); 

Output

Success { start: 0, end: 5, value: 'I am ' }

3).

console.log( digit().plus().firstMatch('I am 20 years old and my sister is 12 years old') ); 

Output

Success { start: 5, end: 7, value: '20' }

4).

console.log( digit().removeFrom('I am 20 years old and my sister is 12 years old') ); 

Output

I am  years old and my sister is  years old

5). Capitalize a sentence , using replaceIn method

console.log(letter().replaceIn('I am 20 years old and my sister is 12 years old', (letter) => {
    return letter.toUpperCase();
})); 

Output

I AM 20 YEARS OLD AND MY SISTER IS 12 YEARS OLD

6).

console.log( letter().allStringMatches('Magabe Lab') ); 

Output

['M', 'a', 'g','a', 'b', 'e','L', 'a', 'b']

7). Format spaces

console.log(space().replaceIn('I    am      good ', (_) => {
    return ' ';
}));  

Output

  I am good

8).

console.log( firstChar().parse('I am the best') ); 

Output

Success { start: 0, end: 1, value: 'I' }

9).

console.log( lastChar().allMatches('I am the best') ); 

Output

Success { start: 12, end: 13, value: 't' } ]

10).

console.log( remainingChars().parse('I am the best') ); 
Success { start: 0, end: 13, value: 'I am the best' }

11).

console.log( space().seq(char('e').repeat(2, 3)).parse('    eee') ); 

Output

Success { start: 0, end: 7, value: '    eee' }

12). using removeFrom method

console.log( digit().or(space()).removeFrom('  744  eee') ); 

Output

eee

13).

console.log(char('c').parse('chura', 0)); 

Output

Success { start: 0, end: 1, value: 'c' }

14).

console.log(char('c').parse('chura', 1)); 
Failure { position: 1, message: '' }

15).

const tag = char('<')
    .seq(letter().plus())
    .seq(spaceOptional())
    .seq(char('/').optional())
    .seq(char('>'));

console.log( tag.firstStringMatch('<tag></tag>') ); 

console.log( tag.allStringMatches('<tag/><i></i>') ); 

Outputs

<tag>

[ '<tag/>', '<i>' ]

16).

const code = '//my variable' +
    '\n' +
    'let foo =1;' +
    '\n' +
    '/*this is my function*/' +
    'function getMoney()' +
    '{' +
    'console.log("money money");' +
    '}';

function comment() {
    const singleLine = string("//").seq(any(string("//").or(char('\n'))).star()).seq(spaceOptional()).plus();
    const multiLine = string("/*").seq(any(string("*/")).plus()).seq(string("*/"));
    return singleLine.or(multiLine);
}


console.log( comment().allStringMatches(code) );

Output

[ '<tag/>', '<i>' ]

WRITING YOUR OWN PARSER

Import the required resources

/** Required when building a parser from scratch**/
const {
    Context,
    Success,
    Failure,
    parserPrototype
} = require('j-simple-parser');

The data we are going to parse.

const code = '//my variable' +
    '\n' +
    'let foo =1;' +
    '\n' +
    '/*this is my function*/' +
    'function getMoney()' +
    '{' +
    'console.log("money money");' +
    '}';

1). Declare a parser class.

function FunctionParser(className) {
    this.className = className;
}

2). Extend Parser using parserPrototype function. parserPrototype function take two arguments, the first argument is the parser class, and the last argument is parseOn function. parseOn function takes one argument which is the context.

FunctionParser.prototype = parserPrototype(FunctionParser, function (context) {
    const buffer = context.buffer;
    const pos = context.position;
    if (pos < buffer.length) {
        const fn_declaration_match = this.fnDeclaration(this.className).parse(buffer, pos);
        if (fn_declaration_match.isSuccess()) {
            const start = fn_declaration_match.start;
            const function_scope_begin_match = spaceOptional().seq(char('{')).parse(buffer, fn_declaration_match.end);
            if (function_scope_begin_match.isFailure()) {
                return new Failure(function_scope_begin_match.position);
            } else {
                let list = char('{').or(char('}')).allMatches(buffer, function_scope_begin_match.end);
                if (list.length === 0) {
                    return new Failure(function_scope_begin_match.end);
                }

                let stack = 1;//the 1 verified by function_scope_begin_match
                let end = 0;
                for (let i = 0; i < list.length; i++) {
                    const match = list[i];
                    if (char('{').hasMatch(match.value)) {
                        stack++;
                    } else {
                        stack--;
                    }
                    end = match.end;

                    if (stack === 0) {
                        return new Success(buffer, start, end);
                    }
                }
                return new Failure(end);
            }
        } else {
            return new Failure(fn_declaration_match.position);
        }
    } else {
        return Failure(buffer.length());
    }
})

3). After extending your parser using parserPrototype , you can now add more methods, these methods can be used with in parseOn method, here i add fnDeclaration method.

FunctionParser.prototype.fnDeclaration = function (className) {
    const pointer_or_ref = char('*').or(char('&'));
    const name = (letter().or(char('_'))).seq((char('-').or(char('_')).or(word())).star());
    const return_type = name.seq(pointer_or_ref).or(name);
    const class_scope_name = string(className).seq(string("::")).seq((char('~').seq(string(className))).or(name));

    const with_return_type = return_type.seq(space()).seq(class_scope_name)
        .seq(spaceOptional()).seq(char('('))
        .seq(any(char(')'), ")").star().seq(char(')')));

    const with_no_return_type = spaceOptional().seq(class_scope_name)
        .seq(spaceOptional()).seq(char('('))
        .seq(any(char(')'), ")").star().seq(char(')'))).or(
            spaceOptional().seq(string('function'))
                .seq(spaceOptional()).seq(name.optional()).seq(spaceOptional()).seq(char('('))
                .seq(any(char(')'), ")").star().seq(char(')')))
        );

    return with_return_type.or(with_no_return_type);
}

4). Following j-simple-parser library conversion, create a factory function for your parser.

function fn(className = '') {
    return new FunctionParser(className);
}

5). Done, our parser is ready to use.

console.log( fn().allMatches(code) );

Output

[
  Success {
    start: 49,
    end: 97,
    value: 'function getMoney(){console.log("money money");}'
  }
]

Using with other parsers.

console.log( comment().seq(fn()).allMatches(code) );

Output

[
  Success {
    start: 26,
    end: 97,
    value: '/*this is my function*/function getMoney(){console.log("money money");}'
  }
]