{"id":15561041,"url":"https://github.com/famished-tiger/srl-ruby","last_synced_at":"2025-04-23T21:47:19.070Z","repository":{"id":59156219,"uuid":"123676730","full_name":"famished-tiger/SRL-Ruby","owner":"famished-tiger","description":"Simple Regex Language library and compiler in Ruby","archived":false,"fork":false,"pushed_at":"2025-03-20T05:15:04.000Z","size":240,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-23T21:46:54.429Z","etag":null,"topics":["regex-language","regular-expression","ruby","srl"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/famished-tiger.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-03-03T09:12:47.000Z","updated_at":"2025-03-20T05:15:08.000Z","dependencies_parsed_at":"2025-03-06T17:35:02.353Z","dependency_job_id":"5bf6a52a-77d9-47a1-9664-c7d5ee28df44","html_url":"https://github.com/famished-tiger/SRL-Ruby","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famished-tiger%2FSRL-Ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famished-tiger%2FSRL-Ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famished-tiger%2FSRL-Ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famished-tiger%2FSRL-Ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/famished-tiger","download_url":"https://codeload.github.com/famished-tiger/SRL-Ruby/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250522292,"owners_count":21444509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["regex-language","regular-expression","ruby","srl"],"created_at":"2024-10-02T16:05:01.693Z","updated_at":"2025-04-23T21:47:19.023Z","avatar_url":"https://github.com/famished-tiger.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# srl_ruby\n[![Linux Build Status](https://travis-ci.org/famished-tiger/SRL-Ruby.svg?branch=master)](https://travis-ci.org/famished-tiger/SRL-Ruby)\n[![Build status](https://ci.appveyor.com/api/projects/status/l5adgcbfo128rvo9?svg=true)](https://ci.appveyor.com/project/famished-tiger/srl-ruby)\n[![Gem Version](https://badge.fury.io/rb/srl_ruby.svg)](https://badge.fury.io/rb/srl_ruby)\n[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/famished-tiger/SRL-Ruby/blob/master/LICENSE.txt)\n\n\nWelcome to the first Ruby implementation of a [Simple Regex Language](https://simple-regex.com) (SRL) parser and compiler.  \nIt allows you to write __highly-readable__ text patterns in SRL and then generate their Ruby `Regexp` counterparts.  \nEver wanted to write challenging regular expressions but were intimided by their arcane, cryptic syntax?  \nWith **srl_ruby** you can easily design your patterns in SRL and let *srl_ruby* transform them into terse `Regexp`.\n\n### Features:\n- Command-line SRL-to-Ruby compiler with customizable output.\n- Ruby API for integrating a SRL parser or compiler with your code.\n- 100% pure Ruby with clean design (_not a port from some other language_)\n- Minimal runtime dependency ([Rley](https://rubygems.org/gems/rley) gem). Won't drag a bunch of gems...\n- Compatibility: works with Ruby 2.3+ MRI, JRuby\n- Portability: tested on both Linux and Windows,...\n\n## Installation\n\n### ...with Bundler\nAdd this line to your application's Gemfile:\n\n    gem 'srl_ruby'\n\nAnd then execute:\n\n    $ bundle\n\n### ...with Rubygem\nOr install it directly yourself with the command line:\n\n    $ gem install srl_ruby\n\n## Usage\nLet's test the installation by launching the __srl2ruby__ (SRL-to-Ruby) command-line compiler with the help option:\n\n    $ srl2ruby --help\n\nIt should output something similar to:\n\n```\nUsage: srl2ruby SRL_FILE [options]\n\nDescription:\n  Parses a SRL file and compiles it into a Ruby Regexp literal.\n  Simple Regex Language (SRL) website: https://simple-regex.com\n\nExamples:\n  srl2ruby example.srl\n  srl2ruby -p 'begin with literally \"Hello world!\"'\n  srl2ruby example.srl -o example_re.rb -t srl_and_ruby.erb\n\nOptions:\n    -p, --pattern SRL_PATT           One-liner SRL pattern.\n    -o, --output-file PATH           Output to a file with specified name.\n    -t, --template-file TEMPLATE     Use given ERB template for the Ruby code generation. srl2ruby looks for\nthe template file in current dir first then in its gem's /templates dir.\n\n        --version                    Display the program version then quit.\n    -?, -h, --help                   Display this help then quit.\n```\n\n## A quick intro to SRL and srl2ruby\n### What is SRL?\nSRL is a small language that lets you write pattern matching expressions\nin a readable syntax that bears some resemblance with English.\nFor SRL documentation and examples, we cannot but recommend you to jump to the [official SRL website](https://simple-regex.com).\n\n### Why SRL?\nIt is a well-known fact: regexes can be really hard to write and even harder to read ('decipher' verb is closer to reality).  \nAlas, the path of creating and maintaining regexes can be full of frustration.\n\nThere comes SRL. The intent is to let developers define self-documenting patterns with an easy syntax.\nAnd then let your computer translate SRL expressions into terse regular expressions.\n\n### Our first SRL pattern\nLet's succumb to the traditional 'hello world' example. True, it is a contrived example that doesn't make justice to SRL expressiveness. On the other hand, it is a starting point good enough to learn the compile cycle.\n\nAs a first step, let's create a file named `hello.srl` with just the following line:  \n```\nbegin with literally \"Hello world!\"\n```\n\nIt should read as 'Match any text that begins with the exact text \"Hello world!\"'  \nNow, if one invokes the `srl2ruby` compiler with the command line...\n\n    $ srl2ruby hello.srl\n\n\n... one gets the following output:\n```\nParsing file 'hello.srl'\n/^Hello world!/\n```\n\nThe last displayed line is the Ruby `Regexp` representation of the above SRL line. It can be pasted as such in your Ruby code, like in the following Ruby snippet:\n```ruby\nsubject = 'Hello world! Welcome to SRL...'\nputs 'It matches!' if subject =~ /^Hello world!/  \n```\n\nAs expected the snippet results in the message:\n```\nIt matches!\n```\n\n**Quick recap:**  \n- `srl2ruby` expects a SRL file (typically with a .srl extension)  \n- It parses the _Simple Regex Language_ content...  \n- ... then generates the `Regexp` that is equivalent to the SRL input  \n- Finally, it prints the results to the console.\n\nFeature: with the command-line `-o` option the compiler will send the output to a file with specified name.  \n\n\n### Gears up\nLet's admit it, our first example wasn't really impressive.  \nSo, let's try with a more imposing example inspired from the [official SRL website](https://simple-regex.com): an email validation pattern.\n\n```\nbegin with any of (digit, letter, one of \"._%+-\") once or more,\nliterally \"@\",\nany of (digit, letter, one of \".-\") once or more,\n( literally \".\",\n  letter at least 2 times\n) optional,\nmust end,\ncase insensitive\n```\n\nAssume that the previous SRL pattern was put in a file named `email_validation.srl` and that we invoked `srl2ruby` with the following command-line:\n\n    $ srl2ruby email_validation.srl\n\nThen the output should be:\n```\nParsing file 'email_validation.srl'\n/(?i-mx:^(?:\\d|[a-z]|[._%+\\-])+@(?:\\d|[a-z]|[.\\-])+(?:\\.[a-z]{2,})?$)/\n```\n\nThe resulting regexp isn't for the fainted hearts: who's ready to maintain it? In addition, the above pattern covers only the most frequent cases.  \nIf you were asked to cover more exotic cases, and knowing that it means an expression at least twice as complex, which version are you willing to update the SRL or the Regexp one?\n\n#### Good to know: customizable output\nIn fact, if one wants to update or maintain a pattern, it would be practical to have the SRL expression and its equivalent Regexp next to each other in our Ruby source code.  \nCan the `srl2ruby` compiler help there?  The answer is ... yes.  \nFirst, it is good to know that the output of the `srl2ruby` compiler can be tailored with an ERB template. For instance, the output of all the previous examples is relying on a default template called `base.erb`. It is bundled in the `srl_ruby` gem as another template called `srl_and_ruby.erb`. This second template will emit the SRL code (in Ruby comments) followed by the Regexp literal.  \nSo let's use it with our email validation example:\n\n    $ srl2ruby email_validation.srl --template-file srl_and_ruby.erb\n\nThe shorter option `-t` syntax is also possible:\n\n    $ srl2ruby email_validation.srl -t srl_and_ruby.erb\n\nThe compiler's output contains now the original SRL expression in comments:  \n\n```\nParsing file 'email_validation.srl'\n# SRL expression follows:\n# begin with any of (digit, letter, one of \"._%+-\") once or more,\n# literally \"@\",\n# any of (digit, letter, one of \".-\") once or more,\n# ( literally \".\",\n#   letter at least 2 times\n# ) optional,\n# must end,\n# case insensitive\n#\n# ... and its Regexp equivalent:\n/(?i-mx:^(?:\\d|[a-z]|[._%+\\-])+@(?:\\d|[a-z]|[.\\-])+(?:\\.[a-z]{2,})?$)/\n```\nThe above SRL code in comments can be safely inserted in a Ruby file.\n\n**Quick recap:**  \n- SRL can be used to specify much more challenging patterns than our boring 'Hello world!'.  \n- The `srl2ruby` compiler uses a ERB template to format its output.  \n- It is possible to choose a specific template via the `-t` option.  \n\n_Feature_: When given the name of a template via the `-t` option, the compiler will look first for such a template in the current directory, then, if not found, in its `templates` directory. This gives the opportunity to use customized local template files.  \n\n## Time for yet another example\n\nAs an example, let's assume that we are asked to create a regular expression that matches the time in 12 hour clock format (say, _hh:mm AM/PM_).\nIn addition, the hour and minute values must be put (= captured) in a variable named `hour` and `min` respectively.  \n\nWe will proceed in multiple iterations of increasing complexity.  \nHowever, for those that are always in a hurry and like movie spoils, here is the requested `Regexp`:\n```ruby\n/(?i-mx:^(?\u003chour\u003e(?:(?:0?\\d)|(?:1[01]))):(?\u003cmin\u003e(?:0?|[1-5])\\d)\\s?[AP]M$)/\n```\nWant to jump directly to the latest [iteration](#iteration-5)?...\n\n\n### Iteration 1\nHere is a very naive SRL expression that matches the requested time format:\n```\nbegin with digit twice,\nliterally \":\",\ndigit twice\nliterally \" \",\none of \"AP\", literally \"M\",\nmust end\n```\n\nIf one compiles the above SRL expression with `srl2ruby` as explained earlier in ['Our first SRL pattern'](#our-first-srl-pattern) section, it will generate the following Regexp literal:\n```ruby\n/^\\d{2}:\\d{2} [AP]M$/\n```\n\nWhen I want to test regular expressions, one of my favorite tool is the\n[Rubular website](http://rubular.com/). Tom Lovitt created a great Regexp editor and tester specifically for the Ruby community.\n\nBy the way, perhaps some lynx-eyed readers spotted a small \"mistake\" on the third line of the SRL snippet: it doesn't end with a comma.  \nMy apologies... For style consistency this line should be written as:  \n```\ndigit twice,\n```\nIn reality, SRL happily ignores comma. Well..., most of the time. There is one exception: for the `any of` construct commas are used to separate alternatives (see example in [Iteration 3](#iteration-3)).\n\n### Iteration 2\nTests won't take a long time to show that the previous pattern is much too 'lenient' and will accept grossly incorrect entries such as 45:67 PM.  \n\nFor our next iteration, we keep note that:  \n- The first digit (from the left) can take the values 0 or 1 only.  \n- The third digit may run from 0 to 5 since the highest value for the minutes is 59.  \n\nHere is the improved SRL version:\n```\nbegin with digit from 0 to 1,\ndigit,\nliterally \":\",\ndigit from 0 to 5,\ndigit,\nliterally \" \",\none of \"AP\", literally \"M\"\nmust end\n```\n\n`srl2ruby` will swallow the SRL file and will spit out the next Regexp:\n```ruby\n/^[0-1]\\d:[0-5]\\d [AP]M$/\n```\n### Iteration 3\nErroneous values like 45:67 PM are no more accepted this time. That's definitively better...\nBut other tests will reveal that our pattern is still too permissive since it accepts values like 17:23 PM. A hour value of 17 is OK in 24 hour format but here we fail meeting our requirements...\n\nSo, for our third try, we keep note that:\n- If the first hour digit is 1, then the second digit can take the values 0 or 1 only.\n\nLet's refactor our pattern:\n```\nbegin with any of (\n  (literally \"0\", digit),\n  (literally \"1\", one of \"01\")\n)\nliterally \":\",\ndigit from 0 to 5,\ndigit,\nliterally \" \",\none of \"AP\", literally \"M\"\nmust end\n```\n\nRemarks:  \n- The indentation isn't required by SRL, but I find that it contributes to the readability...\n\n`srl2ruby` will transform this into:\n```ruby\n/^(?:(?:0\\d)|(?:1[01])):[0-5]\\d [AP]M$/\n```\n\n### Iteration 4\nThis time the pattern works correctly. But in the meantime, our customer changed his requirements (*of course, such things never happen in real life...*). He asks for more flexibility in the pattern:\n- If the most significant digit value is zero, it is optional (i.e. some clock models won't display it).\n- The space between the minute value and the AM/PM indicator is now optional.\n- The AM/PM indicator can sometimes be written in small letters (am/pm).\n\nLet's go for another tour:\n```\nbegin with any of (\n  (literally \"0\" optional, digit),\n  (literally \"1\", one of \"01\")\n)\nliterally \":\",\nany of (\n  literally \"0\" optional,\n  digit from 1 to 5\n),\ndigit,\nwhitespace optional,\none of \"AP\", literally \"M\"\nmust end,\ncase insensitive\n```\n\nHere is the Regexp counterpart generated by `srl2ruby`:\n```ruby\n/(?i-mx:^(?:(?:0?\\d)|(?:1[01])):(?:0?|[1-5])\\d\\s?[AP]M$)/\n```\n### Iteration 5\nAre we done? No: we were asked to capture the values of hours and minutes.\n\nSRL allows for named captures, so here is the updated version:\n```\nbegin with capture(\n  any of (\n    (literally \"0\" optional, digit),\n    (literally \"1\", one of \"01\")\n  )\n) as \"hour\",\nliterally \":\",\ncapture(\n  any of (\n    literally \"0\" optional,\n    digit from 1 to 5\n  ),\n  digit\n) as \"min\",\nwhitespace optional,\none of \"AP\", literally \"M\"\nmust end,\ncase insensitive\n```\n\n`srl2ruby` will swiftly swallow the above SRL pattern and generate the following Regexp:\n```ruby\n/(?i-mx:^(?\u003chour\u003e(?:(?:0?\\d)|(?:1[01]))):(?\u003cmin\u003e(?:0?|[1-5])\\d)\\s?[AP]M$)/\n```\n\nThat Regexp is becoming insane...\n\n\n#### Does this last Regexp really work?\nGlad you asked... Here is a Ruby snippet that can be used to test the last generated Regexp:\n\n```ruby\n# Next Regexp was copy-pasted from srl2ruby output\npattern = /(?i-mx:^(?\u003chour\u003e(?:(?:0?\\d)|(?:1[01]))):(?\u003cmin\u003e(?:0?|[1-5])\\d)\\s?[AP]M$)/\ntext = '1:43am'\n\nmatching = pattern.match(text)\nif matching\n  print 'Capture names: '; p(matching.names) # =\u003e Capture names: [\"hour\", \"min\"]\n  puts \"Value of 'hour': #{matching[:hour]}\" # =\u003e Value of 'hour': 1\n  puts \"Value of 'min': #{matching[:min]}\" # =\u003e Value of 'min': 43\nelse\n  puts \"Text '#{text}' doesn't match.\"\nend\n```\n\nRunning this snippet, gives the following output:\n```\nCapture names: [\"hour\", \"min\"]\nValue of 'hour': 1\nValue of 'min': 43\n```\nAs one can see, from the input '1:43am', the Regexp captured the hour and minute values in the appropriate capture variable. Mission accomplished...   \n\n## srl_ruby API\n\nThe method `SrlRuby#parse` accepts a Simple Regex Language string as input, and returns the corresponding regular expression as a `Regexp` instance.\n\nFor instance, the following snippet...  \n\n```ruby\nrequire 'srl_ruby' # Load srl_ruby library\n\n\n# Here is a multiline SRL expression that matches dates\n# in yyyy-mm-dd format\nsome_srl = \u003c\u003c-END_SRL\n  any of (literally \"19\", literally \"20\"), digit twice,\n  literally \"-\",\n  any of (\n    (literally \"0\", digit),\n    (literally \"1\", one of \"012\")\n  ),\n  literally \"-\",\n  any of (\n    (literally \"0\", digit),\n    (one of \"12\", digit),\n    (literally \"3\", one of \"01\")\n  )  \nEND_SRL\n\n# Next line launches the SRL parser, it returns the corresponding regex literal\nresult = SrlRuby.parse(some_srl)\n\nputs 'Equivalent regexp: /' + result + '/'\n```\n\n...  produces the following output:\n```\nEquivalent regexp: /(?:19|20)\\d{2}-(?:(?:0\\d)|(?:1[012]))-(?:(?:0\\d)|(?:[12]\\d)|(?:3[01]))/\n```\n\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/famished-tiger/SRL-Ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffamished-tiger%2Fsrl-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffamished-tiger%2Fsrl-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffamished-tiger%2Fsrl-ruby/lists"}