Skip to content

Latest commit

 

History

History
160 lines (126 loc) · 4.75 KB

TUTORIAL.org

File metadata and controls

160 lines (126 loc) · 4.75 KB

Tutorial

Table of Contents

How to make your parser
Let’s create your custom parser.
In this tutorial, we make a parser named digit3 which parses three digits.
You can use ‘parser combinator’ to make new parser
In this tutorial, you can learn the basic of parser combinator.
By using built-in parsers and parser combinators, you can create your parser more easily.

How to make your parser

Let’s create a parser named digit3, which parses three digits.

Step 1. Define function body of your parser.

In this example, Exception handling mechanism is used:

  • Defines a variable ctx of type Ctx.
  • Using TRY(&ctx) {...} else {...} (C++, Java like try {...} catch {...} clause)
  • Using parse(digit, src, &ctx) to parse a digit char.
  • Using cthrow(ex, ctx.msg) to re-throw an exception caused in TRY block.
#include "cparsec2.h"

const char* run_digit3(void* arg, Source src, Ctx* ex) {
  /* omit 'arg' since 'digit3' use no arguments */
  UNUSED(arg);

  Buff(Char) str = {0};
  Ctx ctx;
  TRY(&ctx) {                   /* try */
    for (int i = 0; i < 3; ++i) {
      buff_push(&str, parse(digit, src, &ctx));
    }
    return buff_finish(&str);
  }
  else {                        /* catch */
    mem_free(str.data);
    cthrow(ex, ctx.msg);        /* re-throw */
  }
}

Step 2. Create parser by calling PARSER_GEN(T)(fun, arg)

PARSER(String) digit3 = PARSER_GEN(String)(run_digit3, NULL);

Step 3. Then use your parser.

PARSE_TEST(digit3, "1234");   /* -> "123" */
PARSE_TEST(digit3, "123");    /* -> "123" */
PARSE_TEST(digit3, "12");     /* -> "error:too short" */
PARSE_TEST(digit3, "a123");   /* -> "error:not satisfy" */

Finally, you can get the 1st example as follows:

See also example/digit3 folder

#include "cparsec2.h"

const char* run_digit3(void* arg, Source src, Ctx* ex) {
  /* omit 'arg' since 'digit3' use no arguments */
  UNUSED(arg);

  Buff(Char) str = {0};
  Ctx ctx;
  TRY(&ctx) {                   /* try */
    for (int i = 0; i < 3; ++i) {
      buff_push(&str, parse(digit, src, &ctx));
    }
    return buff_finish(&str);
  }
  else {                        /* catch */
    mem_free(str.data);
    cthrow(ex, ctx.msg);        /* re-throw */
  }
}

int main(int argc, char** argv) {
  UNUSED(argc);
  UNUSED(argv);

  /* NOTE: THIS MUST BE CALLED ONCE AT FIRST. */
  cparsec2_init();

  PARSER(String) digit3 = PARSER_GEN(String)(run_digit3, NULL);
  PARSE_TEST(digit3, "1234");   /* -> "123" */
  PARSE_TEST(digit3, "123");    /* -> "123" */
  PARSE_TEST(digit3, "12");     /* -> "error:too short" */
  PARSE_TEST(digit3, "a123");   /* -> "error:not satisfy" */

  return 0;
}

You can use ‘parser combinator’ to make new parser

To make the 1st example digit3 parser, we used pre-defined built-in parser digit. And we made the custom function body such as run_digit3() for the digit3 parser…

Is it difficult?

Fortunately, cparsec2 has various pre-defined built-in parsers, parser generators (factory method to make parser), and parser combinators (factory method to make parser from other parser(s)).

built-in parsers
ex. anyChar, digit, lower, upper, alpha, alnum, letter, …
parser generators
factory methods to make (parameterized) parser
ex. char1(c), satisfy(pred), …
parser combinators
factory methods to make (composite) parser from other parser(s)
ex. many(p), many1(p), seq(p, ...), cons(p, ps), …

Therefore, we can made the 1st example digit3 more easily, as follows:

PARSER(String) digit3 = seq(digit, digit, digit);

That’s all !

No need to define a custom function body in case of digit3. Awesome!

See also example/digit3b folder

#include "cparsec2.h"

int main(int argc, char** argv) {
  UNUSED(argc);
  UNUSED(argv);

  /* NOTE: THIS MUST BE CALLED ONCE AT FIRST. */
  cparsec2_init();

  PARSER(String) digit3 = seq(digit, digit, digit);
  PARSE_TEST(digit3, "1234");   /* -> "123" */
  PARSE_TEST(digit3, "123");    /* -> "123" */
  PARSE_TEST(digit3, "12");     /* -> "error:too short" */
  PARSE_TEST(digit3, "a123");   /* -> "error:not satisfy" */

  return 0;
}