pegtl - how to skip spaces for the entire grammar
I am trying to parse a very simple language with PEGTL. I think I have found the problem, but don't understand why; white spaces are not ignored. I understand it must be possible to not ignore white space so that indentation-aware languages can also be parsed. But I couldn't find a mechanism to "eat" white spaces by default. Given:
struct kw_enum : tao::pegtl::string<'e', 'n', 'u', 'm'> { };
struct enum_decl : tao::pegtl::seq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> { };
the following cannot be parsed:
enum thing;
If I add pegtl::space
between each token explicitly, then it works. But it would be major burden to do it in the entire grammar.
How can white spaces be ignored/eaten/skipped, like in C, without specifying those explicitly?
c++ parsing
add a comment |
I am trying to parse a very simple language with PEGTL. I think I have found the problem, but don't understand why; white spaces are not ignored. I understand it must be possible to not ignore white space so that indentation-aware languages can also be parsed. But I couldn't find a mechanism to "eat" white spaces by default. Given:
struct kw_enum : tao::pegtl::string<'e', 'n', 'u', 'm'> { };
struct enum_decl : tao::pegtl::seq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> { };
the following cannot be parsed:
enum thing;
If I add pegtl::space
between each token explicitly, then it works. But it would be major burden to do it in the entire grammar.
How can white spaces be ignored/eaten/skipped, like in C, without specifying those explicitly?
c++ parsing
add a comment |
I am trying to parse a very simple language with PEGTL. I think I have found the problem, but don't understand why; white spaces are not ignored. I understand it must be possible to not ignore white space so that indentation-aware languages can also be parsed. But I couldn't find a mechanism to "eat" white spaces by default. Given:
struct kw_enum : tao::pegtl::string<'e', 'n', 'u', 'm'> { };
struct enum_decl : tao::pegtl::seq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> { };
the following cannot be parsed:
enum thing;
If I add pegtl::space
between each token explicitly, then it works. But it would be major burden to do it in the entire grammar.
How can white spaces be ignored/eaten/skipped, like in C, without specifying those explicitly?
c++ parsing
I am trying to parse a very simple language with PEGTL. I think I have found the problem, but don't understand why; white spaces are not ignored. I understand it must be possible to not ignore white space so that indentation-aware languages can also be parsed. But I couldn't find a mechanism to "eat" white spaces by default. Given:
struct kw_enum : tao::pegtl::string<'e', 'n', 'u', 'm'> { };
struct enum_decl : tao::pegtl::seq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> { };
the following cannot be parsed:
enum thing;
If I add pegtl::space
between each token explicitly, then it works. But it would be major burden to do it in the entire grammar.
How can white spaces be ignored/eaten/skipped, like in C, without specifying those explicitly?
c++ parsing
c++ parsing
asked Nov 22 '18 at 9:22
user1150609user1150609
457
457
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
I don't think there is a shortcut, you have to specify the grammar in a way that makes it unambiguous where and how many whitespaces you allow.
The best way to do it I think is to add a convenience rule template that allows matching a list (tao::pegtl::seq
) of rules separated by any allowed separater (generally whitespace plus comments).
struct comment : tao::pegtl::disable< /* whatever your comment syntax is */ > {};
struct separator : tao::pegtl::sor< tao::pegtl::ascii::space, comment > {}; // either/or
struct seps : tao::pegtl::star< separator > {}; // Any separators, whitespace or comments
// Template to generate rule
// tao::pegtl::seq<Rule0, Separator, Rule1, Separator, Rule2, ... , Separator, RuleN>
template <typename Separator, typename... Rules>
struct interleaved;
template <typename Separator, typename Rule0, typename... RulesRest>
struct interleaved<Separator, Rule0, RulesRest...>
: tao::pegtl::seq<Rule0, Separator, interleaved<Separator, RulesRest...>> {};
template <typename Separator, typename Rule0>
struct interleaved<Separator, Rule0>
: Rule0 {};
// Note: interleaved<Separator /*, no Rule! */> intentionally not defined.
struct enum_decl : interleaved<seps, kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'> {};
// Expands to:
seq<kw_enum, seps, interleaved<seps, identifier, one<';'>>> ==
seq<kw_enum, seps, seq<identifier, seps, interleaved<seps, one<';'>>>> ==
seq<kw_enum, seps, seq<identifier, seps, one<';'>> ==
seq<kw_enum, seps, identifier, seps, one<';'>>
Basically, doing something like the above, you only need to replace tao::pegtl::seq<R...>
with interleaved<seps, R...>
, but you could even make a separate alias for that:
template<typename... Rules>
using sseq = interleaved<seps, Rules...>;
// Now you only have to replace tao::pegtl::seq with sseq
struct enum_decl : sseq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> {};
This strategy is mostly needed not because of whitespace-significant languages but because you don't have an earlier tokenizer step in the parsing that would separate the enum
from the thing
. Another strategy would be to implement that first, and do the later-stage parsing based on a stream of tokens instead of a stream of characters, but that is a larger rewrite and has its own drawbacks.
Note: I haven't compiled the code, but the point here should be the strategy. Please leave a comment if there is a compile error (I hope I haven't messed up the variadic templates) or if something is unclear.
Note 2: Also you probably don't want to replace every
seq
withsseq
. For example, if you have the logical and (&&
) defined asseq<one<'&'>, one<'&'>>
, that's something you probably don't want to change.
I thought about an approach, though couldn't express it correctly or my idea wouldn't work to begin with; make a class template that surrounds given rule (as template argument) withstar<space>
. I'll try your idea, and see how it works. Thanks!
– user1150609
Nov 22 '18 at 13:37
That's pegtl::pad<Rule, SepL, SepR> but you probably don't want that because you have to replace each element of everyseq
instead of just theseq
s.
– palotasb
Nov 22 '18 at 13:50
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53427551%2fpegtl-how-to-skip-spaces-for-the-entire-grammar%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I don't think there is a shortcut, you have to specify the grammar in a way that makes it unambiguous where and how many whitespaces you allow.
The best way to do it I think is to add a convenience rule template that allows matching a list (tao::pegtl::seq
) of rules separated by any allowed separater (generally whitespace plus comments).
struct comment : tao::pegtl::disable< /* whatever your comment syntax is */ > {};
struct separator : tao::pegtl::sor< tao::pegtl::ascii::space, comment > {}; // either/or
struct seps : tao::pegtl::star< separator > {}; // Any separators, whitespace or comments
// Template to generate rule
// tao::pegtl::seq<Rule0, Separator, Rule1, Separator, Rule2, ... , Separator, RuleN>
template <typename Separator, typename... Rules>
struct interleaved;
template <typename Separator, typename Rule0, typename... RulesRest>
struct interleaved<Separator, Rule0, RulesRest...>
: tao::pegtl::seq<Rule0, Separator, interleaved<Separator, RulesRest...>> {};
template <typename Separator, typename Rule0>
struct interleaved<Separator, Rule0>
: Rule0 {};
// Note: interleaved<Separator /*, no Rule! */> intentionally not defined.
struct enum_decl : interleaved<seps, kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'> {};
// Expands to:
seq<kw_enum, seps, interleaved<seps, identifier, one<';'>>> ==
seq<kw_enum, seps, seq<identifier, seps, interleaved<seps, one<';'>>>> ==
seq<kw_enum, seps, seq<identifier, seps, one<';'>> ==
seq<kw_enum, seps, identifier, seps, one<';'>>
Basically, doing something like the above, you only need to replace tao::pegtl::seq<R...>
with interleaved<seps, R...>
, but you could even make a separate alias for that:
template<typename... Rules>
using sseq = interleaved<seps, Rules...>;
// Now you only have to replace tao::pegtl::seq with sseq
struct enum_decl : sseq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> {};
This strategy is mostly needed not because of whitespace-significant languages but because you don't have an earlier tokenizer step in the parsing that would separate the enum
from the thing
. Another strategy would be to implement that first, and do the later-stage parsing based on a stream of tokens instead of a stream of characters, but that is a larger rewrite and has its own drawbacks.
Note: I haven't compiled the code, but the point here should be the strategy. Please leave a comment if there is a compile error (I hope I haven't messed up the variadic templates) or if something is unclear.
Note 2: Also you probably don't want to replace every
seq
withsseq
. For example, if you have the logical and (&&
) defined asseq<one<'&'>, one<'&'>>
, that's something you probably don't want to change.
I thought about an approach, though couldn't express it correctly or my idea wouldn't work to begin with; make a class template that surrounds given rule (as template argument) withstar<space>
. I'll try your idea, and see how it works. Thanks!
– user1150609
Nov 22 '18 at 13:37
That's pegtl::pad<Rule, SepL, SepR> but you probably don't want that because you have to replace each element of everyseq
instead of just theseq
s.
– palotasb
Nov 22 '18 at 13:50
add a comment |
I don't think there is a shortcut, you have to specify the grammar in a way that makes it unambiguous where and how many whitespaces you allow.
The best way to do it I think is to add a convenience rule template that allows matching a list (tao::pegtl::seq
) of rules separated by any allowed separater (generally whitespace plus comments).
struct comment : tao::pegtl::disable< /* whatever your comment syntax is */ > {};
struct separator : tao::pegtl::sor< tao::pegtl::ascii::space, comment > {}; // either/or
struct seps : tao::pegtl::star< separator > {}; // Any separators, whitespace or comments
// Template to generate rule
// tao::pegtl::seq<Rule0, Separator, Rule1, Separator, Rule2, ... , Separator, RuleN>
template <typename Separator, typename... Rules>
struct interleaved;
template <typename Separator, typename Rule0, typename... RulesRest>
struct interleaved<Separator, Rule0, RulesRest...>
: tao::pegtl::seq<Rule0, Separator, interleaved<Separator, RulesRest...>> {};
template <typename Separator, typename Rule0>
struct interleaved<Separator, Rule0>
: Rule0 {};
// Note: interleaved<Separator /*, no Rule! */> intentionally not defined.
struct enum_decl : interleaved<seps, kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'> {};
// Expands to:
seq<kw_enum, seps, interleaved<seps, identifier, one<';'>>> ==
seq<kw_enum, seps, seq<identifier, seps, interleaved<seps, one<';'>>>> ==
seq<kw_enum, seps, seq<identifier, seps, one<';'>> ==
seq<kw_enum, seps, identifier, seps, one<';'>>
Basically, doing something like the above, you only need to replace tao::pegtl::seq<R...>
with interleaved<seps, R...>
, but you could even make a separate alias for that:
template<typename... Rules>
using sseq = interleaved<seps, Rules...>;
// Now you only have to replace tao::pegtl::seq with sseq
struct enum_decl : sseq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> {};
This strategy is mostly needed not because of whitespace-significant languages but because you don't have an earlier tokenizer step in the parsing that would separate the enum
from the thing
. Another strategy would be to implement that first, and do the later-stage parsing based on a stream of tokens instead of a stream of characters, but that is a larger rewrite and has its own drawbacks.
Note: I haven't compiled the code, but the point here should be the strategy. Please leave a comment if there is a compile error (I hope I haven't messed up the variadic templates) or if something is unclear.
Note 2: Also you probably don't want to replace every
seq
withsseq
. For example, if you have the logical and (&&
) defined asseq<one<'&'>, one<'&'>>
, that's something you probably don't want to change.
I thought about an approach, though couldn't express it correctly or my idea wouldn't work to begin with; make a class template that surrounds given rule (as template argument) withstar<space>
. I'll try your idea, and see how it works. Thanks!
– user1150609
Nov 22 '18 at 13:37
That's pegtl::pad<Rule, SepL, SepR> but you probably don't want that because you have to replace each element of everyseq
instead of just theseq
s.
– palotasb
Nov 22 '18 at 13:50
add a comment |
I don't think there is a shortcut, you have to specify the grammar in a way that makes it unambiguous where and how many whitespaces you allow.
The best way to do it I think is to add a convenience rule template that allows matching a list (tao::pegtl::seq
) of rules separated by any allowed separater (generally whitespace plus comments).
struct comment : tao::pegtl::disable< /* whatever your comment syntax is */ > {};
struct separator : tao::pegtl::sor< tao::pegtl::ascii::space, comment > {}; // either/or
struct seps : tao::pegtl::star< separator > {}; // Any separators, whitespace or comments
// Template to generate rule
// tao::pegtl::seq<Rule0, Separator, Rule1, Separator, Rule2, ... , Separator, RuleN>
template <typename Separator, typename... Rules>
struct interleaved;
template <typename Separator, typename Rule0, typename... RulesRest>
struct interleaved<Separator, Rule0, RulesRest...>
: tao::pegtl::seq<Rule0, Separator, interleaved<Separator, RulesRest...>> {};
template <typename Separator, typename Rule0>
struct interleaved<Separator, Rule0>
: Rule0 {};
// Note: interleaved<Separator /*, no Rule! */> intentionally not defined.
struct enum_decl : interleaved<seps, kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'> {};
// Expands to:
seq<kw_enum, seps, interleaved<seps, identifier, one<';'>>> ==
seq<kw_enum, seps, seq<identifier, seps, interleaved<seps, one<';'>>>> ==
seq<kw_enum, seps, seq<identifier, seps, one<';'>> ==
seq<kw_enum, seps, identifier, seps, one<';'>>
Basically, doing something like the above, you only need to replace tao::pegtl::seq<R...>
with interleaved<seps, R...>
, but you could even make a separate alias for that:
template<typename... Rules>
using sseq = interleaved<seps, Rules...>;
// Now you only have to replace tao::pegtl::seq with sseq
struct enum_decl : sseq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> {};
This strategy is mostly needed not because of whitespace-significant languages but because you don't have an earlier tokenizer step in the parsing that would separate the enum
from the thing
. Another strategy would be to implement that first, and do the later-stage parsing based on a stream of tokens instead of a stream of characters, but that is a larger rewrite and has its own drawbacks.
Note: I haven't compiled the code, but the point here should be the strategy. Please leave a comment if there is a compile error (I hope I haven't messed up the variadic templates) or if something is unclear.
Note 2: Also you probably don't want to replace every
seq
withsseq
. For example, if you have the logical and (&&
) defined asseq<one<'&'>, one<'&'>>
, that's something you probably don't want to change.
I don't think there is a shortcut, you have to specify the grammar in a way that makes it unambiguous where and how many whitespaces you allow.
The best way to do it I think is to add a convenience rule template that allows matching a list (tao::pegtl::seq
) of rules separated by any allowed separater (generally whitespace plus comments).
struct comment : tao::pegtl::disable< /* whatever your comment syntax is */ > {};
struct separator : tao::pegtl::sor< tao::pegtl::ascii::space, comment > {}; // either/or
struct seps : tao::pegtl::star< separator > {}; // Any separators, whitespace or comments
// Template to generate rule
// tao::pegtl::seq<Rule0, Separator, Rule1, Separator, Rule2, ... , Separator, RuleN>
template <typename Separator, typename... Rules>
struct interleaved;
template <typename Separator, typename Rule0, typename... RulesRest>
struct interleaved<Separator, Rule0, RulesRest...>
: tao::pegtl::seq<Rule0, Separator, interleaved<Separator, RulesRest...>> {};
template <typename Separator, typename Rule0>
struct interleaved<Separator, Rule0>
: Rule0 {};
// Note: interleaved<Separator /*, no Rule! */> intentionally not defined.
struct enum_decl : interleaved<seps, kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'> {};
// Expands to:
seq<kw_enum, seps, interleaved<seps, identifier, one<';'>>> ==
seq<kw_enum, seps, seq<identifier, seps, interleaved<seps, one<';'>>>> ==
seq<kw_enum, seps, seq<identifier, seps, one<';'>> ==
seq<kw_enum, seps, identifier, seps, one<';'>>
Basically, doing something like the above, you only need to replace tao::pegtl::seq<R...>
with interleaved<seps, R...>
, but you could even make a separate alias for that:
template<typename... Rules>
using sseq = interleaved<seps, Rules...>;
// Now you only have to replace tao::pegtl::seq with sseq
struct enum_decl : sseq<kw_enum, tao::pegtl::identifier, tao::pegtl::one<';'>> {};
This strategy is mostly needed not because of whitespace-significant languages but because you don't have an earlier tokenizer step in the parsing that would separate the enum
from the thing
. Another strategy would be to implement that first, and do the later-stage parsing based on a stream of tokens instead of a stream of characters, but that is a larger rewrite and has its own drawbacks.
Note: I haven't compiled the code, but the point here should be the strategy. Please leave a comment if there is a compile error (I hope I haven't messed up the variadic templates) or if something is unclear.
Note 2: Also you probably don't want to replace every
seq
withsseq
. For example, if you have the logical and (&&
) defined asseq<one<'&'>, one<'&'>>
, that's something you probably don't want to change.
answered Nov 22 '18 at 12:04
palotasbpalotasb
2,37111420
2,37111420
I thought about an approach, though couldn't express it correctly or my idea wouldn't work to begin with; make a class template that surrounds given rule (as template argument) withstar<space>
. I'll try your idea, and see how it works. Thanks!
– user1150609
Nov 22 '18 at 13:37
That's pegtl::pad<Rule, SepL, SepR> but you probably don't want that because you have to replace each element of everyseq
instead of just theseq
s.
– palotasb
Nov 22 '18 at 13:50
add a comment |
I thought about an approach, though couldn't express it correctly or my idea wouldn't work to begin with; make a class template that surrounds given rule (as template argument) withstar<space>
. I'll try your idea, and see how it works. Thanks!
– user1150609
Nov 22 '18 at 13:37
That's pegtl::pad<Rule, SepL, SepR> but you probably don't want that because you have to replace each element of everyseq
instead of just theseq
s.
– palotasb
Nov 22 '18 at 13:50
I thought about an approach, though couldn't express it correctly or my idea wouldn't work to begin with; make a class template that surrounds given rule (as template argument) with
star<space>
. I'll try your idea, and see how it works. Thanks!– user1150609
Nov 22 '18 at 13:37
I thought about an approach, though couldn't express it correctly or my idea wouldn't work to begin with; make a class template that surrounds given rule (as template argument) with
star<space>
. I'll try your idea, and see how it works. Thanks!– user1150609
Nov 22 '18 at 13:37
That's pegtl::pad<Rule, SepL, SepR> but you probably don't want that because you have to replace each element of every
seq
instead of just the seq
s.– palotasb
Nov 22 '18 at 13:50
That's pegtl::pad<Rule, SepL, SepR> but you probably don't want that because you have to replace each element of every
seq
instead of just the seq
s.– palotasb
Nov 22 '18 at 13:50
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53427551%2fpegtl-how-to-skip-spaces-for-the-entire-grammar%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown