diff --git a/doc/user_guide.md b/doc/user_guide.md index 3645707..6ddc251 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -325,9 +325,16 @@ Regular expressions can include many special characters: * The `(` character begins a matching group. * The `{` character begins a count qualifier. * The `\` character escapes the following character and changes its meaning: + * The `\a` sequence matches an ASCII bell character (0x07). + * The `\b` sequence matches an ASCII backspace character (0x08). * The `\d` sequence matches any character `0` through `9`. + * The `\f` sequence matches an ASCII form feed character (0x0C). + * The `\n` sequence matches an ASCII new line character (0x0A). + * The `\r` sequence matches an ASCII carriage return character (0x0D). * The `\s` sequence matches a space, horizontal tab `\t`, carriage return `\r`, a form feed `\f`, or a vertical tab `\v` character. + * The `\t` sequence matches an ASCII tab character (0x09). + * The `\v` sequence matches an ASCII vertical tab character (0x0B). * Any other character matches itself. * The `|` character creates an alternate match. diff --git a/lib/propane/regex.rb b/lib/propane/regex.rb index cd1fdae..cca029f 100644 --- a/lib/propane/regex.rb +++ b/lib/propane/regex.rb @@ -134,8 +134,18 @@ class Propane else c = @pattern.slice!(0) case c + when "a" + CharacterRangeUnit.new("\a", "\a") + when "b" + CharacterRangeUnit.new("\b", "\b") when "d" CharacterRangeUnit.new("0", "9") + when "f" + CharacterRangeUnit.new("\f", "\f") + when "n" + CharacterRangeUnit.new("\n", "\n") + when "r" + CharacterRangeUnit.new("\r", "\r") when "s" ccu = CharacterClassUnit.new ccu << CharacterRangeUnit.new(" ") @@ -145,6 +155,10 @@ class Propane ccu << CharacterRangeUnit.new("\f") ccu << CharacterRangeUnit.new("\v") ccu + when "t" + CharacterRangeUnit.new("\t", "\t") + when "v" + CharacterRangeUnit.new("\v", "\v") else CharacterRangeUnit.new(c) end diff --git a/spec/propane_spec.rb b/spec/propane_spec.rb index bd5f298..bd365bf 100644 --- a/spec/propane_spec.rb +++ b/spec/propane_spec.rb @@ -768,6 +768,89 @@ EOF expect(results.stderr).to eq "" expect(results.status).to eq 0 end + + it "matches backslash escape sequences" do + case language + when "c" + write_grammar < +>> +tokenid t; +/\\a/ << + printf("A\\n"); +>> +/\\b/ << + printf("B\\n"); +>> +/\\t/ << + printf("T\\n"); +>> +/\\n/ << + printf("N\\n"); +>> +/\\v/ << + printf("V\\n"); +>> +/\\f/ << + printf("F\\n"); +>> +/\\r/ << + printf("R\\n"); +>> +/t/ << + return $token(t); +>> +Start -> t; +EOF + when "d" + write_grammar <> +tokenid t; +/\\a/ << + writeln("A"); +>> +/\\b/ << + writeln("B"); +>> +/\\t/ << + writeln("T"); +>> +/\\n/ << + writeln("N"); +>> +/\\v/ << + writeln("V"); +>> +/\\f/ << + writeln("F"); +>> +/\\r/ << + writeln("R"); +>> +/t/ << + return $token(t); +>> +Start -> t; +EOF + end + build_parser(language: language) + compile("spec/test_match_backslashes.#{language}", language: language) + results = run + expect(results.stderr).to eq "" + expect(results.status).to eq 0 + verify_lines(results.stdout, [ + "A", + "B", + "T", + "N", + "V", + "F", + "R", + ]) + end end end end diff --git a/spec/test_match_backslashes.c b/spec/test_match_backslashes.c new file mode 100644 index 0000000..9c08216 --- /dev/null +++ b/spec/test_match_backslashes.c @@ -0,0 +1,13 @@ +#include "testparser.h" +#include +#include + +int main() +{ + char const * input = "\a\b\t\n\v\f\rt"; + p_context_t context; + p_context_init(&context, (uint8_t const *)input, strlen(input)); + assert(p_parse(&context) == P_SUCCESS); + + return 0; +} diff --git a/spec/test_match_backslashes.d b/spec/test_match_backslashes.d new file mode 100644 index 0000000..ea630ef --- /dev/null +++ b/spec/test_match_backslashes.d @@ -0,0 +1,15 @@ +import testparser; +import std.stdio; + +int main() +{ + return 0; +} + +unittest +{ + string input = "\a\b\t\n\v\f\rt"; + p_context_t context; + p_context_init(&context, input); + assert(p_parse(&context) == P_SUCCESS); +}