A human way to define regular expressions in Ruby
krainboltgreene.github.io
krainboltgreene.github.io
Here's the subset of regular expressions that has gotten me through nearly all of the regular expressions I've ever needed to write. As a plus, it has no dependencies!
* - zero or more of the preceding character/group
+ - one or more of the preceding character/group
? - zero or one of the preceding character/group
$ - end of line
^ - beginning of line
. - one of any one character
\ - escape the following character (for a literal '$' or '.', for example)
[<some characters>] - one of the given characters
[a-zA-Z0-9] - letters and numbers inside a group can have ranges!
(<something>) - capturing group (anything that matches inside it will be accessible in the match object)
<thing1>|<thing2> - either the first thing, or the second thing (or the third, or the fourth...)
This isn't a complete, or even precise, definition, but knowing those things will get you to the point where you can read and write expressions like this:
^(-|+)?[0-9]*\.[0-9]+$
which matches things like -.2, 0.123, +0.1, etc. (floating point numbers, basically). This likely has bugs, since I haven't tested it ;)
If we could come up with some sort of high-level API for regexes we might end up in a better situation, given that this would make them effectively language agnostic.
And you could always revert to your platform's native option if there are some missing features on the API (Given that those type of things generally end up with a least-common-denominator approach)
I've generally found that my practice is basically "try regex; if it doesn't work after ten minutes of effort, abandon regex completely".
I'm not sure the "builder pattern" in the OP does this well though...
Macros! https://github.com/wyantb/js-regex#macros
Given some named macro, whenever the user wants to use it, they use addMacro (or some analogous method).
I'm a huge fan of regular expressions and I love to discover new ones, try to improve my own, etc. But, it does not mean there isn't place for a human readable regex syntax, and I will definitely give it a try.
I only use regex occasionally and, as such, typically need to refresh each time I go back to it.
Obviously you should choose the level of abstraction that fits your needs but I would love something like this if I did any ruby.
The fact you can't come up with a regex and be sure it's correct answers your first two questions.
Regexes are incredibly useful, but the syntax is just a bunch of noise. This library turns it into self-docummenting code, which is incredibly valuable. Novices might prefer showing their regex-fu, but after you've spent a good chunk of your life reading and writing code, you value clear code more.
He's arguing that the nicer syntax is a valuable abstraction over regex's overly-complicated syntax, not that the syntax will be 100% the same as regex 100% of the time.
Apparently, we can't. So, you really want to improve your odds of writing correct code at the first attempt by making the language work with you rather than against.
Replacing a bunch punctuation with code that reads like prose is one way.
The problem is that for anyone who knows how regexps work, this library provides an obfuscation layer. I personally prefer the free-spacing regexp support[1] found in a number of popular parsers. It lets you write a complicated regexp in small and/or indented chunks across multiple lines, including comments. This provides as much or more documentation than this "self-documenting" form and allows the full power of the regexp engine (which big regexps often seem to need). I've occasionally had to work out some pretty gnarly regexps, but when written in two-column code/comments form they end up being pretty understandable and maintainable.
[1] Example from the Ruby docs. IIRC, Python and a few other parsers have similar support: http://www.ruby-doc.org/core-1.9.3/Regexp.html#label-Free-Sp...
Not unnecessary, “useless”, “kidding” are more appropriate words IMHO.
Where is named captures? Oh, you cannot. Well, ordinary $1 $2 $3? Oh, you cannot capture at all! How do you use back refs? Oh, you cannot? Wait, why the heck then you even need regexps?
There are problems with complex regexes, yes. But this silly toy is not the solution. For solutions look at Perl6's “regexps”, Perl6's Grammars (example down the thread), Perl5's Regexp::Grammars, look how Haskell do it with parser combinators. They are all light years ahead.
But yes, 1.2 will likely have named captures and back refs. It's a regex, so $1 etc come with turning it into a regex and doing matching.
Perl5's R::Grammars is doing it the same way you do — it generates one-big-scary regex. But this lib is nowhere close to what it can do.
For idea: it will provide fully parsed struct like {proto: 'http', domain: { all: 'www.exampe.com', subdomains: ['www','example'], tld: 'ord', ... }
So, no, you cannot. And there're many more cool things you cannot
> Am I the only person who thinks that things like this are totally unnecessary?
No, it's been made clear to me that my library is totally unnecessary by quite a few developers. I like it though.
> ^(-|+)?[0-9]*\.[0-9]+$
exp.float, and you only have to define it once.
Regular expressions are a pain, but I strongly believe that you shouldn't use abstractions unless you have at least a functional understanding of what you're abstracting.
No, you're not the only one. I have a feeling that things like that massive regular expression that is meant to match RFC822 email addresses is part of the reason for the focus on this kind of "easy to use, human" regular expression interfaces. The problem is that that email matching regular expression is an extreme case that matches a whole bunch of things that MTAs/MUAs need to know about, but that most people, who are accepting email addresses in web forms, don't need to know about, like "email comments", the real name fields, bang paths, etc. 99.999% of email addresses are going to match something simple like ^\S+@[\w-.]+\.[\w-]+$, and you're going to pass that off to the MTA to parse and validate and bounce anyway. For example, most people should be more concerned with having decent bounce processing support rather than trying to make email address matching regular expressions.
We should start treating regex like a computer language in its own right, rather than some second-hand citizen that we stuff into a single line without any delimiting whitespace. Use the /x switch and comment your code as you would with any other programming language, and you'll find that regex really isn't that scary.
Take the example pattern. The regex written by a human might look something like so:
m{^
https? :// # Protocol
(?: \w+ \. )? # Subdomain
([\w\-]+) \. # Domain
(?: com | org ) # TLD
/?
$}x
It's certainly easier to parse. Debugging it is also a lot easier. We can see that this won't match anything with more than one subdomain. We can also see that it won't match subdomains that have hyphens in them. It also looks at a glance much more like a URL and less like some arbitrary Ruby code.What, like, perl6?
grammar URL {
rule TOP { <proto> '://' <domain> '/' }
token proto { 'http' 's'? }
token subdom { \w+ }
rule domain { <.subdom> [ '.' <.domain> ]? }
}
say URL.parse('http://lelf.lu/');
Betty:hacks lelf$ perl6 gr.p6
「http://lelf.lu/」
proto => 「http」
domain => 「lelf.lu」Here is direct link to grammar module - https://github.com/ihrd/uri/blob/master/lib/IETF/RFC_Grammar...
I have no trouble writing them. What I have trouble doing, when it comes to very complicated regexes, is _reading_ what I've already written a couple months ago (myself! I pity those who aren't me even more). And even more so, when it comes to complicated regexen, _modifying_ what I've already written to fix a bug or change it slightly for new requirements.
A complicated regex meets the requirements for the old joke about 'write-only languages.' Somewhat ameliorated if you use the Perl-originated conventions for insignificant whitespace in a regex, which I _think_ ruby does too now, but I am not sure, cause I never use it, cause I can never remember how it works and never bother to look it up (have you tried looking up docs on ruby regexes on the web? good luck! I have no idea _where_ the official docs, if any English exist, are.)
I have no trouble writing regexes. I think every programmer should be comfortable doing so. I think using this tool without understanding regex basics first is a mistake. And I _still_ think this is a kinda neat and potentially useful tool.
m{^
https? :// # Protocol
(?: \w+ \. )? # Subdomain
([\w\-]+) \. # Domain
(?: com | org ) # TLD
/?
$}x
Isn't really any different from hex {starts
protocol
subdomain
domain
tld
maybe("?")
ends} Protocol = 'https?://'
Subdomain = '\w+'
Domain = '[-\w]+'
TLD = 'com|org'
Re = WholeTextMatches(
Protocol + Optional( Subdomain + '.')
+ Capture(Domain) + TLD)
Typically, I wouldn't go that far, but storing separate parts into well-named strings before assembling the entire regex, IMO, makes things much more readable.That said, this also makes a huge tradeoff against conciseness. Personally, I'd prefer
# Is foo a valid klingon email address?
foo =~ /gibberish/
Over foo = Hexpress.new.
start('g').
maybe('i').
many('b').
find('b').
... %r{
^
# foo
bar
$
}I did a little research and this derives from Verbal Expressions, as explained [1] and implemented [2]. In any case, I'm emphatically in favor. I'm the sort of programmer that needs to write maybe one regex per month, and I'm tired as hell of relearning the human-meaningless syntax for regexs, let alone all the little variations between languages. Not to mention, if it can vastly reduce the number of symbols I have to escape when matching special symbols, so much the better.
[1] http://thechangelog.com/stop-writing-regular-expressions-exp... [2] http://verbalexpressions.github.io/
It almost feels like what high level programming languages are to assembly code.
Sure, we can (and should) learn Regex constructs, but it is undeniable that this library provides an unmatched level of clarity.
Kudos to Krain for coming up with such an elegant solution to the issue of Regex building/reading.
I don't find:
find { matching { [word, "-"] }.multiple }
Clearer than: ([\w\-]+)
It might be an alternative way introduce regexes though.I would also like to see a more complex regex written this way.
Here's is OP example ported to parse:
; build some sub rules
r: context [
domain: charset [#"a" - #"z" #"-"]
proto: ["http" | "https"]
tld: ["com" | "org"]
]
; simple URI rule
rule: [
copy proto r/proto
"://"
some [copy d some r/domain #"." (append domain d)]
copy tld r/tld
["/" | end]
]
; capture
proto: tld: d: none
domain: []
; usage example
parse "http://www.example.com/" rule
; Above returns true with following variables set to...
;
; proto = "http"
; domain = ["www" "example"]
; tld = "com"
Here's a nice tutorial on parse (though it's probably Rebol 2 centric) - http://www.codeconscious.com/rebol/parse-tutorial.htmlOnce that fact changes you will find yourself writing and staring at walls of text.
You will do this enough number of times, then only hope that you have a more succinct and powerful way of expressing such a idiom, you will do all that only to realize using regular expressions are the only way to solving a range problems which it was designed to solve.
The easiest analogy I can give you math. Prior to manipulation of symbols, math was pretty much text. The whole of math looked paragraphs of puzzles and word play. Worked fine when you want to do small things like postulates, axioms and a few things derived from that. To move a higher level of abstraction and interplay of concepts we had to get into symbols.
This is something similar.
a = re.compile(r"""\d + # the integral part
\. # the decimal point
\d * # some fractional digits""", re.X)
Instead of a = re.compile(r"\d+\.\d*")
But that is as far as it goes for me. From that point, masking REGEX with additional layer of 10's of functions is not a wise move.eg.
/\d+ # some comment
\w # another comment
/xThat said, regexes can be tricky to parse and making them clearer to the average person is a worthy goal.
Not that I'm saying that newbie shouldn't learn regex. But any level of abstraction would be great. Rails abstract the complexity of web development. Hexpress abstract the complexity of regex. Both are win in my book.
If there isn't isomorphism between the traditional symbols and the new names, then the expressions will be limited in expressiveness.
#word is (\w+). This works in english, but breaks very early. Still, it might be correct in some time. "word" is extremely context-sensitive.
Seems like in the 'unreadable' regex case, you'll have a boatload of verbose function calls to construct it.
I want to share patterns without having to do much.
Why would that be better? I recently saw Bret Victor's Inventing on Principle talk on Vimeo, and one of the things he mentions that I really agree with is that most of us must 'think like computers' to understand what our code will do. The less we have to do this, the better, because we're terrible computers
Now am I likely to use something like this? No, mostly because I don't program in ruby TOO often, and I am pretty well aware of how to use regular expressions.
I'd imagine that wrappers such as this one do not enable users to create any more complex patterns, than ones they could make with a most basic understanding of regular expressions. Whilst providing neither a base for more complex regex use nor a community to further an understanding of regex.
Also, this.