Skip to content

Instantly share code, notes, and snippets.

@mutoo
Created July 27, 2021 07:05
Show Gist options
  • Save mutoo/73c82de9dee89bf527856670038f3623 to your computer and use it in GitHub Desktop.
Save mutoo/73c82de9dee89bf527856670038f3623 to your computer and use it in GitHub Desktop.
a nearley grammar of BNF for svg path data
# The grammar for path data
# it's implementing the svg 1.1 spec
# https://www.w3.org/TR/SVG11/paths.html#PathDataBNF
#
# N.B. This Implementation Is Ambiguous!
# e.g. M0.6.5 -> M 0 6.5 | M 0.6 0.5
#
@{%
const it = (it) => it;
const nullish = () => null;
const arr2str = (arr) => arr.join('');
const sign2coef = (sign) => sign === '-'? -1: 1;
const inArr = ([id]) => [id];
const joinArr = ([head, _, rest]) => [head, ...(rest||[])];
const cmdWithParams = ([cmd, _, params]) => [cmd, params];
%}
#test -> digit:+ {% ([digits]) => arr2str(digits) %}
#test -> digit_sequence {% id %}
#test -> exponent {% id %}
#test -> fractional_constant {% id %}
#test -> floating_point_constant {% id %}
#test -> integer_constant {% id %}
#test -> flag {% id %}
#test -> number {% id %}
#test -> nonnegative_number {% id %}
#test -> coordinate_pair {% id %}
#test -> moveto_argument_sequence {% id %}
#test -> moveto {% id %}
#test -> closepath {% id %}
#test -> drawto_commands {% id %}
#test -> moveto_drawto_command_group {% id %}
#test -> moveto_drawto_command_groups {% id %}
svg_path ->
wsp:* moveto_drawto_command_groups:? wsp:* {% ([_, cgs, __]) => (cgs || []).flatMap(it) %}
moveto_drawto_command_groups ->
moveto_drawto_command_group {% inArr %}
| moveto_drawto_command_group wsp:* moveto_drawto_command_groups {% joinArr %}
moveto_drawto_command_group ->
moveto wsp:* drawto_commands:? {% ([head, _, rest]) => [head, ...(rest||[]).flatMap(it)] %}
drawto_commands ->
drawto_command {% inArr %}
| drawto_command wsp:* drawto_commands {% joinArr %}
drawto_command ->
closepath
| lineto
| horizontal_lineto
| vertical_lineto
| curveto
| smooth_curveto
| quadratic_bezier_curveto
| smooth_quadratic_bezier_curveto
| elliptical_arc
moveto ->
[Mm] wsp:* moveto_argument_sequence {% cmdWithParams %}
moveto_argument_sequence ->
coordinate_pair {% inArr %}
| coordinate_pair comma_wsp:? lineto_argument_sequence {% joinArr %}
closepath ->
[Zz] {% it %}
lineto ->
[Ll] wsp:* lineto_argument_sequence {% cmdWithParams %}
lineto_argument_sequence ->
coordinate_pair {% inArr %}
| coordinate_pair comma_wsp:? lineto_argument_sequence {% joinArr %}
horizontal_lineto ->
[Hh] wsp:* horizontal_lineto_argument_sequence {% cmdWithParams %}
horizontal_lineto_argument_sequence ->
coordinate {% inArr %}
| coordinate comma_wsp:? horizontal_lineto_argument_sequence {% joinArr %}
vertical_lineto ->
[Vv] wsp:* vertical_lineto_argument_sequence {% cmdWithParams %}
vertical_lineto_argument_sequence ->
coordinate {% inArr %}
| coordinate comma_wsp:? vertical_lineto_argument_sequence {% joinArr %}
curveto ->
[Cc] wsp:* curveto_argument_sequence {% cmdWithParams %}
curveto_argument_sequence ->
curveto_argument {% inArr %}
| curveto_argument comma_wsp:? curveto_argument_sequence {% joinArr %}
curveto_argument ->
coordinate_pair comma_wsp:? coordinate_pair comma_wsp:? coordinate_pair {% ([cp0, _, cp1, __, cp2]) => [cp0, cp1, cp2] %}
smooth_curveto ->
[Ss] wsp:* smooth_curveto_argument_sequence {% cmdWithParams %}
smooth_curveto_argument_sequence ->
smooth_curveto_argument {% inArr %}
| smooth_curveto_argument comma_wsp:? smooth_curveto_argument_sequence {% joinArr %}
smooth_curveto_argument ->
coordinate_pair comma_wsp:? coordinate_pair {% ([cp0, _, cp1]) => [cp0, cp1] %}
quadratic_bezier_curveto ->
[Qq] wsp:* quadratic_bezier_curveto_argument_sequence {% cmdWithParams %}
quadratic_bezier_curveto_argument_sequence ->
quadratic_bezier_curveto_argument {% inArr %}
| quadratic_bezier_curveto_argument comma_wsp:? quadratic_bezier_curveto_argument_sequence {% joinArr %}
quadratic_bezier_curveto_argument ->
coordinate_pair comma_wsp:? coordinate_pair {% ([cp0, _, cp1]) => [cp0, cp1] %}
smooth_quadratic_bezier_curveto ->
[Tt] wsp:* smooth_quadratic_bezier_curveto_argument_sequence
smooth_quadratic_bezier_curveto_argument_sequence ->
coordinate_pair {% inArr %}
| coordinate_pair comma_wsp:? smooth_quadratic_bezier_curveto_argument_sequence {% joinArr %}
elliptical_arc ->
[Aa] wsp:* elliptical_arc_argument_sequence {% cmdWithParams %}
elliptical_arc_argument_sequence ->
elliptical_arc_argument {% inArr %}
| elliptical_arc_argument comma_wsp:? elliptical_arc_argument_sequence {% joinArr %}
elliptical_arc_argument ->
nonnegative_number comma_wsp:? nonnegative_number comma_wsp:? number comma_wsp flag comma_wsp:? flag comma_wsp:? coordinate_pair
{% ([rx, _, ry, __, rotate, ___, largeArc, ____, sweep, _____, [x, y]]) => [rx, ry, rotate, sweep, largeArc, x, y] %}
coordinate_pair ->
coordinate comma_wsp:? coordinate {% ([x, _, y]) => [x, y] %}
coordinate ->
number {% id %}
nonnegative_number ->
integer_constant {% ([int]) => parseInt(int) %}
| floating_point_constant {% ([float]) => parseFloat(float) %}
number ->
sign:? integer_constant {% ([sign, int]) => sign2coef(sign) * parseInt(int) %}
| sign:? floating_point_constant {% ([sign, float]) => sign2coef(sign) * parseFloat(float) %}
flag ->
[01] {% ([f]) => parseInt(f) %}
comma_wsp ->
(wsp:+ comma:? wsp:*) {% nullish %}
| (comma wsp:*) {% nullish %}
comma ->
"," {% nullish %}
integer_constant ->
digit_sequence {% ([ds]) => arr2str(ds) %}
floating_point_constant ->
fractional_constant exponent:? {% ([frac, exp]) => `${frac}${exp||''}` %}
| digit_sequence exponent {% ([ds, exp]) => `${arr2str(ds)}${exp}` %}
fractional_constant ->
digit_sequence:? "." digit_sequence {% ([ds0, _, ds1]) => `${ds0?arr2str(ds0):''}.${arr2str(ds1)}` %}
| digit_sequence "." {% ([ds, _]) => arr2str(ds) %}
exponent ->
( "e" | "E" ) sign:? digit_sequence {% ([e, sign, ds]) => `e${sign||''}${arr2str(ds)}` %}
sign ->
[+-] {% id %}
digit_sequence ->
digit {% ([digit]) => [digit] %}
| digit digit_sequence {% ([d, ds]) => [d, ...ds] %}
digit ->
[0-9] {% id %}
wsp -> [ \t\n\v\f] {% nullish %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment