Skip to content

Instantly share code, notes, and snippets.

@tkruse
Created September 15, 2016 13:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tkruse/e07d9c5af6d3caad945f4715b130d407 to your computer and use it in GitHub Desktop.
Save tkruse/e07d9c5af6d3caad945f4715b130d407 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python
# PoC Ros2Names parsing using EBNF and Grako (install from pypi)
# Tested with grako 3.14.0
#
import grako
POSITIVES = "foo", "abc123", "_foo", "Foo", "BAR", "~", "foo/bar", "~/foo", "{foo}_bar", "foo/{ping}/bar", "foo/_/bar", "rosservice:///foo", "rostopic://foo/bar"
NEGATIVES = "123abc", "123", "__foo", "foo__bar", "foo bar", "foo__", "foo_", "foo//bar", "foo/", "~foo", "foo~", "foo~/bar", "foo/~bar", "/_/bar", "_", "_/_bar", "/~", "foo/~/bar", "{", "}", "}{"
TODO = "foo{x1}bar{x2}baz", "foo_/bar"
## Short EBNF Grako introduction
# [] optional element
# {} repetition
# (* *) comment
# | alternative (order matters, first are tried first)
# '' token
# / / regex?
grammar = """
uriname
=
[ urischeme ] pathname $
;
urischeme
=
namecharp '://'
;
pathname
=
['/' | '~/'] startpathsegment '/' { pathsegment '/' } endpathsegment
|
['/' | '~/'] singlename
|
'~'
;
(* must not start with digit *)
startpathsegment
=
/[_A-Za-z][_A-Za-z0-9]+/
|
/[_A-Za-z][_A-Za-z0-9]*/ '{' namecharx '}' namecharx
;
pathsegment
=
namecharp
|
namecharx '{' namecharx '}' namecharx
;
(* must not finish with _ *)
endpathsegment
=
/[_A-Za-z0-9]*[A-Za-z0-9]/
|
namecharx '{' /[_A-Za-z0-9]*/ '}' { /[_A-Za-z0-9]*[A-Za-z0-9]/ }
;
(* must not start with digit, nor finish with _ *)
singlename
=
/[_A-Za-z][_A-Za-z0-9]*[A-Za-z0-9]/
|
[ /[_A-Za-z][_A-Za-z0-9]*/ ] '{' namecharx '}' { /[_A-Za-z0-9]*[A-Za-z0-9]/ }
|
/[A-Za-z]/
;
namecharp
=
/[_A-Za-z0-9]+/
;
namecharx
=
/[_A-Za-z0-9]*/
;
"""
def test_main():
for s in POSITIVES:
print("ACCEPTED: %s \t as %s " % (s, parse(s)))
print('=' * 40)
for s in NEGATIVES:
thrown = False
try:
print("ERROR, SHOULD REFUSE: %s \t\t as %s" % (s, parse(s)))
except Exception:
print("REJECTED: " + s)
thrown = True
assert thrown
def parse(s):
model = grako.genmodel("model", grammar)
if ('__' in s):
raise Exception("Must not cointain consecutive slashes")
ast = model.parse(s, "uriname")
return ast
if __name__ == '__main__':
test_main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment