Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OnMatch && OnError Callback Function. #28

Merged
merged 11 commits into from
Feb 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
- [ ] Add traverse functions for Tokens.
- [ ] Callbacks for P4_Token.
- [ ] Dynamically change grammar rules.
- [ ] Example JSON: rfc7159.
- [ ] New Expression Kind: Numeric.
- [ ] New Expression Kind: CharacterSet.
- [ ] New Expression Kind: Complement.
Expand All @@ -23,6 +22,7 @@
- [ ] backrefs is not necessary if there is no BackReference in Sequence.
- [ ] A binary executable for display parsed results in JSON output. The executable can support general programming languages, such as Mustache, Python, JSON, YAML, etc. Some other programs can then takes the input from stdin and do some fancy work. `peppapeg --lang=py38 source.py`
- [ ] Create docs and push it to gh-pages branch.
- [x] Example JSON: rfc7159. Added in v1.4.0.
- [x] Auto generate documentation for functions and defined macros. Example: doxygen. Added in v1.4.0.
- [x] NeedLoosen, NeedSquash, NeedLift should be well-tested.
- [x] Add depth setter. Added in v1.3.0.
Expand Down
61 changes: 61 additions & 0 deletions examples/mustache.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,59 @@ typedef enum {
P4_MustacheWhitespace = 22,
} P4_MustacheRuleID;

P4_Error P4_MustacheCallback(P4_Grammar* grammar, P4_Expression* rule, P4_Token* token) {
if (rule
&& rule->id == P4_MustacheTag
&& token
&& token->head
&& token->head->next
&& token->head->next->expr->id == P4_MustacheSetDelimiter) {
P4_String opener = NULL, closer = NULL;
P4_Expression* opener_expr = NULL;
P4_Expression* closer_expr = NULL;

opener = P4_CopyTokenString(token->head->next->head);
if (opener == NULL)
goto finalize_set_delimiter;

closer = P4_CopyTokenString(token->head->next->tail);
if (closer == NULL)
goto finalize_set_delimiter;

opener_expr = P4_CreateLiteral(opener, true);
if (opener_expr == NULL)
goto finalize_set_delimiter;

closer_expr = P4_CreateLiteral(closer, true);
if (closer_expr == NULL)
goto finalize_set_delimiter;

P4_Error err = P4_Ok;
if ((err = P4_ReplaceGrammarRule(grammar, P4_MustacheOpener, opener_expr)) != P4_Ok)
goto finalize_set_delimiter;

if ((err = P4_ReplaceGrammarRule(grammar, P4_MustacheCloser, closer_expr)) != P4_Ok)
goto finalize_set_delimiter;

token->head->expr = opener_expr;
token->tail->expr = closer_expr;

free(opener);
free(closer);

return P4_Ok;

finalize_set_delimiter:
free(opener);
free(closer);
P4_DeleteExpression(opener_expr);
P4_DeleteExpression(closer_expr);
return P4_MemoryError;
}

return P4_Ok;
}

