diff --git a/src/js/parser/javascript.js b/src/js/parser/javascript.js index ccdf6e7..c84a58b 100644 --- a/src/js/parser/javascript.js +++ b/src/js/parser/javascript.js @@ -7,6 +7,11 @@ import MatchFragment from './javascript/match_fragment.js'; import Subexp from './javascript/subexp.js'; import Charset from './javascript/charset.js'; import Terminal from './javascript/terminal.js'; +import Repeat from './javascript/repeat.js'; +import RepeatAny from './javascript/repeat_any.js'; +import RepeatOptional from './javascript/repeat_optional.js'; +import RepeatRequired from './javascript/repeat_required.js'; +import RepeatSpec from './javascript/repeat_spec.js'; parser.Parser.Root = Root; parser.Parser.Regexp = Regexp; @@ -15,5 +20,10 @@ parser.Parser.MatchFragment = MatchFragment; parser.Parser.Subexp = Subexp; parser.Parser.Charset = Charset; parser.Parser.Terminal = Terminal; +parser.Parser.Repeat = Repeat; +parser.Parser.RepeatAny = RepeatAny; +parser.Parser.RepeatOptional = RepeatOptional; +parser.Parser.RepeatRequired = RepeatRequired; +parser.Parser.RepeatSpec = RepeatSpec; export default parser; diff --git a/src/js/parser/javascript/grammar.peg b/src/js/parser/javascript/grammar.peg index 03672db..f35c4ed 100644 --- a/src/js/parser/javascript/grammar.peg +++ b/src/js/parser/javascript/grammar.peg @@ -2,19 +2,19 @@ grammar JavascriptRegexp root <- ( ( "/" regexp "/" _flags:[igm]* ) / regexp ""? ) regexp <- _match:match _alternates:( "|" match )* match <- _anchor_start:anchor_start? - _parts:match_fragment* + (!repeat) _parts:match_fragment* _anchor_end:anchor_end? - match_fragment <- ( subexp / charset / terminal ) repeat? + match_fragment <- _content:( subexp / charset / terminal ) _repeat:repeat? anchor_start <- "^" anchor_end <- "$" - repeat <- ( repeat_any / repeat_required / repeat_optional / repeat_spec ) repeat_greedy? - repeat_any <- "*" - repeat_required <- "+" - repeat_optional <- "?" - repeat_spec <- "{" [0-9]+ "," [0-9]+ "}" - / "{," [0-9]+ "}" - / "{" [0-9]+ ",}" - / "{" [0-9]+ "}" + repeat <- _spec:( repeat_any / repeat_required / repeat_optional / repeat_spec ) _greedy:repeat_greedy? + repeat_any <- "*" + repeat_required <- "+" + repeat_optional <- "?" + repeat_spec <- ( "{" _min:[0-9]+ "," _max:[0-9]+ "}" + / "{," _max:[0-9]+ "}" + / "{" _min:[0-9]+ ",}" + / "{" _exact:[0-9]+ "}" ) repeat_greedy <- "?" subexp <- "(" ( subexp_no_capture / subexp_positive_lookahead / subexp_negative_lookahead )? regexp ")" subexp_no_capture <- "?:" diff --git a/src/js/parser/javascript/match_fragment.js b/src/js/parser/javascript/match_fragment.js index faccf54..d1d01d9 100644 --- a/src/js/parser/javascript/match_fragment.js +++ b/src/js/parser/javascript/match_fragment.js @@ -2,5 +2,48 @@ import _ from 'lodash'; import Base from './base.js'; export default _.extend({}, Base, { - type: 'match_fragment' + type: 'match_fragment', + + render() { + this._content.container = this.container.group(); + this._content.render(); + }, + + position() { + var box, paths = []; + + this._content.position(); + + if (this._repeat.textValue !== '') { + this._content.container.transform(this._repeat.content_position()); + + box = this._content.container.getBBox(); + + if (this._repeat.has_skip()) { + paths.push(Snap.format('M0,{cy}q10,0 10,-10v-{vert}q0,-10 10,-10h{horiz}q10,0 10,10v{vert}q0,10 10,10', _.extend({ + vert: box.height / 2 - 10, + horiz: box.width - 10 + }, box))); + } + + if (this._repeat.has_loop()) { + paths.push(Snap.format('M{x},{cy}q-10,0 -10,10v{vert}q0,10 10,10h{width}q10,0 10,-10v-{vert}q0,-10 -10,-10', _.extend({ + vert: box.height / 2 - 10 + }, box))); + } + + if (this._repeat.has_loop() || this._repeat.has_skip()) { + paths.push(Snap.format('M0,{cy}h15M{x2},{cy}h15', box)); + } + + if (paths.length) { + this.container.path(paths.join('')); + } + } else { + this._content.container.transform(Snap.matrix() + .translate(0, 0)); + } + + this.render_bbox(this.container, this.container.getBBox()); + } }); diff --git a/src/js/parser/javascript/repeat.js b/src/js/parser/javascript/repeat.js new file mode 100644 index 0000000..7f96891 --- /dev/null +++ b/src/js/parser/javascript/repeat.js @@ -0,0 +1,35 @@ +export default { + minimum() { + return this._spec.minimum(); + }, + + maximum() { + return this._spec.maximum(); + }, + + greedy() { + return (this._greedy.textValue !== ''); + }, + + has_skip() { + return this.minimum() === 0; + }, + + has_loop() { + return this.maximum() === -1 || this.maximum() > 1; + }, + + content_position() { + var x = 0, y = 0; + + if (this.has_skip()) { + y = 10; + } + + if (this.has_skip() || this.has_loop()) { + x = 15; + } + + return Snap.matrix().translate(x, y); + } +} diff --git a/src/js/parser/javascript/repeat_any.js b/src/js/parser/javascript/repeat_any.js new file mode 100644 index 0000000..ea76d00 --- /dev/null +++ b/src/js/parser/javascript/repeat_any.js @@ -0,0 +1,9 @@ +export default { + minimum() { + return 0; + }, + + maximum() { + return -1; + } +}; diff --git a/src/js/parser/javascript/repeat_optional.js b/src/js/parser/javascript/repeat_optional.js new file mode 100644 index 0000000..03c73bb --- /dev/null +++ b/src/js/parser/javascript/repeat_optional.js @@ -0,0 +1,9 @@ +export default { + minimum() { + return 0; + }, + + maximum() { + return 1; + } +}; diff --git a/src/js/parser/javascript/repeat_required.js b/src/js/parser/javascript/repeat_required.js new file mode 100644 index 0000000..2ca7754 --- /dev/null +++ b/src/js/parser/javascript/repeat_required.js @@ -0,0 +1,9 @@ +export default { + minimum() { + return 1; + }, + + maximum() { + return -1; + } +}; diff --git a/src/js/parser/javascript/repeat_spec.js b/src/js/parser/javascript/repeat_spec.js new file mode 100644 index 0000000..93181d6 --- /dev/null +++ b/src/js/parser/javascript/repeat_spec.js @@ -0,0 +1,21 @@ +export default { + minimum() { + if (this._min) { + return Number(this._min.textValue); + } else if (this._exact) { + return Number(this._exact.textValue); + } else { + return 0; + } + }, + + maximum() { + if (this._max) { + return Number(this._max.textValue); + } else if (this._exact) { + return Number(this._exact.textValue); + } else { + return -1; + } + } +};