Table of Contents
- How to make your parser
-
Let’s create your custom parser.
In this tutorial, we make a parser nameddigit3
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.
Let’s create a parser named digit3
, which parses three digits.
In this example, Exception handling mechanism is used:
- Defines a variable
ctx
of typeCtx
. - Using
TRY(&ctx) {...} else {...}
(C++, Java liketry {...} 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 */
}
}
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" */
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;
}
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;
}