/*
* Entry = SOI Line*
* Line = (Tag / Text)* (NewLine / EOI) # FLAG: TIGHT, Callback: UpdateIndent, TrimTokens
Expand Down Expand Up @@ -135,6 +188,8 @@ P4_PUBLIC P4_Grammar* P4_CreateMustacheGrammar() {
))
goto finalize;

if (P4_Ok != P4_SetGrammarRuleFlag(grammar, P4_MustacheTag, P4_FLAG_SCOPED))
goto finalize;

if (P4_Ok != P4_AddSequenceWithMembers(grammar, P4_MustacheSetDelimiter, 4,
P4_CreateLiteral("=", true),
Expand Down Expand Up @@ -292,6 +347,12 @@ P4_PUBLIC P4_Grammar* P4_CreateMustacheGrammar() {
))
goto finalize;

if (P4_Ok != P4_SetGrammarRuleFlag(grammar, P4_MustacheEntry, P4_FLAG_LIFTED))
goto finalize;

if (P4_Ok != P4_SetGrammarCallback(grammar, &P4_MustacheCallback, NULL))
goto finalize;

return grammar;

finalize:
Expand Down
103 changes: 99 additions & 4 deletions peppapeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ P4_PRIVATE(P4_String) P4_CopySliceString(P4_String, P4_Slice*);
P4_PRIVATE(P4_Error) P4_SetWhitespaces(P4_Grammar*);
P4_PRIVATE(P4_Expression*) P4_GetWhitespaces(P4_Grammar*);

P4_PRIVATE(P4_Error) P4_RefreshReference(P4_Expression*, P4_RuleID);

P4_PRIVATE(P4_Token*) P4_Match(P4_Source*, P4_Expression*);
P4_PRIVATE(P4_Token*) P4_MatchLiteral(P4_Source*, P4_Expression*);
P4_PRIVATE(P4_Token*) P4_MatchRange(P4_Source*, P4_Expression*);
Expand Down Expand Up @@ -832,31 +834,43 @@ P4_Match(P4_Source* s, P4_Expression* e) {
assert(e != NULL);

if (s->err != P4_Ok) {
return NULL;
goto finalize;
}

P4_Error err = P4_Ok;
P4_Token* result = NULL;

if (IS_RULE(e) && (err = P4_PushFrame(s, e)) != P4_Ok) {
P4_RaiseError(s, err, "failed to push frame");
return NULL;
goto finalize;
}

result = P4_Expression_dispatch(s, e);

if (IS_RULE(e) && (err = P4_PopFrame(s, NULL)) != P4_Ok) {
P4_RaiseError(s, err, "failed to pop frame");
P4_DeleteToken(result);
return NULL;
goto finalize;
}

if (s->err != P4_Ok) {
P4_DeleteToken(result);
return NULL;
goto finalize;
}

if (s->grammar->on_match && (err = (s->grammar->on_match)(s->grammar, e, result)) != P4_Ok) {
P4_RaiseError(s, err, "failed to run match callback.");
P4_DeleteToken(result);
goto finalize;
}

return result;

finalize:
if (s->grammar->on_error && (err = (s->grammar->on_error)(s->grammar, e)) != P4_Ok) {
P4_RaiseError(s, err, "failed to run error callback.");
}
return NULL;
}

P4_Token*
Expand Down Expand Up @@ -1226,6 +1240,8 @@ P4_PUBLIC P4_Grammar* P4_CreateGrammar(void) {
grammar->spaced_count = SIZE_MAX;
grammar->spaced_rules = NULL;
grammar->depth = P4_DEFAULT_RECURSION_LIMIT;
grammar->on_match = NULL;
grammar->on_error = NULL;
return grammar;
}

Expand Down Expand Up @@ -1880,3 +1896,82 @@ P4_PUBLIC void
P4_SetScoped(P4_Expression* e) {
return P4_SetExpressionFlag(e, P4_FLAG_SCOPED);
}

P4_PUBLIC P4_Error
P4_SetGrammarCallback(P4_Grammar* grammar, P4_MatchCallback matchcb, P4_ErrorCallback errcb) {
if (grammar == NULL)
return P4_NullError;

grammar->on_match = matchcb;
grammar->on_error = errcb;

return P4_Ok;
}

P4_PRIVATE(P4_Error)
P4_RefreshReference(P4_Expression* expr, P4_RuleID id) {
if (expr == NULL || id == 0)
return P4_NullError;

P4_Error err = P4_Ok;

switch(expr->kind) {
case P4_Reference:
if (expr->ref_id == id)
expr->ref_expr = NULL;
break;
case P4_Positive:
case P4_Negative:
err = P4_RefreshReference(expr->ref_expr, id);
break;
case P4_Sequence:
case P4_Choice:
for (size_t i = 0; i < expr->count; i++) {
err = P4_RefreshReference(expr->members[i], id);
if (err != P4_Ok)
break;
}
break;
case P4_Repeat:
err = P4_RefreshReference(expr->repeat_expr, id);
break;
default:
break;
}

return err;
}

P4_PUBLIC P4_Error
P4_ReplaceGrammarRule(P4_Grammar* grammar, P4_RuleID id, P4_Expression* expr) {
if (grammar == NULL || id == 0 || expr == NULL)
return P4_NullError;

P4_Expression* oldexpr = P4_GetGrammarRule(grammar, id);
if (oldexpr == NULL)
return P4_NameError;

P4_Error err = P4_Ok;

for (size_t i = 0; i < grammar->count; i++) {
if (grammar->rules[i]->id == id) {
P4_DeleteExpression(oldexpr);

grammar->rules[i] = expr;
expr->id = id;

break;
}
}

for (size_t i = 0; i < grammar->count; i++) {
if (grammar->rules[i]->id != id) {
err = P4_RefreshReference(grammar->rules[i], id);

if (err != P4_Ok)
return err;
}
}

return P4_Ok;
}
37 changes: 34 additions & 3 deletions peppapeg.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,11 @@ typedef struct P4_Expression {
/* The name of expression (only for debugging). */
/* P4_String name; */
/** The id of expression. */
P4_RuleID id;
P4_RuleID id;
/** The kind of expression. */
P4_ExpressionKind kind;
P4_ExpressionKind kind;
/** The flag of expression. */
P4_ExpressionFlag flag;
P4_ExpressionFlag flag;

union {
/** Used by P4_Numeric. */
Expand Down Expand Up @@ -317,6 +317,12 @@ typedef struct P4_Token {
struct P4_Token* tail;
} P4_Token;

/**
* The callback after an expression is matched.
*/
typedef P4_Error (*P4_MatchCallback)(struct P4_Grammar*, struct P4_Expression*, struct P4_Token*);
typedef P4_Error (*P4_ErrorCallback)(struct P4_Grammar*, struct P4_Expression*);

/**
* The grammar object that holds all grammar rules.
*/
Expand All @@ -333,6 +339,10 @@ typedef struct P4_Grammar{
struct P4_Expression* spaced_rules;
/** The recursion limit, or maximum allowed nested rules. */
size_t depth;
/** The callback after a match for an expression is successful. */
P4_MatchCallback on_match;
/** The callback after a match for an expression is failed. */
P4_ErrorCallback on_error;
} P4_Grammar;


Expand Down Expand Up @@ -644,6 +654,27 @@ P4_PUBLIC P4_Slice* P4_GetTokenSlice(P4_Token*);
*/
P4_PUBLIC P4_String P4_CopyTokenString(P4_Token*);


/**
* @brief Set callback function.
* @param grammar The grammar.
* @param matchdb The callback on a successful match.
* @param errorcb The callback on a failure match.
* @return The error code.
*/
P4_PUBLIC P4_Error P4_SetGrammarCallback(P4_Grammar*, P4_MatchCallback, P4_ErrorCallback);

/**
* @brief Replace an existing grammar rule.
* @param grammar The grammar.
* @param id The rule id.
* @param expr The rule expression to replace.
* @return The error code.
*
* The original grammar rule will be deleted.
*/
P4_PUBLIC P4_Error P4_ReplaceGrammarRule(P4_Grammar*, P4_RuleID, P4_Expression*);

#ifdef __cplusplus
}
#endif
Expand Down
55 changes: 55 additions & 0 deletions tests/test_example_mustache.c
Original file line number Diff line number Diff line change
Expand Up @@ -519,12 +519,66 @@ P4_PRIVATE(void) test_set_delimiter(void) {

TEST_ASSERT_NOT_NULL(token->head);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheOpener, token->head);
TEST_ASSERT_EQUAL_TOKEN_STRING("{{", token->head);

TEST_ASSERT_NOT_NULL(token->head->next);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheSetDelimiter, token->head->next);

TEST_ASSERT_NOT_NULL(token->tail);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheCloser, token->tail);
TEST_ASSERT_EQUAL_TOKEN_STRING("}}", token->tail);

P4_DeleteSource(source);
P4_DeleteGrammar(grammar);
}

P4_PRIVATE(void) test_set_delimiter_altered_grammar(void) {
P4_Grammar* grammar = P4_CreateMustacheGrammar();
TEST_ASSERT_NOT_NULL(grammar);

P4_Source* source;

source = P4_CreateSource("{{=<% %>=}}<% x %>", P4_MustacheEntry);
TEST_ASSERT_NOT_NULL(source);
TEST_ASSERT_EQUAL(
P4_Ok,
P4_Parse(grammar, source)
);
TEST_ASSERT_EQUAL(18, P4_GetSourcePosition(source));

P4_Token* token = P4_GetSourceAst(source);

TEST_ASSERT_NOT_NULL(token);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheTag, token);
TEST_ASSERT_EQUAL_TOKEN_STRING("{{=<% %>=}}", token);

TEST_ASSERT_NOT_NULL(token->head);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheOpener, token->head);
TEST_ASSERT_EQUAL_TOKEN_STRING("{{", token->head);

TEST_ASSERT_NOT_NULL(token->head->next);
TEST_ASSERT_EQUAL_TOKEN_STRING("=<% %>=", token->head->next);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheSetDelimiter, token->head->next);

TEST_ASSERT_NOT_NULL(token->tail);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheCloser, token->tail);
TEST_ASSERT_EQUAL_TOKEN_STRING("}}", token->tail);

TEST_ASSERT_NOT_NULL(token->next);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheTag, token->next);
TEST_ASSERT_EQUAL_TOKEN_STRING("<% x %>", token->next);

TEST_ASSERT_NOT_NULL(token->next->head);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheOpener, token->next->head);
TEST_ASSERT_EQUAL_TOKEN_STRING("<%", token->next->head);

TEST_ASSERT_NOT_NULL(token->next->head->next);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheVariable, token->next->head->next);
TEST_ASSERT_EQUAL_TOKEN_STRING("x", token->next->head->next);

TEST_ASSERT_NOT_NULL(token->next->tail);
TEST_ASSERT_EQUAL_TOKEN_RULE(P4_MustacheCloser, token->next->tail);
TEST_ASSERT_EQUAL_TOKEN_STRING("%>", token->next->tail);

P4_DeleteSource(source);
P4_DeleteGrammar(grammar);
Expand Down Expand Up @@ -745,6 +799,7 @@ int main(void) {
RUN_TEST(test_comment);
RUN_TEST(test_set_delimiter_new_opener);
RUN_TEST(test_set_delimiter);
RUN_TEST(test_set_delimiter_altered_grammar);
RUN_TEST(test_text_followed_by_newline);
RUN_TEST(test_text_followed_by_opener);
RUN_TEST(test_line_only_text);
Expand Down
Loading