diff --git a/spec/app/keymap-spec.coffee b/spec/app/keymap-spec.coffee index 60e24434f..f50875bdb 100644 --- a/spec/app/keymap-spec.coffee +++ b/spec/app/keymap-spec.coffee @@ -72,8 +72,10 @@ describe "Keymap", -> expect(insertCharHandler).toHaveBeenCalled() describe "when the event's target node descends from multiple nodes that match selectors with a binding", -> - it "only triggers bindings on selectors associated with the closest ancestor node", -> + beforeEach -> keymap.bindKeys '.child-node', 'x': 'foo' + + it "only triggers bindings on selectors associated with the closest ancestor node", -> fooHandler = jasmine.createSpy 'fooHandler' fragment.on 'foo', fooHandler @@ -83,6 +85,22 @@ describe "Keymap", -> expect(deleteCharHandler).not.toHaveBeenCalled() expect(insertCharHandler).not.toHaveBeenCalled() + describe "when 'abortKeyBinding' is called on the triggered event", -> + it "aborts the current event and tries again with the next-most-specific key binding", -> + fooHandler1 = jasmine.createSpy('fooHandler1').andCallFake (e) -> + expect(deleteCharHandler).not.toHaveBeenCalled() + e.abortKeyBinding() + fooHandler2 = jasmine.createSpy('fooHandler2') + + fragment.find('.child-node').on 'foo', fooHandler1 + fragment.on 'foo', fooHandler2 + + target = fragment.find('.grandchild-node')[0] + keymap.handleKeyEvent(keydownEvent('x', target: target)) + expect(fooHandler1).toHaveBeenCalled() + expect(fooHandler2).not.toHaveBeenCalled() + expect(deleteCharHandler).toHaveBeenCalled() + describe "when the event bubbles to a node that matches multiple selectors", -> describe "when the matching selectors differ in specificity", -> it "triggers the binding for the most specific selector", -> diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index 2cab3e5a1..470fe2c4a 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -7,37 +7,43 @@ describe "Project", -> project = new Project(require.resolve('fixtures/dir')) describe ".open(path)", -> - [absolutePath, newBufferHandler] = [] + [absolutePath, newBufferHandler, newEditSessionHandler] = [] beforeEach -> absolutePath = require.resolve('fixtures/dir/a') newBufferHandler = jasmine.createSpy('newBufferHandler') project.on 'new-buffer', newBufferHandler + newEditSessionHandler = jasmine.createSpy('newEditSessionHandler') + project.on 'new-edit-session', newEditSessionHandler describe "when given an absolute path that hasn't been opened previously", -> - it "returns a new edit session for the given path and emits a 'new-buffer' event", -> + it "returns a new edit session for the given path and emits 'new-buffer' and 'new-edit-session' events", -> editSession = project.open(absolutePath) expect(editSession.buffer.path).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer + expect(newEditSessionHandler).toHaveBeenCalledWith editSession describe "when given a relative path that hasn't been opened previously", -> - it "returns a new edit session for the given path (relative to the project root) and emits a 'new-buffer' event", -> + it "returns a new edit session for the given path (relative to the project root) and emits 'new-buffer' and 'new-edit-session' events", -> editSession = project.open('a') expect(editSession.buffer.path).toBe absolutePath expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer + expect(newEditSessionHandler).toHaveBeenCalledWith editSession describe "when passed the path to a buffer that has already been opened", -> - it "returns a new edit session containing previously opened buffer", -> + it "returns a new edit session containing previously opened buffer and emits a 'new-edit-session' event", -> editSession = project.open(absolutePath) newBufferHandler.reset() expect(project.open(absolutePath).buffer).toBe editSession.buffer expect(project.open('a').buffer).toBe editSession.buffer expect(newBufferHandler).not.toHaveBeenCalled() + expect(newEditSessionHandler).toHaveBeenCalledWith editSession describe "when not passed a path", -> - it "returns a new edit session and emits a new-buffer event", -> + it "returns a new edit session and emits 'new-buffer' and 'new-edit-session' events", -> editSession = project.open() expect(editSession.buffer.getPath()).toBeUndefined() expect(newBufferHandler).toHaveBeenCalledWith(editSession.buffer) + expect(newEditSessionHandler).toHaveBeenCalledWith editSession describe ".resolve(path)", -> it "returns an absolute path based on the project's root", -> diff --git a/spec/extensions/snippets-spec.coffee b/spec/extensions/snippets-spec.coffee new file mode 100644 index 000000000..9d857ba20 --- /dev/null +++ b/spec/extensions/snippets-spec.coffee @@ -0,0 +1,188 @@ +Snippets = require 'snippets' +RootView = require 'root-view' +Buffer = require 'buffer' +Editor = require 'editor' +_ = require 'underscore' +fs = require 'fs' + +describe "Snippets extension", -> + [buffer, editor] = [] + beforeEach -> + rootView = new RootView(require.resolve('fixtures/sample.js')) + rootView.activateExtension(Snippets) + editor = rootView.activeEditor() + buffer = editor.buffer + rootView.simulateDomAttachment() + rootView.enableKeymap() + + describe "when 'tab' is triggered on the editor", -> + beforeEach -> + Snippets.evalSnippets 'js', """ + snippet t1 "Snippet without tab stops" + this is a test + endsnippet + + snippet t2 "With tab stops" + go here next:($2) and finally go here:($3) + go here first:($1) + + endsnippet + + snippet t3 "With indented second line" + line 1 + line 2$1 + + endsnippet + + snippet t4 "With tab stop placeholders" + go here ${1:first} and then here ${2:second} + + endsnippet + """ + + describe "when the letters preceding the cursor trigger a snippet", -> + describe "when the snippet contains no tab stops", -> + it "replaces the prefix with the snippet text and places the cursor at its end", -> + editor.insertText("t1") + expect(editor.getCursorScreenPosition()).toEqual [0, 2] + + editor.trigger keydownEvent('tab', target: editor[0]) + expect(buffer.lineForRow(0)).toBe "this is a testvar quicksort = function () {" + expect(editor.getCursorScreenPosition()).toEqual [0, 14] + + describe "when the snippet contains tab stops", -> + it "places the cursor at the first tab-stop, and moves the cursor in response to 'next-tab-stop' events", -> + anchorCountBefore = editor.activeEditSession.getAnchors().length + editor.setCursorScreenPosition([2, 0]) + editor.insertText('t2') + editor.trigger keydownEvent('tab', target: editor[0]) + expect(buffer.lineForRow(2)).toBe "go here next:() and finally go here:()" + expect(buffer.lineForRow(3)).toBe "go here first:()" + expect(buffer.lineForRow(4)).toBe " if (items.length <= 1) return items;" + expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] + + editor.trigger keydownEvent('tab', target: editor[0]) + expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 14]] + editor.insertText 'abc' + + editor.trigger keydownEvent('tab', target: editor[0]) + expect(editor.getSelectedBufferRange()).toEqual [[2, 40], [2, 40]] + + # tab backwards + editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0]) + expect(editor.getSelectedBufferRange()).toEqual [[2, 14], [2, 17]] # should highlight text typed at tab stop + + editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0]) + expect(editor.getSelectedBufferRange()).toEqual [[3, 15], [3, 15]] + + # shift-tab on first tab-stop does nothing + editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0]) + expect(editor.getCursorScreenPosition()).toEqual [3, 15] + + # tab through all tab stops, then tab on last stop to terminate snippet + editor.trigger keydownEvent('tab', target: editor[0]) + editor.trigger keydownEvent('tab', target: editor[0]) + editor.trigger keydownEvent('tab', target: editor[0]) + expect(buffer.lineForRow(2)).toBe "go here next:(abc) and finally go here:( )" + expect(editor.activeEditSession.getAnchors().length).toBe anchorCountBefore + + describe "when the tab stops have placeholder text", -> + it "auto-fills the placeholder text and highlights it when navigating to that tab stop", -> + editor.insertText 't4' + editor.trigger 'snippets:expand' + expect(buffer.lineForRow(0)).toBe 'go here first and then here second' + expect(editor.getSelectedBufferRange()).toEqual [[0, 8], [0, 13]] + + describe "when the cursor is moved beyond the bounds of a tab stop", -> + it "terminates the snippet on the next tab", -> + editor.setCursorScreenPosition([2, 0]) + editor.insertText('t2') + editor.trigger keydownEvent('tab', target: editor[0]) + + editor.moveCursorRight() + editor.trigger keydownEvent('tab', target: editor[0]) + expect(buffer.lineForRow(3)).toBe "go here first:() " + expect(editor.getCursorBufferPosition()).toEqual [3, 18] + + # test we can terminate with shift-tab + editor.setCursorScreenPosition([4, 0]) + editor.insertText('t2') + editor.trigger keydownEvent('tab', target: editor[0]) + editor.trigger keydownEvent('tab', target: editor[0]) + + editor.moveCursorRight() + editor.trigger keydownEvent('tab', shiftKey: true, target: editor[0]) + expect(editor.getCursorBufferPosition()).toEqual [4, 15] + + + describe "when a the start of the snippet is indented", -> + it "indents the subsequent lines of the snippet to be even with the start of the first line", -> + editor.setCursorScreenPosition([2, Infinity]) + editor.insertText ' t3' + editor.trigger 'snippets:expand' + expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items; line 1" + expect(buffer.lineForRow(3)).toBe " line 2" + expect(editor.getCursorBufferPosition()).toEqual [3, 12] + + describe "when the letters preceding the cursor don't match a snippet", -> + it "inserts a tab as normal", -> + editor.insertText("xte") + expect(editor.getCursorScreenPosition()).toEqual [0, 3] + + editor.trigger 'tab' + expect(buffer.lineForRow(0)).toBe "xte var quicksort = function () {" + expect(editor.getCursorScreenPosition()).toEqual [0, 5] + + describe ".loadSnippetsFile(path)", -> + it "loads the snippets in the given file", -> + spyOn(fs, 'read').andReturn """ + snippet t1 "Test snippet 1" + this is a test 1 + endsnippet + """ + + Snippets.loadSnippetsFile('/tmp/foo/js.snippets') + expect(fs.read).toHaveBeenCalledWith('/tmp/foo/js.snippets') + + editor.insertText("t1") + editor.trigger 'snippets:expand' + expect(buffer.lineForRow(0)).toBe "this is a test 1var quicksort = function () {" + + describe "Snippets parser", -> + it "can parse multiple snippets", -> + snippets = Snippets.snippetsParser.parse """ + snippet t1 "Test snippet 1" + this is a test 1 + endsnippet + + snippet t2 "Test snippet 2" + this is a test 2 + endsnippet + """ + expect(_.keys(snippets).length).toBe 2 + snippet = snippets['t1'] + expect(snippet.prefix).toBe 't1' + expect(snippet.description).toBe "Test snippet 1" + expect(snippet.body).toBe "this is a test 1" + + snippet = snippets['t2'] + expect(snippet.prefix).toBe 't2' + expect(snippet.description).toBe "Test snippet 2" + expect(snippet.body).toBe "this is a test 2" + + it "can parse snippets with tabstops", -> + snippets = Snippets.snippetsParser.parse """ + # this line intentially left blank. + snippet t1 "Snippet with tab stops" + go here next:($2) and finally go here:($3) + go here first:($1) + endsnippet + """ + + snippet = snippets['t1'] + expect(snippet.body).toBe """ + go here next:() and finally go here:() + go here first:() + """ + + expect(snippet.tabStops).toEqual [[[1, 15], [1, 15]], [[0, 14], [0, 14]], [[0, 37], [0, 37]]] diff --git a/spec/stdlib/underscore-extensions-spec.coffee b/spec/stdlib/underscore-extensions-spec.coffee new file mode 100644 index 000000000..ee12f269e --- /dev/null +++ b/spec/stdlib/underscore-extensions-spec.coffee @@ -0,0 +1,22 @@ +_ = require 'underscore' + +describe "underscore extensions", -> + describe "_.adviseBefore", -> + [object, calls] = [] + + beforeEach -> + calls = [] + object = { + method: (args...) -> + calls.push(["original", this, args]) + } + + it "calls the given function before the advised method", -> + _.adviseBefore object, 'method', (args...) -> calls.push(["advice", this, args]) + object.method(1, 2, 3) + expect(calls).toEqual [['advice', object, [1, 2, 3]], ['original', object, [1, 2, 3]]] + + it "cancels the original method's invocation if the advice returns true", -> + _.adviseBefore object, 'method', -> false + object.method(1, 2, 3) + expect(calls).toEqual [] diff --git a/src/app/anchor-range.coffee b/src/app/anchor-range.coffee new file mode 100644 index 000000000..6340945e7 --- /dev/null +++ b/src/app/anchor-range.coffee @@ -0,0 +1,24 @@ +Range = require 'range' + +module.exports = +class AnchorRange + start: null + end: null + + constructor: (@editSession, bufferRange) -> + bufferRange = Range.fromObject(bufferRange) + @startAnchor = @editSession.addAnchorAtBufferPosition(bufferRange.start, ignoreEqual: true) + @endAnchor = @editSession.addAnchorAtBufferPosition(bufferRange.end) + + getBufferRange: -> + new Range(@startAnchor.getBufferPosition(), @endAnchor.getBufferPosition()) + + getScreenRange: -> + new Range(@startAnchor.getScreenPosition(), @endAnchor.getScreenPosition()) + + containsBufferPosition: (bufferPosition) -> + @getBufferRange().containsPoint(bufferPosition) + + destroy: -> + @startAnchor.destroy() + @endAnchor.destroy() diff --git a/src/app/anchor.coffee b/src/app/anchor.coffee index 676d3e97f..c5aee8862 100644 --- a/src/app/anchor.coffee +++ b/src/app/anchor.coffee @@ -8,12 +8,17 @@ class Anchor bufferPosition: null screenPosition: null - constructor: (@editSession) -> + constructor: (@editSession, options = {}) -> + { @ignoreEqual } = options handleBufferChange: (e) -> { oldRange, newRange } = e position = @getBufferPosition() - return if position.isLessThan(oldRange.end) + + if @ignoreEqual + return if position.isLessThanOrEqual(oldRange.end) + else + return if position.isLessThan(oldRange.end) newRow = newRange.end.row newColumn = newRange.end.column @@ -56,4 +61,7 @@ class Anchor screenPosition = @editSession.screenPositionForBufferPosition(@bufferPosition, options) @setScreenPosition(screenPosition, bufferChange: options.bufferChange, clip: false, assignBufferPosition: false) + destroy: -> + @editSession.removeAnchor(this) + _.extend(Anchor.prototype, EventEmitter) diff --git a/src/app/atom.coffee b/src/app/atom.coffee index 36be59da3..1fdda1f62 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -9,13 +9,15 @@ module.exports = class Atom keymap: null windows: null - userConfigurationPath: null + configFilePath: null + configDirPath: null rootViewStates: null constructor: (@loadPath, nativeMethods) -> @windows = [] @setUpKeymap() - @userConfigurationPath = fs.absolute "~/.atom/atom.coffee" + @configDirPath = fs.absolute("~/.atom") + @configFilePath = fs.join(@configDirPath, "atom.coffee") @rootViewStates = {} setUpKeymap: -> diff --git a/src/app/binding-set.coffee b/src/app/binding-set.coffee index aaf08fda9..3882a7236 100644 --- a/src/app/binding-set.coffee +++ b/src/app/binding-set.coffee @@ -12,7 +12,7 @@ class BindingSet commandForEvent: null parser: null - constructor: (@selector, mapOrFunction) -> + constructor: (@selector, mapOrFunction, @index) -> @parser = PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs')) @specificity = Specificity(@selector) @commandsByKeystrokes = {} diff --git a/src/app/buffer.coffee b/src/app/buffer.coffee index d35168063..9d8d6e567 100644 --- a/src/app/buffer.coffee +++ b/src/app/buffer.coffee @@ -26,6 +26,12 @@ class Buffer getPath: -> @path + getExtension: -> + if @getPath() + @getPath().split('/').pop().split('.').pop() + else + null + setPath: (path) -> @path = path @trigger "path-change", this diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index ff868b9a0..9596280fe 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -150,6 +150,9 @@ class Cursor getCurrentLineBufferRange: -> @editSession.bufferRangeForBufferRow(@getBufferRow()) + getCurrentWordPrefix: -> + @editSession.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()]) + isAtBeginningOfLine: -> @getBufferPosition().column == 0 diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index e673daf1a..34bc774d6 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -5,6 +5,7 @@ DisplayBuffer = require 'display-buffer' Cursor = require 'cursor' Selection = require 'selection' EventEmitter = require 'event-emitter' +AnchorRange = require 'anchor-range' _ = require 'underscore' module.exports = @@ -22,6 +23,7 @@ class EditSession scrollLeft: 0 displayBuffer: null anchors: null + anchorRanges: null cursors: null selections: null autoIndent: true @@ -34,6 +36,7 @@ class EditSession @displayBuffer = new DisplayBuffer(@buffer, { @tabText }) @tokenizedBuffer = @displayBuffer.tokenizedBuffer @anchors = [] + @anchorRanges = [] @cursors = [] @selections = [] @addCursorAtScreenPosition([0, 0]) @@ -234,11 +237,21 @@ class EditSession getAnchors: -> new Array(@anchors...) - addAnchor: -> - anchor = new Anchor(this) + addAnchor: (options) -> + anchor = new Anchor(this, options) @anchors.push(anchor) anchor + addAnchorAtBufferPosition: (bufferPosition, options) -> + anchor = @addAnchor(options) + anchor.setBufferPosition(bufferPosition) + anchor + + addAnchorRange: (range) -> + anchorRange = new AnchorRange(this, range) + @anchorRanges.push(anchorRange) + anchorRange + removeAnchor: (anchor) -> _.remove(@anchors, anchor) @@ -342,6 +355,9 @@ class EditSession getSelectedText: -> @getLastSelection().getText() + getTextInBufferRange: (range) -> + @buffer.getTextInRange(range) + moveCursorUp: -> @moveCursors (cursor) -> cursor.moveUp() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 9c16bd265..dd4003999 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -186,6 +186,7 @@ class Editor extends View getSelectionsOrderedByBufferPosition: -> @activeEditSession.getSelectionsOrderedByBufferPosition() getLastSelectionInBuffer: -> @activeEditSession.getLastSelectionInBuffer() getSelectedText: -> @activeEditSession.getSelectedText() + getSelectedBufferRange: -> @activeEditSession.getSelectedBufferRange() setSelectedBufferRange: (bufferRange, options) -> @activeEditSession.setSelectedBufferRange(bufferRange, options) setSelectedBufferRanges: (bufferRanges, options) -> @activeEditSession.setSelectedBufferRanges(bufferRanges, options) addSelectionForBufferRange: (bufferRange, options) -> @activeEditSession.addSelectionForBufferRange(bufferRange, options) diff --git a/src/app/keymap.coffee b/src/app/keymap.coffee index 3d4a59014..1faa80063 100644 --- a/src/app/keymap.coffee +++ b/src/app/keymap.coffee @@ -20,21 +20,21 @@ class Keymap 'meta-o': 'open' $(document).on 'new-window', => $native.newWindow() - $(document).on 'open-user-configuration', => atom.open(atom.userConfigurationPath) + $(document).on 'open-user-configuration', => atom.open(atom.configFilePath) $(document).on 'open', => path = $native.openDialog() atom.open(path) if path bindKeys: (selector, bindings) -> - @bindingSets.unshift(new BindingSet(selector, bindings)) + index = @bindingSets.length + @bindingSets.unshift(new BindingSet(selector, bindings, index)) bindingsForElement: (element) -> keystrokeMap = {} currentNode = $(element) while currentNode.length - bindingSets = @bindingSets.filter (set) -> currentNode.is(set.selector) - bindingSets.sort (a, b) -> b.specificity - a.specificity + bindingSets = @bindingSetsForNode(currentNode) _.defaults(keystrokeMap, set.commandsByKeystrokes) for set in bindingSets currentNode = currentNode.parent() @@ -46,12 +46,11 @@ class Keymap @queuedKeystrokes = null currentNode = $(event.target) while currentNode.length - candidateBindingSets = @bindingSets.filter (set) -> currentNode.is(set.selector) - candidateBindingSets.sort (a, b) -> b.specificity - a.specificity + candidateBindingSets = @bindingSetsForNode(currentNode) for bindingSet in candidateBindingSets command = bindingSet.commandForEvent(event) if command - @triggerCommandEvent(event, command) + continue if @triggerCommandEvent(event, command) return false else if command == false return false @@ -63,10 +62,23 @@ class Keymap !isMultiKeystroke + bindingSetsForNode: (node) -> + bindingSets = @bindingSets.filter (set) -> node.is(set.selector) + bindingSets.sort (a, b) -> + if b.specificity == a.specificity + b.index - a.index + else + b.specificity - a.specificity + triggerCommandEvent: (keyEvent, commandName) -> commandEvent = $.Event(commandName) commandEvent.keyEvent = keyEvent + aborted = false + commandEvent.abortKeyBinding = -> + @stopImmediatePropagation() + aborted = true $(keyEvent.target).trigger(commandEvent) + aborted multiKeystrokeStringForEvent: (event) -> currentKeystroke = @keystrokeStringForEvent(event) diff --git a/src/app/keymaps/snippets.coffee b/src/app/keymaps/snippets.coffee new file mode 100644 index 000000000..6227dd8c1 --- /dev/null +++ b/src/app/keymaps/snippets.coffee @@ -0,0 +1,6 @@ +window.keymap.bindKeys '.editor' + 'tab': 'snippets:expand' + +window.keymap.bindKeys '.editor' + 'tab': 'snippets:next-tab-stop' + 'shift-tab': 'snippets:previous-tab-stop' diff --git a/src/app/project.coffee b/src/app/project.coffee index 1e66e163c..61e7b5635 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -90,6 +90,7 @@ class Project softWrap: @getSoftWrap() @editSessions.push editSession + @trigger 'new-edit-session', editSession editSession buildBuffer: (filePath) -> diff --git a/src/app/range.coffee b/src/app/range.coffee index 41f1136c7..c50b766fd 100644 --- a/src/app/range.coffee +++ b/src/app/range.coffee @@ -40,6 +40,9 @@ class Range inspect: -> "[#{@start.inspect()} - #{@end.inspect()}]" + add: (point) -> + new Range(@start.add(point), @end.add(point)) + intersectsWith: (otherRange) -> if @start.isLessThanOrEqual(otherRange.start) @end.isGreaterThanOrEqual(otherRange.start) diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 6126c5b5b..ae64fb066 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -174,7 +174,7 @@ class RootView extends View loadUserConfiguration: -> try - require atom.userConfigurationPath if fs.exists(atom.userConfigurationPath) + require atom.configFilePath if fs.exists(atom.configFilePath) catch error - console.error "Failed to load `#{atom.userConfigurationPath}`", error.message, error + console.error "Failed to load `#{atom.configFilePath}`", error.message, error diff --git a/src/app/tokenized-buffer.coffee b/src/app/tokenized-buffer.coffee index 01c015c43..80649b8e4 100644 --- a/src/app/tokenized-buffer.coffee +++ b/src/app/tokenized-buffer.coffee @@ -23,8 +23,7 @@ class TokenizedBuffer @aceAdaptor = new AceAdaptor(this) requireAceMode: -> - extension = if @buffer.getPath() then @buffer.getPath().split('/').pop().split('.').pop() else null - modeName = switch extension + modeName = switch @buffer.getExtension() when 'js' then 'javascript' when 'coffee' then 'coffee' when 'rb', 'ru' then 'ruby' diff --git a/src/extensions/snippets/index.coffee b/src/extensions/snippets/index.coffee new file mode 100644 index 000000000..912e393e5 --- /dev/null +++ b/src/extensions/snippets/index.coffee @@ -0,0 +1 @@ +module.exports = require 'extensions/snippets/snippets.coffee' diff --git a/src/extensions/snippets/snippet.coffee b/src/extensions/snippets/snippet.coffee new file mode 100644 index 000000000..c2bb1f48c --- /dev/null +++ b/src/extensions/snippets/snippet.coffee @@ -0,0 +1,36 @@ +_ = require 'underscore' +Range = require 'range' + +module.exports = +class Snippet + body: null + lineCount: null + tabStops: null + + constructor: ({@bodyPosition, @prefix, @description, body}) -> + @body = @extractTabStops(body) + + extractTabStops: (bodyLines) -> + tabStopsByIndex = {} + bodyText = [] + + [row, column] = [0, 0] + for bodyLine, i in bodyLines + lineText = [] + for segment in bodyLine + if segment.index + { index, placeholderText } = segment + tabStopsByIndex[index] = new Range([row, column], [row, column + placeholderText.length]) + lineText.push(placeholderText) + else + lineText.push(segment) + column += segment.length + bodyText.push(lineText.join('')) + row++; column = 0 + @lineCount = row + 1 + + @tabStops = [] + for index in _.keys(tabStopsByIndex).sort() + @tabStops.push tabStopsByIndex[index] + + bodyText.join('\n') diff --git a/src/extensions/snippets/snippets.coffee b/src/extensions/snippets/snippets.coffee new file mode 100644 index 000000000..92f1ff7c9 --- /dev/null +++ b/src/extensions/snippets/snippets.coffee @@ -0,0 +1,101 @@ +fs = require 'fs' +PEG = require 'pegjs' +_ = require 'underscore' + +module.exports = + name: 'Snippets' + snippetsByExtension: {} + snippetsParser: PEG.buildParser(fs.read(require.resolve 'extensions/snippets/snippets.pegjs'), trackLineAndColumn: true) + + activate: (@rootView) -> + @loadSnippets() + @rootView.on 'editor-open', (e, editor) => @enableSnippetsInEditor(editor) + + loadSnippets: -> + snippetsDir = fs.join(atom.configDirPath, 'snippets') + return unless fs.exists(snippetsDir) + @loadSnippetsFile(path) for path in fs.list(snippetsDir) when fs.extension(path) == '.snippets' + + loadSnippetsFile: (path) -> + @evalSnippets(fs.base(path, '.snippets'), fs.read(path)) + + evalSnippets: (extension, text) -> + @snippetsByExtension[extension] = @snippetsParser.parse(text) + + enableSnippetsInEditor: (editor) -> + editor.on 'snippets:expand', (e) => + editSession = editor.activeEditSession + editSession.snippetsSession ?= new SnippetsSession(editSession, @snippetsByExtension) + e.abortKeyBinding() unless editSession.snippetsSession.expandSnippet() + + editor.on 'snippets:next-tab-stop', (e) -> + editSession = editor.activeEditSession + e.abortKeyBinding() unless editSession.snippetsSession?.goToNextTabStop() + + editor.on 'snippets:previous-tab-stop', (e) -> + editSession = editor.activeEditSession + e.abortKeyBinding() unless editSession.snippetsSession?.goToPreviousTabStop() + +class SnippetsSession + tabStopAnchorRanges: null + constructor: (@editSession, @snippetsByExtension) -> + @editSession.on 'move-cursor', => @terminateIfCursorIsOutsideTabStops() + + expandSnippet: -> + return unless snippets = @snippetsByExtension[@editSession.buffer.getExtension()] + prefix = @editSession.getLastCursor().getCurrentWordPrefix() + if snippet = snippets[prefix] + @editSession.selectToBeginningOfWord() + startPosition = @editSession.getCursorBufferPosition() + @editSession.insertText(snippet.body) + @placeTabStopAnchorRanges(startPosition, snippet.tabStops) + @indentSnippet(startPosition.row, snippet) + true + else + false + + placeTabStopAnchorRanges: (startPosition, tabStopRanges) -> + return unless tabStopRanges.length + @tabStopAnchorRanges = tabStopRanges.map (tabStopRange) => + { start, end } = tabStopRange + @editSession.addAnchorRange([startPosition.add(start), startPosition.add(end)]) + @setTabStopIndex(0) + + indentSnippet: (startRow, snippet) -> + if snippet.lineCount > 1 + initialIndent = @editSession.lineForBufferRow(startRow).match(/^\s*/)[0] + for row in [startRow + 1...startRow + snippet.lineCount] + @editSession.buffer.insert([row, 0], initialIndent) + + goToNextTabStop: -> + return false unless @ensureValidTabStops() + nextIndex = @tabStopIndex + 1 + if nextIndex < @tabStopAnchorRanges.length + @setTabStopIndex(nextIndex) + true + else + @terminateActiveSnippet() + false + + goToPreviousTabStop: -> + return false unless @ensureValidTabStops() + @setTabStopIndex(@tabStopIndex - 1) if @tabStopIndex > 0 + true + + ensureValidTabStops: -> + @tabStopAnchorRanges? and @terminateIfCursorIsOutsideTabStops() + + setTabStopIndex: (@tabStopIndex) -> + @editSession.setSelectedBufferRange(@tabStopAnchorRanges[@tabStopIndex].getBufferRange()) + + terminateIfCursorIsOutsideTabStops: -> + return unless @tabStopAnchorRanges + position = @editSession.getCursorBufferPosition() + for anchorRange in @tabStopAnchorRanges + return true if anchorRange.containsBufferPosition(position) + @terminateActiveSnippet() + false + + terminateActiveSnippet: -> + anchorRange.destroy() for anchorRange in @tabStopAnchorRanges + @tabStopAnchorRanges = null diff --git a/src/extensions/snippets/snippets.pegjs b/src/extensions/snippets/snippets.pegjs new file mode 100644 index 000000000..2d2950130 --- /dev/null +++ b/src/extensions/snippets/snippets.pegjs @@ -0,0 +1,39 @@ +{ + var Snippet = require('extensions/snippets/snippet'); + var Point = require('point'); +} + +snippets = snippets:snippet+ ws? { + var snippetsByPrefix = {}; + snippets.forEach(function(snippet) { + snippetsByPrefix[snippet.prefix] = snippet + }); + return snippetsByPrefix; +} + +snippet = ws? start ws prefix:prefix ws description:string bodyPosition:beforeBody body:body end { + return new Snippet({ bodyPosition: bodyPosition, prefix: prefix, description: description, body: body }); +} + +start = 'snippet' +prefix = prefix:[A-Za-z0-9_]+ { return prefix.join(''); } +string = ['] body:[^']* ['] { return body.join(''); } + / ["] body:[^"]* ["] { return body.join(''); } + +beforeBody = [ ]* '\n' { return new Point(line, 0); } // return start position of body: body begins on next line, so don't subtract 1 from line + +body = bodyLine+ +bodyLine = content:(tabStop / bodyText)* '\n' { return content; } +bodyText = text:bodyChar+ { return text.join(''); } +bodyChar = !(end / tabStop) char:[^\n] { return char; } +tabStop = simpleTabStop / tabStopWithPlaceholder +simpleTabStop = '$' index:[0-9]+ { + return { index: parseInt(index), placeholderText: '' }; +} +tabStopWithPlaceholder = '${' index:[0-9]+ ':' placeholderText:[^}]* '}' { + return { index: parseInt(index), placeholderText: placeholderText.join('') }; +} + +end = 'endsnippet' +ws = ([ \n] / comment)+ +comment = '#' [^\n]* diff --git a/src/stdlib/underscore-extensions.coffee b/src/stdlib/underscore-extensions.coffee index ef59494cf..1fda906ee 100644 --- a/src/stdlib/underscore-extensions.coffee +++ b/src/stdlib/underscore-extensions.coffee @@ -10,6 +10,12 @@ _.mixin sum += elt for elt in array sum + adviseBefore: (object, methodName, advice) -> + original = object[methodName] + object[methodName] = (args...) -> + unless advice.apply(this, args) == false + original.apply(this, args) + escapeRegExp: (string) -> # Referring to the table here: # https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/regexp diff --git a/vendor/pegjs.js b/vendor/pegjs.js index e9d300558..42e67b359 100644 --- a/vendor/pegjs.js +++ b/vendor/pegjs.js @@ -1,12 +1,16 @@ -/* PEG.js 0.6.2 (http://pegjs.majda.cz/) */ - -(function() { - -var undefined; +/* + * PEG.js 0.7.0 + * + * http://pegjs.majda.cz/ + * + * Copyright (c) 2010-2012 David Majda + * Licensend under the MIT license. + */ +var PEG = (function(undefined) { var PEG = { - /* PEG.js version. */ - VERSION: "0.6.2", + /* PEG.js version (uses semantic versioning). */ + VERSION: "0.7.0", /* * Generates a parser from a specified grammar and returns it. @@ -19,8 +23,8 @@ var PEG = { * errors are detected during the generation and some may protrude to the * generated parser and cause its malfunction. */ - buildParser: function(grammar) { - return PEG.compiler.compile(PEG.parser.parse(grammar)); + buildParser: function(grammar, options) { + return PEG.compiler.compile(PEG.parser.parse(grammar), options); } }; @@ -33,6 +37,29 @@ PEG.GrammarError = function(message) { PEG.GrammarError.prototype = Error.prototype; +/* Like Python's |range|, but without |step|. */ +function range(start, stop) { + if (stop === undefined) { + stop = start; + start = 0; + } + + var result = new Array(Math.max(0, stop - start)); + for (var i = 0, j = start; j < stop; i++, j++) { + result[i] = j; + } + return result; +} + +function find(array, callback) { + var length = array.length; + for (var i = 0; i < length; i++) { + if (callback(array[i])) { + return array[i]; + } + } +} + function contains(array, value) { /* * Stupid IE does not have Array.prototype.indexOf, otherwise this function @@ -50,7 +77,7 @@ function contains(array, value) { function each(array, callback) { var length = array.length; for (var i = 0; i < length; i++) { - callback(array[i]); + callback(array[i], i); } } @@ -58,7 +85,27 @@ function map(array, callback) { var result = []; var length = array.length; for (var i = 0; i < length; i++) { - result[i] = callback(array[i]); + result[i] = callback(array[i], i); + } + return result; +} + +function pluck(array, key) { + return map(array, function (e) { return e[key]; }); +} + +function keys(object) { + var result = []; + for (var key in object) { + result.push(key); + } + return result; +} + +function values(object) { + var result = []; + for (var key in object) { + result.push(object[key]); } return result; } @@ -66,7 +113,7 @@ function map(array, callback) { /* * Returns a string padded on the left to a desired length with a character. * - * The code needs to be in sync with th code template in the compilation + * The code needs to be in sync with the code template in the compilation * function for "action" nodes. */ function padLeft(input, padding, length) { @@ -84,18 +131,20 @@ function padLeft(input, padding, length) { * Returns an escape sequence for given character. Uses \x for characters <= * 0xFF to save space, \u for the rest. * - * The code needs to be in sync with th code template in the compilation + * The code needs to be in sync with the code template in the compilation * function for "action" nodes. */ function escape(ch) { var charCode = ch.charCodeAt(0); + var escapeChar; + var length; if (charCode <= 0xFF) { - var escapeChar = 'x'; - var length = 2; + escapeChar = 'x'; + length = 2; } else { - var escapeChar = 'u'; - var length = 4; + escapeChar = 'u'; + length = 4; } return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length); @@ -105,7 +154,7 @@ function escape(ch) { * Surrounds the string with quotes and escapes characters inside so that the * result is a valid JavaScript string. * - * The code needs to be in sync with th code template in the compilation + * The code needs to be in sync with the code template in the compilation * function for "action" nodes. */ function quote(s) { @@ -115,16 +164,21 @@ function quote(s) { * line separator, paragraph separator, and line feed. Any character may * appear in the form of an escape sequence. * - * For portability, we also escape escape all non-ASCII characters. + * For portability, we also escape escape all control and non-ASCII + * characters. Note that "\0" and "\v" escape sequences are not used because + * JSHint does not like the first and IE the second. */ return '"' + s - .replace(/\\/g, '\\\\') // backslash - .replace(/"/g, '\\"') // closing quote character - .replace(/\r/g, '\\r') // carriage return - .replace(/\n/g, '\\n') // line feed - .replace(/[\x80-\uFFFF]/g, escape) // non-ASCII characters + .replace(/\\/g, '\\\\') // backslash + .replace(/"/g, '\\"') // closing quote character + .replace(/\x08/g, '\\b') // backspace + .replace(/\t/g, '\\t') // horizontal tab + .replace(/\n/g, '\\n') // line feed + .replace(/\f/g, '\\f') // form feed + .replace(/\r/g, '\\r') // carriage return + .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape) + '"'; -}; +} /* * Escapes characters inside the string so that it can be used as a list of @@ -134,17 +188,21 @@ function quoteForRegexpClass(s) { /* * Based on ECMA-262, 5th ed., 7.8.5 & 15.10.1. * - * For portability, we also escape escape all non-ASCII characters. + * For portability, we also escape escape all control and non-ASCII + * characters. */ return s - .replace(/\\/g, '\\\\') // backslash - .replace(/\0/g, '\\0') // null, IE needs this - .replace(/\//g, '\\/') // closing slash - .replace(/]/g, '\\]') // closing bracket - .replace(/-/g, '\\-') // dash - .replace(/\r/g, '\\r') // carriage return - .replace(/\n/g, '\\n') // line feed - .replace(/[\x80-\uFFFF]/g, escape) // non-ASCII characters + .replace(/\\/g, '\\\\') // backslash + .replace(/\//g, '\\/') // closing slash + .replace(/\]/g, '\\]') // closing bracket + .replace(/-/g, '\\-') // dash + .replace(/\0/g, '\\0') // null + .replace(/\t/g, '\\t') // horizontal tab + .replace(/\n/g, '\\n') // line feed + .replace(/\v/g, '\\x0B') // vertical tab + .replace(/\f/g, '\\f') // form feed + .replace(/\r/g, '\\r') // carriage return + .replace(/[\x01-\x08\x0E-\x1F\x80-\uFFFF]/g, escape); } /* @@ -156,10 +214,41 @@ function quoteForRegexpClass(s) { function buildNodeVisitor(functions) { return function(node) { return functions[node.type].apply(null, arguments); - } + }; +} + +function findRuleByName(ast, name) { + return find(ast.rules, function(r) { return r.name === name; }); } PEG.parser = (function(){ - /* Generated by PEG.js 0.6.2 (http://pegjs.majda.cz/). */ + /* + * Generated by PEG.js 0.7.0. + * + * http://pegjs.majda.cz/ + */ + + function quote(s) { + /* + * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a + * string literal except for the closing quote character, backslash, + * carriage return, line separator, paragraph separator, and line feed. + * Any character may appear in the form of an escape sequence. + * + * For portability, we also escape escape all control and non-ASCII + * characters. Note that "\0" and "\v" escape sequences are not used + * because JSHint does not like the first and IE the second. + */ + return '"' + s + .replace(/\\/g, '\\\\') // backslash + .replace(/"/g, '\\"') // closing quote character + .replace(/\x08/g, '\\b') // backspace + .replace(/\t/g, '\\t') // horizontal tab + .replace(/\n/g, '\\n') // line feed + .replace(/\f/g, '\\f') // form feed + .replace(/\r/g, '\\r') // carriage return + .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape) + + '"'; + } var result = { /* @@ -170,61 +259,62 @@ PEG.parser = (function(){ */ parse: function(input, startRule) { var parseFunctions = { - "__": parse___, - "action": parse_action, - "and": parse_and, - "braced": parse_braced, - "bracketDelimitedCharacter": parse_bracketDelimitedCharacter, + "grammar": parse_grammar, + "initializer": parse_initializer, + "rule": parse_rule, "choice": parse_choice, - "class": parse_class, - "classCharacter": parse_classCharacter, - "classCharacterRange": parse_classCharacterRange, + "sequence": parse_sequence, + "labeled": parse_labeled, + "prefixed": parse_prefixed, + "suffixed": parse_suffixed, + "primary": parse_primary, + "action": parse_action, + "braced": parse_braced, + "nonBraceCharacters": parse_nonBraceCharacters, + "nonBraceCharacter": parse_nonBraceCharacter, + "equals": parse_equals, "colon": parse_colon, - "comment": parse_comment, - "digit": parse_digit, + "semicolon": parse_semicolon, + "slash": parse_slash, + "and": parse_and, + "not": parse_not, + "question": parse_question, + "star": parse_star, + "plus": parse_plus, + "lparen": parse_lparen, + "rparen": parse_rparen, "dot": parse_dot, + "identifier": parse_identifier, + "literal": parse_literal, + "string": parse_string, + "doubleQuotedString": parse_doubleQuotedString, "doubleQuotedCharacter": parse_doubleQuotedCharacter, - "doubleQuotedLiteral": parse_doubleQuotedLiteral, + "simpleDoubleQuotedCharacter": parse_simpleDoubleQuotedCharacter, + "singleQuotedString": parse_singleQuotedString, + "singleQuotedCharacter": parse_singleQuotedCharacter, + "simpleSingleQuotedCharacter": parse_simpleSingleQuotedCharacter, + "class": parse_class, + "classCharacterRange": parse_classCharacterRange, + "classCharacter": parse_classCharacter, + "bracketDelimitedCharacter": parse_bracketDelimitedCharacter, + "simpleBracketDelimitedCharacter": parse_simpleBracketDelimitedCharacter, + "simpleEscapeSequence": parse_simpleEscapeSequence, + "zeroEscapeSequence": parse_zeroEscapeSequence, + "hexEscapeSequence": parse_hexEscapeSequence, + "unicodeEscapeSequence": parse_unicodeEscapeSequence, + "eolEscapeSequence": parse_eolEscapeSequence, + "digit": parse_digit, + "hexDigit": parse_hexDigit, + "letter": parse_letter, + "lowerCaseLetter": parse_lowerCaseLetter, + "upperCaseLetter": parse_upperCaseLetter, + "__": parse___, + "comment": parse_comment, + "singleLineComment": parse_singleLineComment, + "multiLineComment": parse_multiLineComment, "eol": parse_eol, "eolChar": parse_eolChar, - "eolEscapeSequence": parse_eolEscapeSequence, - "equals": parse_equals, - "grammar": parse_grammar, - "hexDigit": parse_hexDigit, - "hexEscapeSequence": parse_hexEscapeSequence, - "identifier": parse_identifier, - "initializer": parse_initializer, - "labeled": parse_labeled, - "letter": parse_letter, - "literal": parse_literal, - "lowerCaseLetter": parse_lowerCaseLetter, - "lparen": parse_lparen, - "multiLineComment": parse_multiLineComment, - "nonBraceCharacter": parse_nonBraceCharacter, - "nonBraceCharacters": parse_nonBraceCharacters, - "not": parse_not, - "plus": parse_plus, - "prefixed": parse_prefixed, - "primary": parse_primary, - "question": parse_question, - "rparen": parse_rparen, - "rule": parse_rule, - "semicolon": parse_semicolon, - "sequence": parse_sequence, - "simpleBracketDelimitedCharacter": parse_simpleBracketDelimitedCharacter, - "simpleDoubleQuotedCharacter": parse_simpleDoubleQuotedCharacter, - "simpleEscapeSequence": parse_simpleEscapeSequence, - "simpleSingleQuotedCharacter": parse_simpleSingleQuotedCharacter, - "singleLineComment": parse_singleLineComment, - "singleQuotedCharacter": parse_singleQuotedCharacter, - "singleQuotedLiteral": parse_singleQuotedLiteral, - "slash": parse_slash, - "star": parse_star, - "suffixed": parse_suffixed, - "unicodeEscapeSequence": parse_unicodeEscapeSequence, - "upperCaseLetter": parse_upperCaseLetter, - "whitespace": parse_whitespace, - "zeroEscapeSequence": parse_zeroEscapeSequence + "whitespace": parse_whitespace }; if (startRule !== undefined) { @@ -236,10 +326,9 @@ PEG.parser = (function(){ } var pos = 0; - var reportMatchFailures = true; - var rightmostMatchFailuresPos = 0; - var rightmostMatchFailuresExpected = []; - var cache = {}; + var reportFailures = 0; + var rightmostFailuresPos = 0; + var rightmostFailuresExpected = []; function padLeft(input, padding, length) { var result = input; @@ -254,3795 +343,2728 @@ PEG.parser = (function(){ function escape(ch) { var charCode = ch.charCodeAt(0); + var escapeChar; + var length; if (charCode <= 0xFF) { - var escapeChar = 'x'; - var length = 2; + escapeChar = 'x'; + length = 2; } else { - var escapeChar = 'u'; - var length = 4; + escapeChar = 'u'; + length = 4; } return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length); } - function quote(s) { - /* - * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a - * string literal except for the closing quote character, backslash, - * carriage return, line separator, paragraph separator, and line feed. - * Any character may appear in the form of an escape sequence. - */ - return '"' + s - .replace(/\\/g, '\\\\') // backslash - .replace(/"/g, '\\"') // closing quote character - .replace(/\r/g, '\\r') // carriage return - .replace(/\n/g, '\\n') // line feed - .replace(/[\x80-\uFFFF]/g, escape) // non-ASCII characters - + '"'; - } - function matchFailed(failure) { - if (pos < rightmostMatchFailuresPos) { + if (pos < rightmostFailuresPos) { return; } - if (pos > rightmostMatchFailuresPos) { - rightmostMatchFailuresPos = pos; - rightmostMatchFailuresExpected = []; + if (pos > rightmostFailuresPos) { + rightmostFailuresPos = pos; + rightmostFailuresExpected = []; } - rightmostMatchFailuresExpected.push(failure); + rightmostFailuresExpected.push(failure); } function parse_grammar() { - var cacheKey = 'grammar@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2, result3; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - var result3 = parse___(); - if (result3 !== null) { - var result7 = parse_initializer(); - var result4 = result7 !== null ? result7 : ''; - if (result4 !== null) { - var result6 = parse_rule(); - if (result6 !== null) { - var result5 = []; - while (result6 !== null) { - result5.push(result6); - var result6 = parse_rule(); + pos0 = pos; + pos1 = pos; + result0 = parse___(); + if (result0 !== null) { + result1 = parse_initializer(); + result1 = result1 !== null ? result1 : ""; + if (result1 !== null) { + result3 = parse_rule(); + if (result3 !== null) { + result2 = []; + while (result3 !== null) { + result2.push(result3); + result3 = parse_rule(); } } else { - var result5 = null; + result2 = null; } - if (result5 !== null) { - var result1 = [result3, result4, result5]; + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(initializer, rules) { - var rulesConverted = {}; - each(rules, function(rule) { rulesConverted[rule.name] = rule; }); - - return { - type: "grammar", - initializer: initializer !== "" ? initializer : null, - rules: rulesConverted, - startRule: rules[0].name - } - })(result1[1], result1[2]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, initializer, rules) { + return { + type: "grammar", + initializer: initializer !== "" ? initializer : null, + rules: rules, + startRule: rules[0].name + }; + })(pos0, result0[1], result0[2]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_initializer() { - var cacheKey = 'initializer@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - var result3 = parse_action(); - if (result3 !== null) { - var result5 = parse_semicolon(); - var result4 = result5 !== null ? result5 : ''; - if (result4 !== null) { - var result1 = [result3, result4]; + pos0 = pos; + pos1 = pos; + result0 = parse_action(); + if (result0 !== null) { + result1 = parse_semicolon(); + result1 = result1 !== null ? result1 : ""; + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(code) { - return { - type: "initializer", - code: code - }; - })(result1[0]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, code) { + return { + type: "initializer", + code: code + }; + })(pos0, result0[0]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_rule() { - var cacheKey = 'rule@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2, result3, result4; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - var result3 = parse_identifier(); - if (result3 !== null) { - var result10 = parse_literal(); - if (result10 !== null) { - var result4 = result10; - } else { - if (input.substr(pos, 0) === "") { - var result9 = ""; - pos += 0; - } else { - var result9 = null; - if (reportMatchFailures) { - matchFailed("\"\""); - } - } - if (result9 !== null) { - var result4 = result9; - } else { - var result4 = null;; - }; - } - if (result4 !== null) { - var result5 = parse_equals(); - if (result5 !== null) { - var result6 = parse_choice(); - if (result6 !== null) { - var result8 = parse_semicolon(); - var result7 = result8 !== null ? result8 : ''; - if (result7 !== null) { - var result1 = [result3, result4, result5, result6, result7]; + pos0 = pos; + pos1 = pos; + result0 = parse_identifier(); + if (result0 !== null) { + result1 = parse_string(); + result1 = result1 !== null ? result1 : ""; + if (result1 !== null) { + result2 = parse_equals(); + if (result2 !== null) { + result3 = parse_choice(); + if (result3 !== null) { + result4 = parse_semicolon(); + result4 = result4 !== null ? result4 : ""; + if (result4 !== null) { + result0 = [result0, result1, result2, result3, result4]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(name, displayName, expression) { - return { - type: "rule", - name: name, - displayName: displayName !== "" ? displayName : null, - expression: expression - }; - })(result1[0], result1[1], result1[3]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, name, displayName, expression) { + return { + type: "rule", + name: name, + displayName: displayName !== "" ? displayName : null, + expression: expression + }; + })(pos0, result0[0], result0[1], result0[3]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_choice() { - var cacheKey = 'choice@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2, result3; + var pos0, pos1, pos2; - - var savedPos0 = pos; - var savedPos1 = pos; - var result3 = parse_sequence(); - if (result3 !== null) { - var result4 = []; - var savedPos2 = pos; - var result6 = parse_slash(); - if (result6 !== null) { - var result7 = parse_sequence(); - if (result7 !== null) { - var result5 = [result6, result7]; + pos0 = pos; + pos1 = pos; + result0 = parse_sequence(); + if (result0 !== null) { + result1 = []; + pos2 = pos; + result2 = parse_slash(); + if (result2 !== null) { + result3 = parse_sequence(); + if (result3 !== null) { + result2 = [result2, result3]; } else { - var result5 = null; - pos = savedPos2; + result2 = null; + pos = pos2; } } else { - var result5 = null; - pos = savedPos2; + result2 = null; + pos = pos2; } - while (result5 !== null) { - result4.push(result5); - var savedPos2 = pos; - var result6 = parse_slash(); - if (result6 !== null) { - var result7 = parse_sequence(); - if (result7 !== null) { - var result5 = [result6, result7]; + while (result2 !== null) { + result1.push(result2); + pos2 = pos; + result2 = parse_slash(); + if (result2 !== null) { + result3 = parse_sequence(); + if (result3 !== null) { + result2 = [result2, result3]; } else { - var result5 = null; - pos = savedPos2; + result2 = null; + pos = pos2; } } else { - var result5 = null; - pos = savedPos2; + result2 = null; + pos = pos2; } } - if (result4 !== null) { - var result1 = [result3, result4]; + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(head, tail) { - if (tail.length > 0) { - var alternatives = [head].concat(map( - tail, - function(element) { return element[1]; } - )); - return { - type: "choice", - alternatives: alternatives - } - } else { - return head; - } - })(result1[0], result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, head, tail) { + if (tail.length > 0) { + var alternatives = [head].concat(map( + tail, + function(element) { return element[1]; } + )); + return { + type: "choice", + alternatives: alternatives + }; + } else { + return head; + } + })(pos0, result0[0], result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_sequence() { - var cacheKey = 'sequence@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos1 = pos; - var savedPos2 = pos; - var result8 = []; - var result10 = parse_labeled(); - while (result10 !== null) { - result8.push(result10); - var result10 = parse_labeled(); + pos0 = pos; + pos1 = pos; + result0 = []; + result1 = parse_labeled(); + while (result1 !== null) { + result0.push(result1); + result1 = parse_labeled(); } - if (result8 !== null) { - var result9 = parse_action(); - if (result9 !== null) { - var result6 = [result8, result9]; + if (result0 !== null) { + result1 = parse_action(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result6 = null; - pos = savedPos2; + result0 = null; + pos = pos1; } } else { - var result6 = null; - pos = savedPos2; + result0 = null; + pos = pos1; } - var result7 = result6 !== null - ? (function(elements, code) { - var expression = elements.length != 1 + if (result0 !== null) { + result0 = (function(offset, elements, code) { + var expression = elements.length !== 1 + ? { + type: "sequence", + elements: elements + } + : elements[0]; + return { + type: "action", + expression: expression, + code: code + }; + })(pos0, result0[0], result0[1]); + } + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + pos0 = pos; + result0 = []; + result1 = parse_labeled(); + while (result1 !== null) { + result0.push(result1); + result1 = parse_labeled(); + } + if (result0 !== null) { + result0 = (function(offset, elements) { + return elements.length !== 1 ? { type: "sequence", elements: elements } : elements[0]; - return { - type: "action", - expression: expression, - code: code - }; - })(result6[0], result6[1]) - : null; - if (result7 !== null) { - var result5 = result7; - } else { - var result5 = null; - pos = savedPos1; - } - if (result5 !== null) { - var result0 = result5; - } else { - var savedPos0 = pos; - var result2 = []; - var result4 = parse_labeled(); - while (result4 !== null) { - result2.push(result4); - var result4 = parse_labeled(); + })(pos0, result0); } - var result3 = result2 !== null - ? (function(elements) { - return elements.length != 1 - ? { - type: "sequence", - elements: elements - } - : elements[0]; - })(result2) - : null; - if (result3 !== null) { - var result1 = result3; - } else { - var result1 = null; - pos = savedPos0; + if (result0 === null) { + pos = pos0; } - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_labeled() { - var cacheKey = 'labeled@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - var result5 = parse_identifier(); - if (result5 !== null) { - var result6 = parse_colon(); - if (result6 !== null) { - var result7 = parse_prefixed(); - if (result7 !== null) { - var result3 = [result5, result6, result7]; + pos0 = pos; + pos1 = pos; + result0 = parse_identifier(); + if (result0 !== null) { + result1 = parse_colon(); + if (result1 !== null) { + result2 = parse_prefixed(); + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result3 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result3 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result3 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result4 = result3 !== null - ? (function(label, expression) { - return { - type: "labeled", - label: label, - expression: expression - }; - })(result3[0], result3[2]) - : null; - if (result4 !== null) { - var result2 = result4; - } else { - var result2 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, label, expression) { + return { + type: "labeled", + label: label, + expression: expression + }; + })(pos0, result0[0], result0[2]); } - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_prefixed(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + result0 = parse_prefixed(); } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_prefixed() { - var cacheKey = 'prefixed@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos6 = pos; - var savedPos7 = pos; - var result20 = parse_and(); - if (result20 !== null) { - var result21 = parse_action(); - if (result21 !== null) { - var result18 = [result20, result21]; + pos0 = pos; + pos1 = pos; + result0 = parse_and(); + if (result0 !== null) { + result1 = parse_action(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result18 = null; - pos = savedPos7; + result0 = null; + pos = pos1; } } else { - var result18 = null; - pos = savedPos7; + result0 = null; + pos = pos1; } - var result19 = result18 !== null - ? (function(code) { - return { - type: "semantic_and", - code: code - }; - })(result18[1]) - : null; - if (result19 !== null) { - var result17 = result19; - } else { - var result17 = null; - pos = savedPos6; + if (result0 !== null) { + result0 = (function(offset, code) { + return { + type: "semantic_and", + code: code + }; + })(pos0, result0[1]); } - if (result17 !== null) { - var result0 = result17; - } else { - var savedPos4 = pos; - var savedPos5 = pos; - var result15 = parse_and(); - if (result15 !== null) { - var result16 = parse_suffixed(); - if (result16 !== null) { - var result13 = [result15, result16]; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + pos0 = pos; + pos1 = pos; + result0 = parse_and(); + if (result0 !== null) { + result1 = parse_suffixed(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result13 = null; - pos = savedPos5; + result0 = null; + pos = pos1; } } else { - var result13 = null; - pos = savedPos5; + result0 = null; + pos = pos1; } - var result14 = result13 !== null - ? (function(expression) { - return { - type: "simple_and", - expression: expression - }; - })(result13[1]) - : null; - if (result14 !== null) { - var result12 = result14; - } else { - var result12 = null; - pos = savedPos4; + if (result0 !== null) { + result0 = (function(offset, expression) { + return { + type: "simple_and", + expression: expression + }; + })(pos0, result0[1]); } - if (result12 !== null) { - var result0 = result12; - } else { - var savedPos2 = pos; - var savedPos3 = pos; - var result10 = parse_not(); - if (result10 !== null) { - var result11 = parse_action(); - if (result11 !== null) { - var result8 = [result10, result11]; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + pos0 = pos; + pos1 = pos; + result0 = parse_not(); + if (result0 !== null) { + result1 = parse_action(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result8 = null; - pos = savedPos3; + result0 = null; + pos = pos1; } } else { - var result8 = null; - pos = savedPos3; + result0 = null; + pos = pos1; } - var result9 = result8 !== null - ? (function(code) { - return { - type: "semantic_not", - code: code - }; - })(result8[1]) - : null; - if (result9 !== null) { - var result7 = result9; - } else { - var result7 = null; - pos = savedPos2; + if (result0 !== null) { + result0 = (function(offset, code) { + return { + type: "semantic_not", + code: code + }; + })(pos0, result0[1]); } - if (result7 !== null) { - var result0 = result7; - } else { - var savedPos0 = pos; - var savedPos1 = pos; - var result5 = parse_not(); - if (result5 !== null) { - var result6 = parse_suffixed(); - if (result6 !== null) { - var result3 = [result5, result6]; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + pos0 = pos; + pos1 = pos; + result0 = parse_not(); + if (result0 !== null) { + result1 = parse_suffixed(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result3 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result3 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result4 = result3 !== null - ? (function(expression) { - return { - type: "simple_not", - expression: expression - }; - })(result3[1]) - : null; - if (result4 !== null) { - var result2 = result4; - } else { - var result2 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, expression) { + return { + type: "simple_not", + expression: expression + }; + })(pos0, result0[1]); } - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_suffixed(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - }; - }; - }; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + result0 = parse_suffixed(); + } + } + } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_suffixed() { - var cacheKey = 'suffixed@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos4 = pos; - var savedPos5 = pos; - var result15 = parse_primary(); - if (result15 !== null) { - var result16 = parse_question(); - if (result16 !== null) { - var result13 = [result15, result16]; + pos0 = pos; + pos1 = pos; + result0 = parse_primary(); + if (result0 !== null) { + result1 = parse_question(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result13 = null; - pos = savedPos5; + result0 = null; + pos = pos1; } } else { - var result13 = null; - pos = savedPos5; + result0 = null; + pos = pos1; } - var result14 = result13 !== null - ? (function(expression) { + if (result0 !== null) { + result0 = (function(offset, expression) { + return { + type: "optional", + expression: expression + }; + })(pos0, result0[0]); + } + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + pos0 = pos; + pos1 = pos; + result0 = parse_primary(); + if (result0 !== null) { + result1 = parse_star(); + if (result1 !== null) { + result0 = [result0, result1]; + } else { + result0 = null; + pos = pos1; + } + } else { + result0 = null; + pos = pos1; + } + if (result0 !== null) { + result0 = (function(offset, expression) { return { - type: "optional", + type: "zero_or_more", expression: expression }; - })(result13[0]) - : null; - if (result14 !== null) { - var result12 = result14; - } else { - var result12 = null; - pos = savedPos4; - } - if (result12 !== null) { - var result0 = result12; - } else { - var savedPos2 = pos; - var savedPos3 = pos; - var result10 = parse_primary(); - if (result10 !== null) { - var result11 = parse_star(); - if (result11 !== null) { - var result8 = [result10, result11]; - } else { - var result8 = null; - pos = savedPos3; - } - } else { - var result8 = null; - pos = savedPos3; + })(pos0, result0[0]); } - var result9 = result8 !== null - ? (function(expression) { - return { - type: "zero_or_more", - expression: expression - }; - })(result8[0]) - : null; - if (result9 !== null) { - var result7 = result9; - } else { - var result7 = null; - pos = savedPos2; + if (result0 === null) { + pos = pos0; } - if (result7 !== null) { - var result0 = result7; - } else { - var savedPos0 = pos; - var savedPos1 = pos; - var result5 = parse_primary(); - if (result5 !== null) { - var result6 = parse_plus(); - if (result6 !== null) { - var result3 = [result5, result6]; + if (result0 === null) { + pos0 = pos; + pos1 = pos; + result0 = parse_primary(); + if (result0 !== null) { + result1 = parse_plus(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result3 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result3 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result4 = result3 !== null - ? (function(expression) { - return { - type: "one_or_more", - expression: expression - }; - })(result3[0]) - : null; - if (result4 !== null) { - var result2 = result4; - } else { - var result2 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, expression) { + return { + type: "one_or_more", + expression: expression + }; + })(pos0, result0[0]); } - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_primary(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - }; - }; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + result0 = parse_primary(); + } + } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_primary() { - var cacheKey = 'primary@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1, pos2, pos3; - - var savedPos4 = pos; - var savedPos5 = pos; - var result17 = parse_identifier(); - if (result17 !== null) { - var savedPos6 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - var savedPos7 = pos; - var result23 = parse_literal(); - if (result23 !== null) { - var result20 = result23; - } else { - if (input.substr(pos, 0) === "") { - var result22 = ""; - pos += 0; + pos0 = pos; + pos1 = pos; + result0 = parse_identifier(); + if (result0 !== null) { + pos2 = pos; + reportFailures++; + pos3 = pos; + result1 = parse_string(); + result1 = result1 !== null ? result1 : ""; + if (result1 !== null) { + result2 = parse_equals(); + if (result2 !== null) { + result1 = [result1, result2]; } else { - var result22 = null; - if (reportMatchFailures) { - matchFailed("\"\""); - } - } - if (result22 !== null) { - var result20 = result22; - } else { - var result20 = null;; - }; - } - if (result20 !== null) { - var result21 = parse_equals(); - if (result21 !== null) { - var result19 = [result20, result21]; - } else { - var result19 = null; - pos = savedPos7; + result1 = null; + pos = pos3; } } else { - var result19 = null; - pos = savedPos7; + result1 = null; + pos = pos3; } - reportMatchFailures = savedReportMatchFailuresVar0; - if (result19 === null) { - var result18 = ''; + reportFailures--; + if (result1 === null) { + result1 = ""; } else { - var result18 = null; - pos = savedPos6; + result1 = null; + pos = pos2; } - if (result18 !== null) { - var result15 = [result17, result18]; + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result15 = null; - pos = savedPos5; + result0 = null; + pos = pos1; } } else { - var result15 = null; - pos = savedPos5; + result0 = null; + pos = pos1; } - var result16 = result15 !== null - ? (function(name) { - return { - type: "rule_ref", - name: name - }; - })(result15[0]) - : null; - if (result16 !== null) { - var result14 = result16; - } else { - var result14 = null; - pos = savedPos4; + if (result0 !== null) { + result0 = (function(offset, name) { + return { + type: "rule_ref", + name: name + }; + })(pos0, result0[0]); } - if (result14 !== null) { - var result0 = result14; - } else { - var savedPos3 = pos; - var result12 = parse_literal(); - var result13 = result12 !== null - ? (function(value) { - return { - type: "literal", - value: value - }; - })(result12) - : null; - if (result13 !== null) { - var result11 = result13; - } else { - var result11 = null; - pos = savedPos3; - } - if (result11 !== null) { - var result0 = result11; - } else { - var savedPos2 = pos; - var result9 = parse_dot(); - var result10 = result9 !== null - ? (function() { return { type: "any" }; })() - : null; - if (result10 !== null) { - var result8 = result10; - } else { - var result8 = null; - pos = savedPos2; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + result0 = parse_literal(); + if (result0 === null) { + pos0 = pos; + result0 = parse_dot(); + if (result0 !== null) { + result0 = (function(offset) { return { type: "any" }; })(pos0); } - if (result8 !== null) { - var result0 = result8; - } else { - var result7 = parse_class(); - if (result7 !== null) { - var result0 = result7; - } else { - var savedPos0 = pos; - var savedPos1 = pos; - var result4 = parse_lparen(); - if (result4 !== null) { - var result5 = parse_choice(); - if (result5 !== null) { - var result6 = parse_rparen(); - if (result6 !== null) { - var result2 = [result4, result5, result6]; + if (result0 === null) { + pos = pos0; + } + if (result0 === null) { + result0 = parse_class(); + if (result0 === null) { + pos0 = pos; + pos1 = pos; + result0 = parse_lparen(); + if (result0 !== null) { + result1 = parse_choice(); + if (result1 !== null) { + result2 = parse_rparen(); + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result2 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result2 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result2 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result3 = result2 !== null - ? (function(expression) { return expression; })(result2[1]) - : null; - if (result3 !== null) { - var result1 = result3; - } else { - var result1 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, expression) { return expression; })(pos0, result0[1]); } - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - }; - }; - }; + if (result0 === null) { + pos = pos0; + } + } + } + } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_action() { - var cacheKey = 'action@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - var savedReportMatchFailures = reportMatchFailures; - reportMatchFailures = false; - var savedPos0 = pos; - var savedPos1 = pos; - var result3 = parse_braced(); - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + reportFailures++; + pos0 = pos; + pos1 = pos; + result0 = parse_braced(); + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(braced) { return braced.substr(1, braced.length - 2); })(result1[0]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, braced) { return braced.substr(1, braced.length - 2); })(pos0, result0[0]); } - reportMatchFailures = savedReportMatchFailures; - if (reportMatchFailures && result0 === null) { + if (result0 === null) { + pos = pos0; + } + reportFailures--; + if (reportFailures === 0 && result0 === null) { matchFailed("action"); } - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_braced() { - var cacheKey = 'braced@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "{") { - var result3 = "{"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 123) { + result0 = "{"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"{\""); } } - if (result3 !== null) { - var result4 = []; - var result8 = parse_braced(); - if (result8 !== null) { - var result6 = result8; - } else { - var result7 = parse_nonBraceCharacter(); - if (result7 !== null) { - var result6 = result7; - } else { - var result6 = null;; - }; + if (result0 !== null) { + result1 = []; + result2 = parse_braced(); + if (result2 === null) { + result2 = parse_nonBraceCharacter(); } - while (result6 !== null) { - result4.push(result6); - var result8 = parse_braced(); - if (result8 !== null) { - var result6 = result8; - } else { - var result7 = parse_nonBraceCharacter(); - if (result7 !== null) { - var result6 = result7; - } else { - var result6 = null;; - }; + while (result2 !== null) { + result1.push(result2); + result2 = parse_braced(); + if (result2 === null) { + result2 = parse_nonBraceCharacter(); } } - if (result4 !== null) { - if (input.substr(pos, 1) === "}") { - var result5 = "}"; - pos += 1; + if (result1 !== null) { + if (input.charCodeAt(pos) === 125) { + result2 = "}"; + pos++; } else { - var result5 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"}\""); } } - if (result5 !== null) { - var result1 = [result3, result4, result5]; + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(parts) { - return "{" + parts.join("") + "}"; - })(result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, parts) { + return "{" + parts.join("") + "}"; + })(pos0, result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_nonBraceCharacters() { - var cacheKey = 'nonBraceCharacters@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0; - - var savedPos0 = pos; - var result3 = parse_nonBraceCharacter(); - if (result3 !== null) { - var result1 = []; - while (result3 !== null) { - result1.push(result3); - var result3 = parse_nonBraceCharacter(); + pos0 = pos; + result1 = parse_nonBraceCharacter(); + if (result1 !== null) { + result0 = []; + while (result1 !== null) { + result0.push(result1); + result1 = parse_nonBraceCharacter(); } } else { - var result1 = null; + result0 = null; } - var result2 = result1 !== null - ? (function(chars) { return chars.join(""); })(result1) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, chars) { return chars.join(""); })(pos0, result0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_nonBraceCharacter() { - var cacheKey = 'nonBraceCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - - if (input.substr(pos).match(/^[^{}]/) !== null) { - var result0 = input.charAt(pos); + if (/^[^{}]/.test(input.charAt(pos))) { + result0 = input.charAt(pos); pos++; } else { - var result0 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("[^{}]"); } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_equals() { - var cacheKey = 'equals@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "=") { - var result3 = "="; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 61) { + result0 = "="; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"=\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "="; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "="; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_colon() { - var cacheKey = 'colon@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === ":") { - var result3 = ":"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 58) { + result0 = ":"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\":\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return ":"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return ":"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_semicolon() { - var cacheKey = 'semicolon@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === ";") { - var result3 = ";"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 59) { + result0 = ";"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\";\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return ";"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return ";"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_slash() { - var cacheKey = 'slash@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "/") { - var result3 = "/"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 47) { + result0 = "/"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"/\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "/"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "/"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_and() { - var cacheKey = 'and@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "&") { - var result3 = "&"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 38) { + result0 = "&"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"&\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "&"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "&"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_not() { - var cacheKey = 'not@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "!") { - var result3 = "!"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 33) { + result0 = "!"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"!\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "!"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "!"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_question() { - var cacheKey = 'question@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "?") { - var result3 = "?"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 63) { + result0 = "?"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"?\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "?"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "?"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_star() { - var cacheKey = 'star@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "*") { - var result3 = "*"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 42) { + result0 = "*"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"*\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "*"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "*"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_plus() { - var cacheKey = 'plus@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "+") { - var result3 = "+"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 43) { + result0 = "+"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"+\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "+"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "+"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_lparen() { - var cacheKey = 'lparen@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "(") { - var result3 = "("; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 40) { + result0 = "("; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"(\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "("; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "("; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_rparen() { - var cacheKey = 'rparen@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === ")") { - var result3 = ")"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 41) { + result0 = ")"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\")\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return ")"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return ")"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_dot() { - var cacheKey = 'dot@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === ".") { - var result3 = "."; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 46) { + result0 = "."; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\".\""); } } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "."; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "."; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_identifier() { - var cacheKey = 'identifier@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1; - var savedReportMatchFailures = reportMatchFailures; - reportMatchFailures = false; - var savedPos0 = pos; - var savedPos1 = pos; - var result13 = parse_letter(); - if (result13 !== null) { - var result3 = result13; - } else { - if (input.substr(pos, 1) === "_") { - var result12 = "_"; - pos += 1; + reportFailures++; + pos0 = pos; + pos1 = pos; + result0 = parse_letter(); + if (result0 === null) { + if (input.charCodeAt(pos) === 95) { + result0 = "_"; + pos++; } else { - var result12 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"_\""); } } - if (result12 !== null) { - var result3 = result12; - } else { - if (input.substr(pos, 1) === "$") { - var result11 = "$"; - pos += 1; + if (result0 === null) { + if (input.charCodeAt(pos) === 36) { + result0 = "$"; + pos++; } else { - var result11 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"$\""); } } - if (result11 !== null) { - var result3 = result11; - } else { - var result3 = null;; - }; - }; + } } - if (result3 !== null) { - var result4 = []; - var result10 = parse_letter(); - if (result10 !== null) { - var result6 = result10; - } else { - var result9 = parse_digit(); - if (result9 !== null) { - var result6 = result9; - } else { - if (input.substr(pos, 1) === "_") { - var result8 = "_"; - pos += 1; + if (result0 !== null) { + result1 = []; + result2 = parse_letter(); + if (result2 === null) { + result2 = parse_digit(); + if (result2 === null) { + if (input.charCodeAt(pos) === 95) { + result2 = "_"; + pos++; } else { - var result8 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"_\""); } } - if (result8 !== null) { - var result6 = result8; - } else { - if (input.substr(pos, 1) === "$") { - var result7 = "$"; - pos += 1; + if (result2 === null) { + if (input.charCodeAt(pos) === 36) { + result2 = "$"; + pos++; } else { - var result7 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"$\""); } } - if (result7 !== null) { - var result6 = result7; - } else { - var result6 = null;; - }; - }; - }; + } + } } - while (result6 !== null) { - result4.push(result6); - var result10 = parse_letter(); - if (result10 !== null) { - var result6 = result10; - } else { - var result9 = parse_digit(); - if (result9 !== null) { - var result6 = result9; - } else { - if (input.substr(pos, 1) === "_") { - var result8 = "_"; - pos += 1; + while (result2 !== null) { + result1.push(result2); + result2 = parse_letter(); + if (result2 === null) { + result2 = parse_digit(); + if (result2 === null) { + if (input.charCodeAt(pos) === 95) { + result2 = "_"; + pos++; } else { - var result8 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"_\""); } } - if (result8 !== null) { - var result6 = result8; - } else { - if (input.substr(pos, 1) === "$") { - var result7 = "$"; - pos += 1; + if (result2 === null) { + if (input.charCodeAt(pos) === 36) { + result2 = "$"; + pos++; } else { - var result7 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"$\""); } } - if (result7 !== null) { - var result6 = result7; - } else { - var result6 = null;; - }; - }; - }; + } + } } } - if (result4 !== null) { - var result5 = parse___(); - if (result5 !== null) { - var result1 = [result3, result4, result5]; + if (result1 !== null) { + result2 = parse___(); + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(head, tail) { - return head + tail.join(""); - })(result1[0], result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, head, tail) { + return head + tail.join(""); + })(pos0, result0[0], result0[1]); } - reportMatchFailures = savedReportMatchFailures; - if (reportMatchFailures && result0 === null) { + if (result0 === null) { + pos = pos0; + } + reportFailures--; + if (reportFailures === 0 && result0 === null) { matchFailed("identifier"); } - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_literal() { - var cacheKey = 'literal@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1; - var savedReportMatchFailures = reportMatchFailures; - reportMatchFailures = false; - var savedPos0 = pos; - var savedPos1 = pos; - var result6 = parse_doubleQuotedLiteral(); - if (result6 !== null) { - var result3 = result6; - } else { - var result5 = parse_singleQuotedLiteral(); - if (result5 !== null) { - var result3 = result5; - } else { - var result3 = null;; - }; + reportFailures++; + pos0 = pos; + pos1 = pos; + result0 = parse_doubleQuotedString(); + if (result0 === null) { + result0 = parse_singleQuotedString(); } - if (result3 !== null) { - var result4 = parse___(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + if (input.charCodeAt(pos) === 105) { + result1 = "i"; + pos++; } else { - var result1 = null; - pos = savedPos1; + result1 = null; + if (reportFailures === 0) { + matchFailed("\"i\""); + } + } + result1 = result1 !== null ? result1 : ""; + if (result1 !== null) { + result2 = parse___(); + if (result2 !== null) { + result0 = [result0, result1, result2]; + } else { + result0 = null; + pos = pos1; + } + } else { + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(literal) { return literal; })(result1[0]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, value, flags) { + return { + type: "literal", + value: value, + ignoreCase: flags === "i" + }; + })(pos0, result0[0], result0[1]); } - reportMatchFailures = savedReportMatchFailures; - if (reportMatchFailures && result0 === null) { + if (result0 === null) { + pos = pos0; + } + reportFailures--; + if (reportFailures === 0 && result0 === null) { matchFailed("literal"); } - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } - function parse_doubleQuotedLiteral() { - var cacheKey = 'doubleQuotedLiteral@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; + function parse_string() { + var result0, result1; + var pos0, pos1; + + reportFailures++; + pos0 = pos; + pos1 = pos; + result0 = parse_doubleQuotedString(); + if (result0 === null) { + result0 = parse_singleQuotedString(); } - - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "\"") { - var result3 = "\""; - pos += 1; + if (result0 !== null) { + result1 = parse___(); + if (result1 !== null) { + result0 = [result0, result1]; + } else { + result0 = null; + pos = pos1; + } } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + pos = pos1; + } + if (result0 !== null) { + result0 = (function(offset, string) { return string; })(pos0, result0[0]); + } + if (result0 === null) { + pos = pos0; + } + reportFailures--; + if (reportFailures === 0 && result0 === null) { + matchFailed("string"); + } + return result0; + } + + function parse_doubleQuotedString() { + var result0, result1, result2; + var pos0, pos1; + + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 34) { + result0 = "\""; + pos++; + } else { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\"\""); } } - if (result3 !== null) { - var result4 = []; - var result6 = parse_doubleQuotedCharacter(); - while (result6 !== null) { - result4.push(result6); - var result6 = parse_doubleQuotedCharacter(); + if (result0 !== null) { + result1 = []; + result2 = parse_doubleQuotedCharacter(); + while (result2 !== null) { + result1.push(result2); + result2 = parse_doubleQuotedCharacter(); } - if (result4 !== null) { - if (input.substr(pos, 1) === "\"") { - var result5 = "\""; - pos += 1; + if (result1 !== null) { + if (input.charCodeAt(pos) === 34) { + result2 = "\""; + pos++; } else { - var result5 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"\\\"\""); } } - if (result5 !== null) { - var result1 = [result3, result4, result5]; + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(chars) { return chars.join(""); })(result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, chars) { return chars.join(""); })(pos0, result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_doubleQuotedCharacter() { - var cacheKey = 'doubleQuotedCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; + var result0; + + result0 = parse_simpleDoubleQuotedCharacter(); + if (result0 === null) { + result0 = parse_simpleEscapeSequence(); + if (result0 === null) { + result0 = parse_zeroEscapeSequence(); + if (result0 === null) { + result0 = parse_hexEscapeSequence(); + if (result0 === null) { + result0 = parse_unicodeEscapeSequence(); + if (result0 === null) { + result0 = parse_eolEscapeSequence(); + } + } + } + } } - - - var result6 = parse_simpleDoubleQuotedCharacter(); - if (result6 !== null) { - var result0 = result6; - } else { - var result5 = parse_simpleEscapeSequence(); - if (result5 !== null) { - var result0 = result5; - } else { - var result4 = parse_zeroEscapeSequence(); - if (result4 !== null) { - var result0 = result4; - } else { - var result3 = parse_hexEscapeSequence(); - if (result3 !== null) { - var result0 = result3; - } else { - var result2 = parse_unicodeEscapeSequence(); - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_eolEscapeSequence(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - }; - }; - }; - }; - } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_simpleDoubleQuotedCharacter() { - var cacheKey = 'simpleDoubleQuotedCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1, pos2; - - var savedPos0 = pos; - var savedPos1 = pos; - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - if (input.substr(pos, 1) === "\"") { - var result8 = "\""; - pos += 1; + pos0 = pos; + pos1 = pos; + pos2 = pos; + reportFailures++; + if (input.charCodeAt(pos) === 34) { + result0 = "\""; + pos++; } else { - var result8 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\"\""); } } - if (result8 !== null) { - var result5 = result8; - } else { - if (input.substr(pos, 1) === "\\") { - var result7 = "\\"; - pos += 1; + if (result0 === null) { + if (input.charCodeAt(pos) === 92) { + result0 = "\\"; + pos++; } else { - var result7 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\\""); } } - if (result7 !== null) { - var result5 = result7; - } else { - var result6 = parse_eolChar(); - if (result6 !== null) { - var result5 = result6; - } else { - var result5 = null;; - }; - }; + if (result0 === null) { + result0 = parse_eolChar(); + } } - reportMatchFailures = savedReportMatchFailuresVar0; - if (result5 === null) { - var result3 = ''; + reportFailures--; + if (result0 === null) { + result0 = ""; } else { - var result3 = null; - pos = savedPos2; + result0 = null; + pos = pos2; } - if (result3 !== null) { + if (result0 !== null) { if (input.length > pos) { - var result4 = input.charAt(pos); + result1 = input.charAt(pos); pos++; } else { - var result4 = null; - if (reportMatchFailures) { - matchFailed('any character'); + result1 = null; + if (reportFailures === 0) { + matchFailed("any character"); } } - if (result4 !== null) { - var result1 = [result3, result4]; + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(char_) { return char_; })(result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, char_) { return char_; })(pos0, result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } - function parse_singleQuotedLiteral() { - var cacheKey = 'singleQuotedLiteral@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + function parse_singleQuotedString() { + var result0, result1, result2; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "'") { - var result3 = "'"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 39) { + result0 = "'"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"'\""); } } - if (result3 !== null) { - var result4 = []; - var result6 = parse_singleQuotedCharacter(); - while (result6 !== null) { - result4.push(result6); - var result6 = parse_singleQuotedCharacter(); + if (result0 !== null) { + result1 = []; + result2 = parse_singleQuotedCharacter(); + while (result2 !== null) { + result1.push(result2); + result2 = parse_singleQuotedCharacter(); } - if (result4 !== null) { - if (input.substr(pos, 1) === "'") { - var result5 = "'"; - pos += 1; + if (result1 !== null) { + if (input.charCodeAt(pos) === 39) { + result2 = "'"; + pos++; } else { - var result5 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"'\""); } } - if (result5 !== null) { - var result1 = [result3, result4, result5]; + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(chars) { return chars.join(""); })(result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, chars) { return chars.join(""); })(pos0, result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_singleQuotedCharacter() { - var cacheKey = 'singleQuotedCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; + var result0; + + result0 = parse_simpleSingleQuotedCharacter(); + if (result0 === null) { + result0 = parse_simpleEscapeSequence(); + if (result0 === null) { + result0 = parse_zeroEscapeSequence(); + if (result0 === null) { + result0 = parse_hexEscapeSequence(); + if (result0 === null) { + result0 = parse_unicodeEscapeSequence(); + if (result0 === null) { + result0 = parse_eolEscapeSequence(); + } + } + } + } } - - - var result6 = parse_simpleSingleQuotedCharacter(); - if (result6 !== null) { - var result0 = result6; - } else { - var result5 = parse_simpleEscapeSequence(); - if (result5 !== null) { - var result0 = result5; - } else { - var result4 = parse_zeroEscapeSequence(); - if (result4 !== null) { - var result0 = result4; - } else { - var result3 = parse_hexEscapeSequence(); - if (result3 !== null) { - var result0 = result3; - } else { - var result2 = parse_unicodeEscapeSequence(); - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_eolEscapeSequence(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - }; - }; - }; - }; - } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_simpleSingleQuotedCharacter() { - var cacheKey = 'simpleSingleQuotedCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1, pos2; - - var savedPos0 = pos; - var savedPos1 = pos; - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - if (input.substr(pos, 1) === "'") { - var result8 = "'"; - pos += 1; + pos0 = pos; + pos1 = pos; + pos2 = pos; + reportFailures++; + if (input.charCodeAt(pos) === 39) { + result0 = "'"; + pos++; } else { - var result8 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"'\""); } } - if (result8 !== null) { - var result5 = result8; - } else { - if (input.substr(pos, 1) === "\\") { - var result7 = "\\"; - pos += 1; + if (result0 === null) { + if (input.charCodeAt(pos) === 92) { + result0 = "\\"; + pos++; } else { - var result7 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\\""); } } - if (result7 !== null) { - var result5 = result7; - } else { - var result6 = parse_eolChar(); - if (result6 !== null) { - var result5 = result6; - } else { - var result5 = null;; - }; - }; + if (result0 === null) { + result0 = parse_eolChar(); + } } - reportMatchFailures = savedReportMatchFailuresVar0; - if (result5 === null) { - var result3 = ''; + reportFailures--; + if (result0 === null) { + result0 = ""; } else { - var result3 = null; - pos = savedPos2; + result0 = null; + pos = pos2; } - if (result3 !== null) { + if (result0 !== null) { if (input.length > pos) { - var result4 = input.charAt(pos); + result1 = input.charAt(pos); pos++; } else { - var result4 = null; - if (reportMatchFailures) { - matchFailed('any character'); + result1 = null; + if (reportFailures === 0) { + matchFailed("any character"); } } - if (result4 !== null) { - var result1 = [result3, result4]; + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(char_) { return char_; })(result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, char_) { return char_; })(pos0, result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_class() { - var cacheKey = 'class@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2, result3, result4, result5; + var pos0, pos1; - var savedReportMatchFailures = reportMatchFailures; - reportMatchFailures = false; - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "[") { - var result3 = "["; - pos += 1; + reportFailures++; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 91) { + result0 = "["; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"[\""); } } - if (result3 !== null) { - if (input.substr(pos, 1) === "^") { - var result11 = "^"; - pos += 1; + if (result0 !== null) { + if (input.charCodeAt(pos) === 94) { + result1 = "^"; + pos++; } else { - var result11 = null; - if (reportMatchFailures) { + result1 = null; + if (reportFailures === 0) { matchFailed("\"^\""); } } - var result4 = result11 !== null ? result11 : ''; - if (result4 !== null) { - var result5 = []; - var result10 = parse_classCharacterRange(); - if (result10 !== null) { - var result8 = result10; - } else { - var result9 = parse_classCharacter(); - if (result9 !== null) { - var result8 = result9; - } else { - var result8 = null;; - }; + result1 = result1 !== null ? result1 : ""; + if (result1 !== null) { + result2 = []; + result3 = parse_classCharacterRange(); + if (result3 === null) { + result3 = parse_classCharacter(); } - while (result8 !== null) { - result5.push(result8); - var result10 = parse_classCharacterRange(); - if (result10 !== null) { - var result8 = result10; - } else { - var result9 = parse_classCharacter(); - if (result9 !== null) { - var result8 = result9; - } else { - var result8 = null;; - }; + while (result3 !== null) { + result2.push(result3); + result3 = parse_classCharacterRange(); + if (result3 === null) { + result3 = parse_classCharacter(); } } - if (result5 !== null) { - if (input.substr(pos, 1) === "]") { - var result6 = "]"; - pos += 1; + if (result2 !== null) { + if (input.charCodeAt(pos) === 93) { + result3 = "]"; + pos++; } else { - var result6 = null; - if (reportMatchFailures) { + result3 = null; + if (reportFailures === 0) { matchFailed("\"]\""); } } - if (result6 !== null) { - var result7 = parse___(); - if (result7 !== null) { - var result1 = [result3, result4, result5, result6, result7]; + if (result3 !== null) { + if (input.charCodeAt(pos) === 105) { + result4 = "i"; + pos++; } else { - var result1 = null; - pos = savedPos1; + result4 = null; + if (reportFailures === 0) { + matchFailed("\"i\""); + } + } + result4 = result4 !== null ? result4 : ""; + if (result4 !== null) { + result5 = parse___(); + if (result5 !== null) { + result0 = [result0, result1, result2, result3, result4, result5]; + } else { + result0 = null; + pos = pos1; + } + } else { + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(inverted, parts) { - var partsConverted = map(parts, function(part) { return part.data; }); - var rawText = "[" - + inverted - + map(parts, function(part) { return part.rawText; }).join("") - + "]"; + if (result0 !== null) { + result0 = (function(offset, inverted, parts, flags) { + var partsConverted = map(parts, function(part) { return part.data; }); + var rawText = "[" + + inverted + + map(parts, function(part) { return part.rawText; }).join("") + + "]" + + flags; - return { - type: "class", - inverted: inverted === "^", - parts: partsConverted, - // FIXME: Get the raw text from the input directly. - rawText: rawText - }; - })(result1[1], result1[2]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + return { + type: "class", + inverted: inverted === "^", + ignoreCase: flags === "i", + parts: partsConverted, + // FIXME: Get the raw text from the input directly. + rawText: rawText + }; + })(pos0, result0[1], result0[2], result0[4]); } - reportMatchFailures = savedReportMatchFailures; - if (reportMatchFailures && result0 === null) { + if (result0 === null) { + pos = pos0; + } + reportFailures--; + if (reportFailures === 0 && result0 === null) { matchFailed("character class"); } - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_classCharacterRange() { - var cacheKey = 'classCharacterRange@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - var result3 = parse_classCharacter(); - if (result3 !== null) { - if (input.substr(pos, 1) === "-") { - var result4 = "-"; - pos += 1; + pos0 = pos; + pos1 = pos; + result0 = parse_classCharacter(); + if (result0 !== null) { + if (input.charCodeAt(pos) === 45) { + result1 = "-"; + pos++; } else { - var result4 = null; - if (reportMatchFailures) { + result1 = null; + if (reportFailures === 0) { matchFailed("\"-\""); } } - if (result4 !== null) { - var result5 = parse_classCharacter(); - if (result5 !== null) { - var result1 = [result3, result4, result5]; + if (result1 !== null) { + result2 = parse_classCharacter(); + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(begin, end) { - if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) { - throw new this.SyntaxError( - "Invalid character range: " + begin.rawText + "-" + end.rawText + "." - ); - } + if (result0 !== null) { + result0 = (function(offset, begin, end) { + if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) { + throw new this.SyntaxError( + "Invalid character range: " + begin.rawText + "-" + end.rawText + "." + ); + } - return { - data: [begin.data, end.data], - // FIXME: Get the raw text from the input directly. - rawText: begin.rawText + "-" + end.rawText - } - })(result1[0], result1[2]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + return { + data: [begin.data, end.data], + // FIXME: Get the raw text from the input directly. + rawText: begin.rawText + "-" + end.rawText + }; + })(pos0, result0[0], result0[2]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_classCharacter() { - var cacheKey = 'classCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; + var result0; + var pos0; + + pos0 = pos; + result0 = parse_bracketDelimitedCharacter(); + if (result0 !== null) { + result0 = (function(offset, char_) { + return { + data: char_, + // FIXME: Get the raw text from the input directly. + rawText: quoteForRegexpClass(char_) + }; + })(pos0, result0); } - - - var savedPos0 = pos; - var result1 = parse_bracketDelimitedCharacter(); - var result2 = result1 !== null - ? (function(char_) { - return { - data: char_, - // FIXME: Get the raw text from the input directly. - rawText: quoteForRegexpClass(char_) - }; - })(result1) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_bracketDelimitedCharacter() { - var cacheKey = 'bracketDelimitedCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; + var result0; + + result0 = parse_simpleBracketDelimitedCharacter(); + if (result0 === null) { + result0 = parse_simpleEscapeSequence(); + if (result0 === null) { + result0 = parse_zeroEscapeSequence(); + if (result0 === null) { + result0 = parse_hexEscapeSequence(); + if (result0 === null) { + result0 = parse_unicodeEscapeSequence(); + if (result0 === null) { + result0 = parse_eolEscapeSequence(); + } + } + } + } } - - - var result6 = parse_simpleBracketDelimitedCharacter(); - if (result6 !== null) { - var result0 = result6; - } else { - var result5 = parse_simpleEscapeSequence(); - if (result5 !== null) { - var result0 = result5; - } else { - var result4 = parse_zeroEscapeSequence(); - if (result4 !== null) { - var result0 = result4; - } else { - var result3 = parse_hexEscapeSequence(); - if (result3 !== null) { - var result0 = result3; - } else { - var result2 = parse_unicodeEscapeSequence(); - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_eolEscapeSequence(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - }; - }; - }; - }; - } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_simpleBracketDelimitedCharacter() { - var cacheKey = 'simpleBracketDelimitedCharacter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1, pos2; - - var savedPos0 = pos; - var savedPos1 = pos; - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - if (input.substr(pos, 1) === "]") { - var result8 = "]"; - pos += 1; + pos0 = pos; + pos1 = pos; + pos2 = pos; + reportFailures++; + if (input.charCodeAt(pos) === 93) { + result0 = "]"; + pos++; } else { - var result8 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"]\""); } } - if (result8 !== null) { - var result5 = result8; - } else { - if (input.substr(pos, 1) === "\\") { - var result7 = "\\"; - pos += 1; + if (result0 === null) { + if (input.charCodeAt(pos) === 92) { + result0 = "\\"; + pos++; } else { - var result7 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\\""); } } - if (result7 !== null) { - var result5 = result7; - } else { - var result6 = parse_eolChar(); - if (result6 !== null) { - var result5 = result6; - } else { - var result5 = null;; - }; - }; + if (result0 === null) { + result0 = parse_eolChar(); + } } - reportMatchFailures = savedReportMatchFailuresVar0; - if (result5 === null) { - var result3 = ''; + reportFailures--; + if (result0 === null) { + result0 = ""; } else { - var result3 = null; - pos = savedPos2; + result0 = null; + pos = pos2; } - if (result3 !== null) { + if (result0 !== null) { if (input.length > pos) { - var result4 = input.charAt(pos); + result1 = input.charAt(pos); pos++; } else { - var result4 = null; - if (reportMatchFailures) { - matchFailed('any character'); + result1 = null; + if (reportFailures === 0) { + matchFailed("any character"); } } - if (result4 !== null) { - var result1 = [result3, result4]; + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(char_) { return char_; })(result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, char_) { return char_; })(pos0, result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_simpleEscapeSequence() { - var cacheKey = 'simpleEscapeSequence@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1, pos2; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "\\") { - var result3 = "\\"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 92) { + result0 = "\\"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\\""); } } - if (result3 !== null) { - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - var result10 = parse_digit(); - if (result10 !== null) { - var result6 = result10; - } else { - if (input.substr(pos, 1) === "x") { - var result9 = "x"; - pos += 1; + if (result0 !== null) { + pos2 = pos; + reportFailures++; + result1 = parse_digit(); + if (result1 === null) { + if (input.charCodeAt(pos) === 120) { + result1 = "x"; + pos++; } else { - var result9 = null; - if (reportMatchFailures) { + result1 = null; + if (reportFailures === 0) { matchFailed("\"x\""); } } - if (result9 !== null) { - var result6 = result9; - } else { - if (input.substr(pos, 1) === "u") { - var result8 = "u"; - pos += 1; + if (result1 === null) { + if (input.charCodeAt(pos) === 117) { + result1 = "u"; + pos++; } else { - var result8 = null; - if (reportMatchFailures) { + result1 = null; + if (reportFailures === 0) { matchFailed("\"u\""); } } - if (result8 !== null) { - var result6 = result8; - } else { - var result7 = parse_eolChar(); - if (result7 !== null) { - var result6 = result7; - } else { - var result6 = null;; - }; - }; - }; - } - reportMatchFailures = savedReportMatchFailuresVar0; - if (result6 === null) { - var result4 = ''; - } else { - var result4 = null; - pos = savedPos2; - } - if (result4 !== null) { - if (input.length > pos) { - var result5 = input.charAt(pos); - pos++; - } else { - var result5 = null; - if (reportMatchFailures) { - matchFailed('any character'); + if (result1 === null) { + result1 = parse_eolChar(); } } - if (result5 !== null) { - var result1 = [result3, result4, result5]; + } + reportFailures--; + if (result1 === null) { + result1 = ""; + } else { + result1 = null; + pos = pos2; + } + if (result1 !== null) { + if (input.length > pos) { + result2 = input.charAt(pos); + pos++; } else { - var result1 = null; - pos = savedPos1; + result2 = null; + if (reportFailures === 0) { + matchFailed("any character"); + } + } + if (result2 !== null) { + result0 = [result0, result1, result2]; + } else { + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(char_) { - return char_ - .replace("b", "\b") - .replace("f", "\f") - .replace("n", "\n") - .replace("r", "\r") - .replace("t", "\t") - .replace("v", "\x0B") // IE does not recognize "\v". - })(result1[2]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, char_) { + return char_ + .replace("b", "\b") + .replace("f", "\f") + .replace("n", "\n") + .replace("r", "\r") + .replace("t", "\t") + .replace("v", "\x0B"); // IE does not recognize "\v". + })(pos0, result0[2]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_zeroEscapeSequence() { - var cacheKey = 'zeroEscapeSequence@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1, pos2; - - var savedPos0 = pos; - var savedPos1 = pos; + pos0 = pos; + pos1 = pos; if (input.substr(pos, 2) === "\\0") { - var result3 = "\\0"; + result0 = "\\0"; pos += 2; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\0\""); } } - if (result3 !== null) { - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - var result5 = parse_digit(); - reportMatchFailures = savedReportMatchFailuresVar0; - if (result5 === null) { - var result4 = ''; + if (result0 !== null) { + pos2 = pos; + reportFailures++; + result1 = parse_digit(); + reportFailures--; + if (result1 === null) { + result1 = ""; } else { - var result4 = null; - pos = savedPos2; + result1 = null; + pos = pos2; } - if (result4 !== null) { - var result1 = [result3, result4]; + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function() { return "\0"; })() - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset) { return "\x00"; })(pos0); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_hexEscapeSequence() { - var cacheKey = 'hexEscapeSequence@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; + pos0 = pos; + pos1 = pos; if (input.substr(pos, 2) === "\\x") { - var result3 = "\\x"; + result0 = "\\x"; pos += 2; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\x\""); } } - if (result3 !== null) { - var result4 = parse_hexDigit(); - if (result4 !== null) { - var result5 = parse_hexDigit(); - if (result5 !== null) { - var result1 = [result3, result4, result5]; + if (result0 !== null) { + result1 = parse_hexDigit(); + if (result1 !== null) { + result2 = parse_hexDigit(); + if (result2 !== null) { + result0 = [result0, result1, result2]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(h1, h2) { - return String.fromCharCode(parseInt("0x" + h1 + h2)); - })(result1[1], result1[2]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, h1, h2) { + return String.fromCharCode(parseInt(h1 + h2, 16)); + })(pos0, result0[1], result0[2]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_unicodeEscapeSequence() { - var cacheKey = 'unicodeEscapeSequence@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2, result3, result4; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; + pos0 = pos; + pos1 = pos; if (input.substr(pos, 2) === "\\u") { - var result3 = "\\u"; + result0 = "\\u"; pos += 2; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\u\""); } } - if (result3 !== null) { - var result4 = parse_hexDigit(); - if (result4 !== null) { - var result5 = parse_hexDigit(); - if (result5 !== null) { - var result6 = parse_hexDigit(); - if (result6 !== null) { - var result7 = parse_hexDigit(); - if (result7 !== null) { - var result1 = [result3, result4, result5, result6, result7]; + if (result0 !== null) { + result1 = parse_hexDigit(); + if (result1 !== null) { + result2 = parse_hexDigit(); + if (result2 !== null) { + result3 = parse_hexDigit(); + if (result3 !== null) { + result4 = parse_hexDigit(); + if (result4 !== null) { + result0 = [result0, result1, result2, result3, result4]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(h1, h2, h3, h4) { - return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4)); - })(result1[1], result1[2], result1[3], result1[4]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, h1, h2, h3, h4) { + return String.fromCharCode(parseInt(h1 + h2 + h3 + h4, 16)); + })(pos0, result0[1], result0[2], result0[3], result0[4]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_eolEscapeSequence() { - var cacheKey = 'eolEscapeSequence@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; + var pos0, pos1; - - var savedPos0 = pos; - var savedPos1 = pos; - if (input.substr(pos, 1) === "\\") { - var result3 = "\\"; - pos += 1; + pos0 = pos; + pos1 = pos; + if (input.charCodeAt(pos) === 92) { + result0 = "\\"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\\\\""); } } - if (result3 !== null) { - var result4 = parse_eol(); - if (result4 !== null) { - var result1 = [result3, result4]; + if (result0 !== null) { + result1 = parse_eol(); + if (result1 !== null) { + result0 = [result0, result1]; } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } } else { - var result1 = null; - pos = savedPos1; + result0 = null; + pos = pos1; } - var result2 = result1 !== null - ? (function(eol) { return eol; })(result1[1]) - : null; - if (result2 !== null) { - var result0 = result2; - } else { - var result0 = null; - pos = savedPos0; + if (result0 !== null) { + result0 = (function(offset, eol) { return eol; })(pos0, result0[1]); + } + if (result0 === null) { + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_digit() { - var cacheKey = 'digit@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - - if (input.substr(pos).match(/^[0-9]/) !== null) { - var result0 = input.charAt(pos); + if (/^[0-9]/.test(input.charAt(pos))) { + result0 = input.charAt(pos); pos++; } else { - var result0 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("[0-9]"); } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_hexDigit() { - var cacheKey = 'hexDigit@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - - if (input.substr(pos).match(/^[0-9a-fA-F]/) !== null) { - var result0 = input.charAt(pos); + if (/^[0-9a-fA-F]/.test(input.charAt(pos))) { + result0 = input.charAt(pos); pos++; } else { - var result0 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("[0-9a-fA-F]"); } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_letter() { - var cacheKey = 'letter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; + var result0; + + result0 = parse_lowerCaseLetter(); + if (result0 === null) { + result0 = parse_upperCaseLetter(); } - - - var result2 = parse_lowerCaseLetter(); - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_upperCaseLetter(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_lowerCaseLetter() { - var cacheKey = 'lowerCaseLetter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - - if (input.substr(pos).match(/^[a-z]/) !== null) { - var result0 = input.charAt(pos); + if (/^[a-z]/.test(input.charAt(pos))) { + result0 = input.charAt(pos); pos++; } else { - var result0 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("[a-z]"); } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_upperCaseLetter() { - var cacheKey = 'upperCaseLetter@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - - if (input.substr(pos).match(/^[A-Z]/) !== null) { - var result0 = input.charAt(pos); + if (/^[A-Z]/.test(input.charAt(pos))) { + result0 = input.charAt(pos); pos++; } else { - var result0 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("[A-Z]"); } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse___() { - var cacheKey = '__@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1; - - var result0 = []; - var result4 = parse_whitespace(); - if (result4 !== null) { - var result1 = result4; - } else { - var result3 = parse_eol(); - if (result3 !== null) { - var result1 = result3; - } else { - var result2 = parse_comment(); - if (result2 !== null) { - var result1 = result2; - } else { - var result1 = null;; - }; - }; + result0 = []; + result1 = parse_whitespace(); + if (result1 === null) { + result1 = parse_eol(); + if (result1 === null) { + result1 = parse_comment(); + } } while (result1 !== null) { result0.push(result1); - var result4 = parse_whitespace(); - if (result4 !== null) { - var result1 = result4; - } else { - var result3 = parse_eol(); - if (result3 !== null) { - var result1 = result3; - } else { - var result2 = parse_comment(); - if (result2 !== null) { - var result1 = result2; - } else { - var result1 = null;; - }; - }; + result1 = parse_whitespace(); + if (result1 === null) { + result1 = parse_eol(); + if (result1 === null) { + result1 = parse_comment(); + } } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_comment() { - var cacheKey = 'comment@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - var savedReportMatchFailures = reportMatchFailures; - reportMatchFailures = false; - var result2 = parse_singleLineComment(); - if (result2 !== null) { - var result0 = result2; - } else { - var result1 = parse_multiLineComment(); - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; + reportFailures++; + result0 = parse_singleLineComment(); + if (result0 === null) { + result0 = parse_multiLineComment(); } - reportMatchFailures = savedReportMatchFailures; - if (reportMatchFailures && result0 === null) { + reportFailures--; + if (reportFailures === 0 && result0 === null) { matchFailed("comment"); } - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_singleLineComment() { - var cacheKey = 'singleLineComment@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2, result3; + var pos0, pos1, pos2; - - var savedPos0 = pos; + pos0 = pos; if (input.substr(pos, 2) === "//") { - var result1 = "//"; + result0 = "//"; pos += 2; } else { - var result1 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"//\""); } } - if (result1 !== null) { - var result2 = []; - var savedPos1 = pos; - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - var result6 = parse_eolChar(); - reportMatchFailures = savedReportMatchFailuresVar0; - if (result6 === null) { - var result4 = ''; + if (result0 !== null) { + result1 = []; + pos1 = pos; + pos2 = pos; + reportFailures++; + result2 = parse_eolChar(); + reportFailures--; + if (result2 === null) { + result2 = ""; } else { - var result4 = null; - pos = savedPos2; - } - if (result4 !== null) { - if (input.length > pos) { - var result5 = input.charAt(pos); - pos++; - } else { - var result5 = null; - if (reportMatchFailures) { - matchFailed('any character'); - } - } - if (result5 !== null) { - var result3 = [result4, result5]; - } else { - var result3 = null; - pos = savedPos1; - } - } else { - var result3 = null; - pos = savedPos1; - } - while (result3 !== null) { - result2.push(result3); - var savedPos1 = pos; - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - var result6 = parse_eolChar(); - reportMatchFailures = savedReportMatchFailuresVar0; - if (result6 === null) { - var result4 = ''; - } else { - var result4 = null; - pos = savedPos2; - } - if (result4 !== null) { - if (input.length > pos) { - var result5 = input.charAt(pos); - pos++; - } else { - var result5 = null; - if (reportMatchFailures) { - matchFailed('any character'); - } - } - if (result5 !== null) { - var result3 = [result4, result5]; - } else { - var result3 = null; - pos = savedPos1; - } - } else { - var result3 = null; - pos = savedPos1; - } + result2 = null; + pos = pos2; } if (result2 !== null) { - var result0 = [result1, result2]; + if (input.length > pos) { + result3 = input.charAt(pos); + pos++; + } else { + result3 = null; + if (reportFailures === 0) { + matchFailed("any character"); + } + } + if (result3 !== null) { + result2 = [result2, result3]; + } else { + result2 = null; + pos = pos1; + } } else { - var result0 = null; - pos = savedPos0; + result2 = null; + pos = pos1; + } + while (result2 !== null) { + result1.push(result2); + pos1 = pos; + pos2 = pos; + reportFailures++; + result2 = parse_eolChar(); + reportFailures--; + if (result2 === null) { + result2 = ""; + } else { + result2 = null; + pos = pos2; + } + if (result2 !== null) { + if (input.length > pos) { + result3 = input.charAt(pos); + pos++; + } else { + result3 = null; + if (reportFailures === 0) { + matchFailed("any character"); + } + } + if (result3 !== null) { + result2 = [result2, result3]; + } else { + result2 = null; + pos = pos1; + } + } else { + result2 = null; + pos = pos1; + } + } + if (result1 !== null) { + result0 = [result0, result1]; + } else { + result0 = null; + pos = pos0; } } else { - var result0 = null; - pos = savedPos0; + result0 = null; + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_multiLineComment() { - var cacheKey = 'multiLineComment@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0, result1, result2, result3; + var pos0, pos1, pos2; - - var savedPos0 = pos; + pos0 = pos; if (input.substr(pos, 2) === "/*") { - var result1 = "/*"; + result0 = "/*"; pos += 2; } else { - var result1 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"/*\""); } } - if (result1 !== null) { - var result2 = []; - var savedPos1 = pos; - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; + if (result0 !== null) { + result1 = []; + pos1 = pos; + pos2 = pos; + reportFailures++; if (input.substr(pos, 2) === "*/") { - var result7 = "*/"; + result2 = "*/"; pos += 2; } else { - var result7 = null; - if (reportMatchFailures) { + result2 = null; + if (reportFailures === 0) { matchFailed("\"*/\""); } } - reportMatchFailures = savedReportMatchFailuresVar0; - if (result7 === null) { - var result5 = ''; + reportFailures--; + if (result2 === null) { + result2 = ""; } else { - var result5 = null; - pos = savedPos2; - } - if (result5 !== null) { - if (input.length > pos) { - var result6 = input.charAt(pos); - pos++; - } else { - var result6 = null; - if (reportMatchFailures) { - matchFailed('any character'); - } - } - if (result6 !== null) { - var result4 = [result5, result6]; - } else { - var result4 = null; - pos = savedPos1; - } - } else { - var result4 = null; - pos = savedPos1; - } - while (result4 !== null) { - result2.push(result4); - var savedPos1 = pos; - var savedPos2 = pos; - var savedReportMatchFailuresVar0 = reportMatchFailures; - reportMatchFailures = false; - if (input.substr(pos, 2) === "*/") { - var result7 = "*/"; - pos += 2; - } else { - var result7 = null; - if (reportMatchFailures) { - matchFailed("\"*/\""); - } - } - reportMatchFailures = savedReportMatchFailuresVar0; - if (result7 === null) { - var result5 = ''; - } else { - var result5 = null; - pos = savedPos2; - } - if (result5 !== null) { - if (input.length > pos) { - var result6 = input.charAt(pos); - pos++; - } else { - var result6 = null; - if (reportMatchFailures) { - matchFailed('any character'); - } - } - if (result6 !== null) { - var result4 = [result5, result6]; - } else { - var result4 = null; - pos = savedPos1; - } - } else { - var result4 = null; - pos = savedPos1; - } + result2 = null; + pos = pos2; } if (result2 !== null) { - if (input.substr(pos, 2) === "*/") { - var result3 = "*/"; - pos += 2; + if (input.length > pos) { + result3 = input.charAt(pos); + pos++; } else { - var result3 = null; - if (reportMatchFailures) { - matchFailed("\"*/\""); + result3 = null; + if (reportFailures === 0) { + matchFailed("any character"); } } if (result3 !== null) { - var result0 = [result1, result2, result3]; + result2 = [result2, result3]; } else { - var result0 = null; - pos = savedPos0; + result2 = null; + pos = pos1; } } else { - var result0 = null; - pos = savedPos0; + result2 = null; + pos = pos1; + } + while (result2 !== null) { + result1.push(result2); + pos1 = pos; + pos2 = pos; + reportFailures++; + if (input.substr(pos, 2) === "*/") { + result2 = "*/"; + pos += 2; + } else { + result2 = null; + if (reportFailures === 0) { + matchFailed("\"*/\""); + } + } + reportFailures--; + if (result2 === null) { + result2 = ""; + } else { + result2 = null; + pos = pos2; + } + if (result2 !== null) { + if (input.length > pos) { + result3 = input.charAt(pos); + pos++; + } else { + result3 = null; + if (reportFailures === 0) { + matchFailed("any character"); + } + } + if (result3 !== null) { + result2 = [result2, result3]; + } else { + result2 = null; + pos = pos1; + } + } else { + result2 = null; + pos = pos1; + } + } + if (result1 !== null) { + if (input.substr(pos, 2) === "*/") { + result2 = "*/"; + pos += 2; + } else { + result2 = null; + if (reportFailures === 0) { + matchFailed("\"*/\""); + } + } + if (result2 !== null) { + result0 = [result0, result1, result2]; + } else { + result0 = null; + pos = pos0; + } + } else { + result0 = null; + pos = pos0; } } else { - var result0 = null; - pos = savedPos0; + result0 = null; + pos = pos0; } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_eol() { - var cacheKey = 'eol@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - var savedReportMatchFailures = reportMatchFailures; - reportMatchFailures = false; - if (input.substr(pos, 1) === "\n") { - var result5 = "\n"; - pos += 1; + reportFailures++; + if (input.charCodeAt(pos) === 10) { + result0 = "\n"; + pos++; } else { - var result5 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\n\""); } } - if (result5 !== null) { - var result0 = result5; - } else { + if (result0 === null) { if (input.substr(pos, 2) === "\r\n") { - var result4 = "\r\n"; + result0 = "\r\n"; pos += 2; } else { - var result4 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\r\\n\""); } } - if (result4 !== null) { - var result0 = result4; - } else { - if (input.substr(pos, 1) === "\r") { - var result3 = "\r"; - pos += 1; + if (result0 === null) { + if (input.charCodeAt(pos) === 13) { + result0 = "\r"; + pos++; } else { - var result3 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\r\""); } } - if (result3 !== null) { - var result0 = result3; - } else { - if (input.substr(pos, 1) === "\u2028") { - var result2 = "\u2028"; - pos += 1; + if (result0 === null) { + if (input.charCodeAt(pos) === 8232) { + result0 = "\u2028"; + pos++; } else { - var result2 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\u2028\""); } } - if (result2 !== null) { - var result0 = result2; - } else { - if (input.substr(pos, 1) === "\u2029") { - var result1 = "\u2029"; - pos += 1; + if (result0 === null) { + if (input.charCodeAt(pos) === 8233) { + result0 = "\u2029"; + pos++; } else { - var result1 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("\"\\u2029\""); } } - if (result1 !== null) { - var result0 = result1; - } else { - var result0 = null;; - }; - }; - }; - }; + } + } + } } - reportMatchFailures = savedReportMatchFailures; - if (reportMatchFailures && result0 === null) { + reportFailures--; + if (reportFailures === 0 && result0 === null) { matchFailed("end of line"); } - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_eolChar() { - var cacheKey = 'eolChar@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - - if (input.substr(pos).match(/^[\n\r\u2028\u2029]/) !== null) { - var result0 = input.charAt(pos); + if (/^[\n\r\u2028\u2029]/.test(input.charAt(pos))) { + result0 = input.charAt(pos); pos++; } else { - var result0 = null; - if (reportMatchFailures) { + result0 = null; + if (reportFailures === 0) { matchFailed("[\\n\\r\\u2028\\u2029]"); } } - - - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } function parse_whitespace() { - var cacheKey = 'whitespace@' + pos; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = cachedResult.nextPos; - return cachedResult.result; - } + var result0; - var savedReportMatchFailures = reportMatchFailures; - reportMatchFailures = false; - if (input.substr(pos).match(/^[ \xA0\uFEFF\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]/) !== null) { - var result0 = input.charAt(pos); + reportFailures++; + if (/^[ \t\x0B\f\xA0\uFEFF\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]/.test(input.charAt(pos))) { + result0 = input.charAt(pos); pos++; } else { - var result0 = null; - if (reportMatchFailures) { - matchFailed("[ \\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]"); + result0 = null; + if (reportFailures === 0) { + matchFailed("[ \\t\\x0B\\f\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]"); } } - reportMatchFailures = savedReportMatchFailures; - if (reportMatchFailures && result0 === null) { + reportFailures--; + if (reportFailures === 0 && result0 === null) { matchFailed("whitespace"); } - - cache[cacheKey] = { - nextPos: pos, - result: result0 - }; return result0; } - function buildErrorMessage() { - function buildExpected(failuresExpected) { - failuresExpected.sort(); - var lastFailure = null; - var failuresExpectedUnique = []; - for (var i = 0; i < failuresExpected.length; i++) { - if (failuresExpected[i] !== lastFailure) { - failuresExpectedUnique.push(failuresExpected[i]); - lastFailure = failuresExpected[i]; - } - } + function cleanupExpected(expected) { + expected.sort(); - switch (failuresExpectedUnique.length) { - case 0: - return 'end of input'; - case 1: - return failuresExpectedUnique[0]; - default: - return failuresExpectedUnique.slice(0, failuresExpectedUnique.length - 1).join(', ') - + ' or ' - + failuresExpectedUnique[failuresExpectedUnique.length - 1]; + var lastExpected = null; + var cleanExpected = []; + for (var i = 0; i < expected.length; i++) { + if (expected[i] !== lastExpected) { + cleanExpected.push(expected[i]); + lastExpected = expected[i]; } } - - var expected = buildExpected(rightmostMatchFailuresExpected); - var actualPos = Math.max(pos, rightmostMatchFailuresPos); - var actual = actualPos < input.length - ? quote(input.charAt(actualPos)) - : 'end of input'; - - return 'Expected ' + expected + ' but ' + actual + ' found.'; + return cleanExpected; } function computeErrorPosition() { @@ -4057,13 +3079,13 @@ PEG.parser = (function(){ var column = 1; var seenCR = false; - for (var i = 0; i < rightmostMatchFailuresPos; i++) { + for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) { var ch = input.charAt(i); - if (ch === '\n') { + if (ch === "\n") { if (!seenCR) { line++; } column = 1; seenCR = false; - } else if (ch === '\r' | ch === '\u2028' || ch === '\u2029') { + } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { line++; column = 1; seenCR = true; @@ -4077,7 +3099,6 @@ PEG.parser = (function(){ } - var result = parseFunctions[startRule](); /* @@ -4087,27 +3108,32 @@ PEG.parser = (function(){ * * - |result !== null| * - |pos === input.length| - * - |rightmostMatchFailuresExpected| may or may not contain something + * - |rightmostFailuresExpected| may or may not contain something * * 2. The parser successfully parsed only a part of the input. * * - |result !== null| * - |pos < input.length| - * - |rightmostMatchFailuresExpected| may or may not contain something + * - |rightmostFailuresExpected| may or may not contain something * * 3. The parser did not successfully parse any part of the input. * * - |result === null| * - |pos === 0| - * - |rightmostMatchFailuresExpected| contains at least one failure + * - |rightmostFailuresExpected| contains at least one failure * * All code following this comment (including called functions) must * handle these states. */ if (result === null || pos !== input.length) { + var offset = Math.max(pos, rightmostFailuresPos); + var found = offset < input.length ? input.charAt(offset) : null; var errorPosition = computeErrorPosition(); + throw new this.SyntaxError( - buildErrorMessage(), + cleanupExpected(rightmostFailuresExpected), + found, + offset, errorPosition.line, errorPosition.column ); @@ -4122,9 +3148,33 @@ PEG.parser = (function(){ /* Thrown when a parser encounters a syntax error. */ - result.SyntaxError = function(message, line, column) { - this.name = 'SyntaxError'; - this.message = message; + result.SyntaxError = function(expected, found, offset, line, column) { + function buildMessage(expected, found) { + var expectedHumanized, foundHumanized; + + switch (expected.length) { + case 0: + expectedHumanized = "end of input"; + break; + case 1: + expectedHumanized = expected[0]; + break; + default: + expectedHumanized = expected.slice(0, expected.length - 1).join(", ") + + " or " + + expected[expected.length - 1]; + } + + foundHumanized = found ? quote(found) : "end of input"; + + return "Expected " + expectedHumanized + " but " + foundHumanized + " found."; + } + + this.name = "SyntaxError"; + this.expected = expected; + this.found = found; + this.message = buildMessage(expected, found); + this.offset = offset; this.line = line; this.column = column; }; @@ -4134,31 +3184,32 @@ PEG.parser = (function(){ return result; })(); PEG.compiler = { + /* + * Names of passes that will get run during the compilation (in the specified + * order). + */ + appliedPassNames: [ + "reportMissingRules", + "reportLeftRecursion", + "removeProxyRules", + "computeVarNames", + "computeParams" + ], + /* * Generates a parser from a specified grammar AST. Throws |PEG.GrammarError| * if the AST contains a semantic error. Note that not all errors are detected * during the generation and some may protrude to the generated parser and * cause its malfunction. */ - compile: function(ast) { - var CHECK_NAMES = [ - "missingReferencedRules", - "leftRecursion" - ]; + compile: function(ast, options) { + var that = this; - var PASS_NAMES = [ - "proxyRules" - ]; + each(this.appliedPassNames, function(passName) { + that.passes[passName](ast); + }); - for (var i = 0; i < CHECK_NAMES.length; i++) { - this.checks[CHECK_NAMES[i]](ast); - } - - for (var i = 0; i < PASS_NAMES.length; i++) { - ast = this.passes[PASS_NAMES[i]](ast); - } - - var source = this.emitter(ast); + var source = this.emitter(ast, options); var result = eval(source); result._source = source; @@ -4167,16 +3218,15 @@ PEG.compiler = { }; /* - * Checks made on the grammar AST before compilation. Each check is a function - * that is passed the AST and does not return anything. If the check passes, the - * function does not do anything special, otherwise it throws - * |PEG.GrammarError|. The order in which the checks are run is specified in - * |PEG.compiler.compile| and should be the same as the order of definitions - * here. + * Compiler passes. + * + * Each pass is a function that is passed the AST. It can perform checks on it + * or modify it as needed. If the pass encounters a semantic error, it throws + * |PEG.GrammarError|. */ -PEG.compiler.checks = { +PEG.compiler.passes = { /* Checks that all referenced rules exist. */ - missingReferencedRules: function(ast) { + reportMissingRules: function(ast) { function nop() {} function checkExpression(node) { check(node.expression); } @@ -4186,13 +3236,7 @@ PEG.compiler.checks = { } var check = buildNodeVisitor({ - grammar: - function(node) { - for (var name in node.rules) { - check(node.rules[name]); - } - }, - + grammar: checkSubnodes("rules"), rule: checkExpression, choice: checkSubnodes("alternatives"), sequence: checkSubnodes("elements"), @@ -4208,7 +3252,7 @@ PEG.compiler.checks = { rule_ref: function(node) { - if (ast.rules[node.name] === undefined) { + if (!findRuleByName(ast, node.name)) { throw new PEG.GrammarError( "Referenced rule \"" + node.name + "\" does not exist." ); @@ -4224,32 +3268,30 @@ PEG.compiler.checks = { }, /* Checks that no left recursion is present. */ - leftRecursion: function(ast) { + reportLeftRecursion: function(ast) { function nop() {} function checkExpression(node, appliedRules) { check(node.expression, appliedRules); } + function checkSubnodes(propertyName) { + return function(node, appliedRules) { + each(node[propertyName], function(subnode) { + check(subnode, appliedRules); + }); + }; + } + var check = buildNodeVisitor({ - grammar: - function(node, appliedRules) { - for (var name in node.rules) { - check(node.rules[name], appliedRules); - } - }, + grammar: checkSubnodes("rules"), rule: function(node, appliedRules) { check(node.expression, appliedRules.concat(node.name)); }, - choice: - function(node, appliedRules) { - each(node.alternatives, function(alternative) { - check(alternative, appliedRules); - }); - }, + choice: checkSubnodes("alternatives"), sequence: function(node, appliedRules) { @@ -4275,7 +3317,7 @@ PEG.compiler.checks = { "Left recursion detected for rule \"" + node.name + "\"." ); } - check(ast.rules[node.name], appliedRules); + check(findRuleByName(ast, node.name), appliedRules); }, literal: nop, @@ -4284,20 +3326,12 @@ PEG.compiler.checks = { }); check(ast, []); - } -}; -/* - * Optimalization passes made on the grammar AST before compilation. Each pass - * is a function that is passed the AST and returns a new AST. The AST can be - * modified in-place by the pass. The order in which the passes are run is - * specified in |PEG.compiler.compile| and should be the same as the order of - * definitions here. - */ -PEG.compiler.passes = { + }, + /* * Removes proxy rules -- that is, rules that only delegate to other rule. */ - proxyRules: function(ast) { + removeProxyRules: function(ast) { function isProxyRule(node) { return node.type === "rule" && node.expression.type === "rule_ref"; } @@ -4318,13 +3352,7 @@ PEG.compiler.passes = { } var replace = buildNodeVisitor({ - grammar: - function(node, from, to) { - for (var name in node.rules) { - replace(node.rules[name], from, to); - } - }, - + grammar: replaceInSubnodes("rules"), rule: replaceInExpression, choice: replaceInSubnodes("alternatives"), sequence: replaceInSubnodes("elements"), @@ -4353,789 +3381,1148 @@ PEG.compiler.passes = { replace(ast, from, to); } - for (var name in ast.rules) { - if (isProxyRule(ast.rules[name])) { - replaceRuleRefs(ast, ast.rules[name].name, ast.rules[name].expression.name); - if (name === ast.startRule) { - ast.startRule = ast.rules[name].expression.name; + var indices = []; + + each(ast.rules, function(rule, i) { + if (isProxyRule(rule)) { + replaceRuleRefs(ast, rule.name, rule.expression.name); + if (rule.name === ast.startRule) { + ast.startRule = rule.expression.name; } - delete ast.rules[name]; + indices.push(i); } + }); + + indices.reverse(); + + each(indices, function(index) { + ast.rules.splice(index, 1); + }); + }, + + /* + * Computes names of variables used for storing match results and parse + * positions in generated code. These variables are organized as two stacks. + * The following will hold after running this pass: + * + * * All nodes except "grammar" and "rule" nodes will have a |resultVar| + * property. It will contain a name of the variable that will store a + * match result of the expression represented by the node in generated + * code. + * + * * Some nodes will have a |posVar| property. It will contain a name of the + * variable that will store a parse position in generated code. + * + * * All "rule" nodes will contain |resultVars| and |posVars| properties. + * They will contain a list of values of |resultVar| and |posVar| + * properties used in rule's subnodes. (This is useful to declare + * variables in generated code.) + */ + computeVarNames: function(ast) { + function resultVar(index) { return "result" + index; } + function posVar(index) { return "pos" + index; } + + function computeLeaf(node, index) { + node.resultVar = resultVar(index.result); + + return { result: 0, pos: 0 }; } - return ast; + function computeFromExpression(delta) { + return function(node, index) { + var depth = compute( + node.expression, + { + result: index.result + delta.result, + pos: index.pos + delta.pos + } + ); + + node.resultVar = resultVar(index.result); + if (delta.pos !== 0) { + node.posVar = posVar(index.pos); + } + + return { + result: depth.result + delta.result, + pos: depth.pos + delta.pos + }; + }; + } + + var compute = buildNodeVisitor({ + grammar: + function(node, index) { + each(node.rules, function(node) { + compute(node, index); + }); + }, + + rule: + function(node, index) { + var depth = compute(node.expression, index); + + node.resultVar = resultVar(index.result); + node.resultVars = map(range(depth.result + 1), resultVar); + node.posVars = map(range(depth.pos), posVar); + }, + + choice: + function(node, index) { + var depths = map(node.alternatives, function(alternative) { + return compute(alternative, index); + }); + + node.resultVar = resultVar(index.result); + + return { + result: Math.max.apply(null, pluck(depths, "result")), + pos: Math.max.apply(null, pluck(depths, "pos")) + }; + }, + + sequence: + function(node, index) { + var depths = map(node.elements, function(element, i) { + return compute( + element, + { result: index.result + i, pos: index.pos + 1 } + ); + }); + + node.resultVar = resultVar(index.result); + node.posVar = posVar(index.pos); + + return { + result: + node.elements.length > 0 + ? Math.max.apply( + null, + map(depths, function(d, i) { return i + d.result; }) + ) + : 0, + + pos: + node.elements.length > 0 + ? 1 + Math.max.apply(null, pluck(depths, "pos")) + : 1 + }; + }, + + labeled: computeFromExpression({ result: 0, pos: 0 }), + simple_and: computeFromExpression({ result: 0, pos: 1 }), + simple_not: computeFromExpression({ result: 0, pos: 1 }), + semantic_and: computeLeaf, + semantic_not: computeLeaf, + optional: computeFromExpression({ result: 0, pos: 0 }), + zero_or_more: computeFromExpression({ result: 1, pos: 0 }), + one_or_more: computeFromExpression({ result: 1, pos: 0 }), + action: computeFromExpression({ result: 0, pos: 1 }), + rule_ref: computeLeaf, + literal: computeLeaf, + any: computeLeaf, + "class": computeLeaf + }); + + compute(ast, { result: 0, pos: 0 }); + }, + + /* + * This pass walks through the AST and tracks what labels are visible at each + * point. For "action", "semantic_and" and "semantic_or" nodes it computes + * parameter names and values for the function used in generated code. (In the + * emitter, user's code is wrapped into a function that is immediately + * executed. Its parameter names correspond to visible labels and its + * parameter values to their captured values). Implicitly, this pass defines + * scoping rules for labels. + * + * After running this pass, all "action", "semantic_and" and "semantic_or" + * nodes will have a |params| property containing an object mapping parameter + * names to the expressions that will be used as their values. + */ + computeParams: function(ast) { + var envs = []; + + function scoped(f) { + envs.push({}); + f(); + envs.pop(); + } + + function nop() {} + + function computeForScopedExpression(node) { + scoped(function() { compute(node.expression); }); + } + + function computeParams(node) { + var env = envs[envs.length - 1], params = {}, name; + + for (name in env) { + params[name] = env[name]; + } + node.params = params; + } + + var compute = buildNodeVisitor({ + grammar: + function(node) { + each(node.rules, compute); + }, + + rule: computeForScopedExpression, + + choice: + function(node) { + scoped(function() { each(node.alternatives, compute); }); + }, + + sequence: + function(node) { + var env = envs[envs.length - 1], name; + + function fixup(name) { + each(pluck(node.elements, "resultVar"), function(resultVar, i) { + if ((new RegExp("^" + resultVar + "(\\[\\d+\\])*$")).test(env[name])) { + env[name] = node.resultVar + "[" + i + "]" + + env[name].substr(resultVar.length); + } + }); + } + + each(node.elements, compute); + + for (name in env) { + fixup(name); + } + }, + + labeled: + function(node) { + envs[envs.length - 1][node.label] = node.resultVar; + + scoped(function() { compute(node.expression); }); + }, + + simple_and: computeForScopedExpression, + simple_not: computeForScopedExpression, + semantic_and: computeParams, + semantic_not: computeParams, + optional: computeForScopedExpression, + zero_or_more: computeForScopedExpression, + one_or_more: computeForScopedExpression, + + action: + function(node) { + scoped(function() { + compute(node.expression); + computeParams(node); + }); + }, + + rule_ref: nop, + literal: nop, + any: nop, + "class": nop + }); + + compute(ast); } }; /* Emits the generated code for the AST. */ -PEG.compiler.emitter = function(ast) { +PEG.compiler.emitter = function(ast, options) { + options = options || {}; + if (options.cache === undefined) { + options.cache = false; + } + if (options.trackLineAndColumn === undefined) { + options.trackLineAndColumn = false; + } + /* - * Takes parts of code, interpolates variables inside them and joins them with - * a newline. + * Codie 1.1.0 * - * Variables are delimited with "${" and "}" and their names must be valid - * identifiers (i.e. they must match [a-zA-Z_][a-zA-Z0-9_]*). Variable values - * are specified as properties of the last parameter (if this is an object, - * otherwise empty variable set is assumed). Undefined variables result in - * throwing |Error|. + * https://github.com/dmajda/codie * - * There can be a filter specified after the variable name, prefixed with "|". - * The filter name must be a valid identifier. The only recognized filter - * right now is "string", which quotes the variable value as a JavaScript - * string. Unrecognized filters result in throwing |Error|. - * - * If any part has multiple lines and the first line is indented by some - * amount of whitespace (as defined by the /\s+/ JavaScript regular - * expression), second to last lines are indented by the same amount of - * whitespace. This results in nicely indented multiline code in variables - * without making the templates look ugly. - * - * Examples: - * - * formatCode("foo", "bar"); // "foo\nbar" - * formatCode("foo", "${bar}", { bar: "baz" }); // "foo\nbaz" - * formatCode("foo", "${bar}"); // throws Error - * formatCode("foo", "${bar|string}", { bar: "baz" }); // "foo\n\"baz\"" - * formatCode("foo", "${bar|eeek}", { bar: "baz" }); // throws Error - * formatCode("foo", "${bar}", { bar: " baz\nqux" }); // "foo\n baz\n qux" + * Copyright (c) 2011-2012 David Majda + * Licensend under the MIT license. */ - function formatCode() { - function interpolateVariablesInParts(parts) { - return map(parts, function(part) { - return part.replace( - /\$\{([a-zA-Z_][a-zA-Z0-9_]*)(\|([a-zA-Z_][a-zA-Z0-9_]*))?\}/g, - function(match, name, dummy, filter) { - var value = vars[name]; - if (value === undefined) { - throw new Error("Undefined variable: \"" + name + "\"."); - } + var Codie = (function(undefined) { - if (filter !== undefined && filter != "") { // JavaScript engines differ here. - if (filter === "string") { - return quote(value); - } else { - throw new Error("Unrecognized filter: \"" + filter + "\"."); - } - } else { - return value; - } + function stringEscape(s) { + function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } + + /* + * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a + * string literal except for the closing quote character, backslash, + * carriage return, line separator, paragraph separator, and line feed. + * Any character may appear in the form of an escape sequence. + * + * For portability, we also escape escape all control and non-ASCII + * characters. Note that "\0" and "\v" escape sequences are not used + * because JSHint does not like the first and IE the second. + */ + return s + .replace(/\\/g, '\\\\') // backslash + .replace(/"/g, '\\"') // closing double quote + .replace(/\x08/g, '\\b') // backspace + .replace(/\t/g, '\\t') // horizontal tab + .replace(/\n/g, '\\n') // line feed + .replace(/\f/g, '\\f') // form feed + .replace(/\r/g, '\\r') // carriage return + .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) + .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) + .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) + .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); + } + + function push(s) { return '__p.push(' + s + ');'; } + + function pushRaw(template, length, state) { + function unindent(code, level, unindentFirst) { + return code.replace( + new RegExp('^.{' + level +'}', "gm"), + function(str, offset) { + if (offset === 0) { + return unindentFirst ? '' : str; + } else { + return ""; } - ); - }); - } - - function indentMultilineParts(parts) { - return map(parts, function(part) { - if (!/\n/.test(part)) { return part; } - - var firstLineWhitespacePrefix = part.match(/^\s*/)[0]; - var lines = part.split("\n"); - var linesIndented = [lines[0]].concat( - map(lines.slice(1), function(line) { - return firstLineWhitespacePrefix + line; - }) - ); - return linesIndented.join("\n"); - }); - } - - var args = Array.prototype.slice.call(arguments); - var vars = args[args.length - 1] instanceof Object ? args.pop() : {}; - - return indentMultilineParts(interpolateVariablesInParts(args)).join("\n"); - }; - - /* Unique ID generator. */ - var UID = { - _counters: {}, - - next: function(prefix) { - this._counters[prefix] = this._counters[prefix] || 0; - return prefix + this._counters[prefix]++; - }, - - reset: function() { - this._counters = {}; - } - }; - - var emit = buildNodeVisitor({ - grammar: function(node) { - var initializerCode = node.initializer !== null - ? emit(node.initializer) - : ""; - - var parseFunctionTableItems = []; - for (var name in node.rules) { - parseFunctionTableItems.push(quote(name) + ": parse_" + name); - } - parseFunctionTableItems.sort(); - - var parseFunctionDefinitions = []; - for (var name in node.rules) { - parseFunctionDefinitions.push(emit(node.rules[name])); - } - - return formatCode( - "(function(){", - " /* Generated by PEG.js 0.6.2 (http://pegjs.majda.cz/). */", - " ", - " var result = {", - " /*", - " * Parses the input with a generated parser. If the parsing is successfull,", - " * returns a value explicitly or implicitly specified by the grammar from", - " * which the parser was generated (see |PEG.buildParser|). If the parsing is", - " * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.", - " */", - " parse: function(input, startRule) {", - " var parseFunctions = {", - " ${parseFunctionTableItems}", - " };", - " ", - " if (startRule !== undefined) {", - " if (parseFunctions[startRule] === undefined) {", - " throw new Error(\"Invalid rule name: \" + quote(startRule) + \".\");", - " }", - " } else {", - " startRule = ${startRule|string};", - " }", - " ", - " var pos = 0;", - " var reportMatchFailures = true;", - " var rightmostMatchFailuresPos = 0;", - " var rightmostMatchFailuresExpected = [];", - " var cache = {};", - " ", - /* This needs to be in sync with |padLeft| in utils.js. */ - " function padLeft(input, padding, length) {", - " var result = input;", - " ", - " var padLength = length - input.length;", - " for (var i = 0; i < padLength; i++) {", - " result = padding + result;", - " }", - " ", - " return result;", - " }", - " ", - /* This needs to be in sync with |escape| in utils.js. */ - " function escape(ch) {", - " var charCode = ch.charCodeAt(0);", - " ", - " if (charCode <= 0xFF) {", - " var escapeChar = 'x';", - " var length = 2;", - " } else {", - " var escapeChar = 'u';", - " var length = 4;", - " }", - " ", - " return '\\\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);", - " }", - " ", - /* This needs to be in sync with |quote| in utils.js. */ - " function quote(s) {", - " /*", - " * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a", - " * string literal except for the closing quote character, backslash,", - " * carriage return, line separator, paragraph separator, and line feed.", - " * Any character may appear in the form of an escape sequence.", - " */", - " return '\"' + s", - " .replace(/\\\\/g, '\\\\\\\\') // backslash", - " .replace(/\"/g, '\\\\\"') // closing quote character", - " .replace(/\\r/g, '\\\\r') // carriage return", - " .replace(/\\n/g, '\\\\n') // line feed", - " .replace(/[\\x80-\\uFFFF]/g, escape) // non-ASCII characters", - " + '\"';", - " }", - " ", - " function matchFailed(failure) {", - " if (pos < rightmostMatchFailuresPos) {", - " return;", - " }", - " ", - " if (pos > rightmostMatchFailuresPos) {", - " rightmostMatchFailuresPos = pos;", - " rightmostMatchFailuresExpected = [];", - " }", - " ", - " rightmostMatchFailuresExpected.push(failure);", - " }", - " ", - " ${parseFunctionDefinitions}", - " ", - " function buildErrorMessage() {", - " function buildExpected(failuresExpected) {", - " failuresExpected.sort();", - " ", - " var lastFailure = null;", - " var failuresExpectedUnique = [];", - " for (var i = 0; i < failuresExpected.length; i++) {", - " if (failuresExpected[i] !== lastFailure) {", - " failuresExpectedUnique.push(failuresExpected[i]);", - " lastFailure = failuresExpected[i];", - " }", - " }", - " ", - " switch (failuresExpectedUnique.length) {", - " case 0:", - " return 'end of input';", - " case 1:", - " return failuresExpectedUnique[0];", - " default:", - " return failuresExpectedUnique.slice(0, failuresExpectedUnique.length - 1).join(', ')", - " + ' or '", - " + failuresExpectedUnique[failuresExpectedUnique.length - 1];", - " }", - " }", - " ", - " var expected = buildExpected(rightmostMatchFailuresExpected);", - " var actualPos = Math.max(pos, rightmostMatchFailuresPos);", - " var actual = actualPos < input.length", - " ? quote(input.charAt(actualPos))", - " : 'end of input';", - " ", - " return 'Expected ' + expected + ' but ' + actual + ' found.';", - " }", - " ", - " function computeErrorPosition() {", - " /*", - " * The first idea was to use |String.split| to break the input up to the", - " * error position along newlines and derive the line and column from", - " * there. However IE's |split| implementation is so broken that it was", - " * enough to prevent it.", - " */", - " ", - " var line = 1;", - " var column = 1;", - " var seenCR = false;", - " ", - " for (var i = 0; i < rightmostMatchFailuresPos; i++) {", - " var ch = input.charAt(i);", - " if (ch === '\\n') {", - " if (!seenCR) { line++; }", - " column = 1;", - " seenCR = false;", - " } else if (ch === '\\r' | ch === '\\u2028' || ch === '\\u2029') {", - " line++;", - " column = 1;", - " seenCR = true;", - " } else {", - " column++;", - " seenCR = false;", - " }", - " }", - " ", - " return { line: line, column: column };", - " }", - " ", - " ${initializerCode}", - " ", - " var result = parseFunctions[startRule]();", - " ", - " /*", - " * The parser is now in one of the following three states:", - " *", - " * 1. The parser successfully parsed the whole input.", - " *", - " * - |result !== null|", - " * - |pos === input.length|", - " * - |rightmostMatchFailuresExpected| may or may not contain something", - " *", - " * 2. The parser successfully parsed only a part of the input.", - " *", - " * - |result !== null|", - " * - |pos < input.length|", - " * - |rightmostMatchFailuresExpected| may or may not contain something", - " *", - " * 3. The parser did not successfully parse any part of the input.", - " *", - " * - |result === null|", - " * - |pos === 0|", - " * - |rightmostMatchFailuresExpected| contains at least one failure", - " *", - " * All code following this comment (including called functions) must", - " * handle these states.", - " */", - " if (result === null || pos !== input.length) {", - " var errorPosition = computeErrorPosition();", - " throw new this.SyntaxError(", - " buildErrorMessage(),", - " errorPosition.line,", - " errorPosition.column", - " );", - " }", - " ", - " return result;", - " },", - " ", - " /* Returns the parser source code. */", - " toSource: function() { return this._source; }", - " };", - " ", - " /* Thrown when a parser encounters a syntax error. */", - " ", - " result.SyntaxError = function(message, line, column) {", - " this.name = 'SyntaxError';", - " this.message = message;", - " this.line = line;", - " this.column = column;", - " };", - " ", - " result.SyntaxError.prototype = Error.prototype;", - " ", - " return result;", - "})()", - { - initializerCode: initializerCode, - parseFunctionTableItems: parseFunctionTableItems.join(",\n"), - parseFunctionDefinitions: parseFunctionDefinitions.join("\n\n"), - startRule: node.startRule } ); - }, + } - initializer: function(node) { - return node.code; - }, + var escaped = stringEscape(unindent( + template.substring(0, length), + state.indentLevel(), + state.atBOL + )); - rule: function(node) { - /* - * We want to reset variable names at the beginning of every function so - * that a little change in the source grammar does not change variables in - * all the generated code. This is desired especially when one has the - * generated grammar stored in a VCS (this is true e.g. for our - * metagrammar). - */ - UID.reset(); + return escaped.length > 0 ? push('"' + escaped + '"') : ''; + } - var resultVar = UID.next("result"); - if (node.displayName !== null) { - var setReportMatchFailuresCode = formatCode( - "var savedReportMatchFailures = reportMatchFailures;", - "reportMatchFailures = false;" - ); - var restoreReportMatchFailuresCode = formatCode( - "reportMatchFailures = savedReportMatchFailures;" - ); - var reportMatchFailureCode = formatCode( - "if (reportMatchFailures && ${resultVar} === null) {", - " matchFailed(${displayName|string});", - "}", - { - displayName: node.displayName, - resultVar: resultVar + var Codie = { + /* Codie version (uses semantic versioning). */ + VERSION: "1.1.0", + + /* + * Specifies by how many characters do #if/#else and #for unindent their + * content in the generated code. + */ + indentStep: 2, + + /* Description of #-commands. Extend to define your own commands. */ + commands: { + "if": { + params: /^(.*)$/, + compile: function(state, prefix, params) { + return ['if(' + params[0] + '){', []]; + }, + stackOp: "push" + }, + "else": { + params: /^$/, + compile: function(state) { + var stack = state.commandStack, + insideElse = stack[stack.length - 1] === "else", + insideIf = stack[stack.length - 1] === "if"; + + if (insideElse) { throw new Error("Multiple #elses."); } + if (!insideIf) { throw new Error("Using #else outside of #if."); } + + return ['}else{', []]; + }, + stackOp: "replace" + }, + "for": { + params: /^([a-zA-Z_][a-zA-Z0-9_]*)[ \t]+in[ \t]+(.*)$/, + init: function(state) { + state.forCurrLevel = 0; // current level of #for loop nesting + state.forMaxLevel = 0; // maximum level of #for loop nesting + }, + compile: function(state, prefix, params) { + var c = '__c' + state.forCurrLevel, // __c for "collection" + l = '__l' + state.forCurrLevel, // __l for "length" + i = '__i' + state.forCurrLevel; // __i for "index" + + state.forCurrLevel++; + if (state.forMaxLevel < state.forCurrLevel) { + state.forMaxLevel = state.forCurrLevel; } - ); - } else { - var setReportMatchFailuresCode = ""; - var restoreReportMatchFailuresCode = ""; - var reportMatchFailureCode = ""; - } - return formatCode( - "function parse_${name}() {", - " var cacheKey = '${name}@' + pos;", - " var cachedResult = cache[cacheKey];", - " if (cachedResult) {", - " pos = cachedResult.nextPos;", - " return cachedResult.result;", - " }", - " ", - " ${setReportMatchFailuresCode}", - " ${code}", - " ${restoreReportMatchFailuresCode}", - " ${reportMatchFailureCode}", - " ", - " cache[cacheKey] = {", - " nextPos: pos,", - " result: ${resultVar}", - " };", - " return ${resultVar};", - "}", - { - name: node.name, - setReportMatchFailuresCode: setReportMatchFailuresCode, - restoreReportMatchFailuresCode: restoreReportMatchFailuresCode, - reportMatchFailureCode: reportMatchFailureCode, - code: emit(node.expression, resultVar), - resultVar: resultVar - } - ); + return [ + c + '=' + params[1] + ';' + + l + '=' + c + '.length;' + + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' + + params[0] + '=' + c + '[' + i + '];', + [params[0], c, l, i] + ]; + }, + exit: function(state) { state.forCurrLevel--; }, + stackOp: "push" + }, + "end": { + params: /^$/, + compile: function(state) { + var stack = state.commandStack, exit; + + if (stack.length === 0) { throw new Error("Too many #ends."); } + + exit = Codie.commands[stack[stack.length - 1]].exit; + if (exit) { exit(state); } + + return ['}', []]; + }, + stackOp: "pop" + }, + "block": { + params: /^(.*)$/, + compile: function(state, prefix, params) { + var x = '__x', // __x for "prefix", + n = '__n', // __n for "lines" + l = '__l', // __l for "length" + i = '__i'; // __i for "index" + + /* + * Originally, the generated code used |String.prototype.replace|, but + * it is buggy in certain versions of V8 so it was rewritten. See the + * tests for details. + */ + return [ + x + '="' + stringEscape(prefix.substring(state.indentLevel())) + '";' + + n + '=(' + params[0] + ').toString().split("\\n");' + + l + '=' + n + '.length;' + + 'for(' + i + '=0;' + i + '<' + l + ';' + i + '++){' + + n + '[' + i +']=' + x + '+' + n + '[' + i + ']+"\\n";' + + '}' + + push(n + '.join("")'), + [x, n, l, i] + ]; + }, + stackOp: "nop" + } }, + /* + * Compiles a template into a function. When called, this function will + * execute the template in the context of an object passed in a parameter and + * return the result. + */ + template: function(template) { + var stackOps = { + push: function(stack, name) { stack.push(name); }, + replace: function(stack, name) { stack[stack.length - 1] = name; }, + pop: function(stack) { stack.pop(); }, + nop: function() { } + }; + + function compileExpr(state, expr) { + state.atBOL = false; + return [push(expr), []]; + } + + function compileCommand(state, prefix, name, params) { + var command, match, result; + + command = Codie.commands[name]; + if (!command) { throw new Error("Unknown command: #" + name + "."); } + + match = command.params.exec(params); + if (match === null) { + throw new Error( + "Invalid params for command #" + name + ": " + params + "." + ); + } + + result = command.compile(state, prefix, match.slice(1)); + stackOps[command.stackOp](state.commandStack, name); + state.atBOL = true; + return result; + } + + var state = { // compilation state + commandStack: [], // stack of commands as they were nested + atBOL: true, // is the next character to process at BOL? + indentLevel: function() { + return Codie.indentStep * this.commandStack.length; + } + }, + code = '', // generated template function code + vars = ['__p=[]'], // variables used by generated code + name, match, result, i; + + /* Initialize state. */ + for (name in Codie.commands) { + if (Codie.commands[name].init) { Codie.commands[name].init(state); } + } + + /* Compile the template. */ + while ((match = /^([ \t]*)#([a-zA-Z_][a-zA-Z0-9_]*)(?:[ \t]+([^ \t\n][^\n]*))?[ \t]*(?:\n|$)|#\{([^}]*)\}/m.exec(template)) !== null) { + code += pushRaw(template, match.index, state); + result = match[2] !== undefined && match[2] !== "" + ? compileCommand(state, match[1], match[2], match[3] || "") // #-command + : compileExpr(state, match[4]); // #{...} + code += result[0]; + vars = vars.concat(result[1]); + template = template.substring(match.index + match[0].length); + } + code += pushRaw(template, template.length, state); + + /* Check the final state. */ + if (state.commandStack.length > 0) { throw new Error("Missing #end."); } + + /* Sanitize the list of variables used by commands. */ + vars.sort(); + for (i = 0; i < vars.length; i++) { + if (vars[i] === vars[i - 1]) { vars.splice(i--, 1); } + } + + /* Create the resulting function. */ + return new Function("__v", [ + '__v=__v||{};', + 'var ' + vars.join(',') + ';', + 'with(__v){', + code, + 'return __p.join("").replace(/^\\n+|\\n+$/g,"");};' + ].join('')); + } + }; + + return Codie; + + })(); + + var templates = (function() { + var name, + templates = {}, + sources = { + grammar: [ + '(function(){', + ' /*', + ' * Generated by PEG.js 0.7.0.', + ' *', + ' * http://pegjs.majda.cz/', + ' */', + ' ', + /* This needs to be in sync with |quote| in utils.js. */ + ' function quote(s) {', + ' /*', + ' * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a', + ' * string literal except for the closing quote character, backslash,', + ' * carriage return, line separator, paragraph separator, and line feed.', + ' * Any character may appear in the form of an escape sequence.', + ' *', + ' * For portability, we also escape escape all control and non-ASCII', + ' * characters. Note that "\\0" and "\\v" escape sequences are not used', + ' * because JSHint does not like the first and IE the second.', + ' */', + ' return \'"\' + s', + ' .replace(/\\\\/g, \'\\\\\\\\\') // backslash', + ' .replace(/"/g, \'\\\\"\') // closing quote character', + ' .replace(/\\x08/g, \'\\\\b\') // backspace', + ' .replace(/\\t/g, \'\\\\t\') // horizontal tab', + ' .replace(/\\n/g, \'\\\\n\') // line feed', + ' .replace(/\\f/g, \'\\\\f\') // form feed', + ' .replace(/\\r/g, \'\\\\r\') // carriage return', + ' .replace(/[\\x00-\\x07\\x0B\\x0E-\\x1F\\x80-\\uFFFF]/g, escape)', + ' + \'"\';', + ' }', + ' ', + ' var result = {', + ' /*', + ' * Parses the input with a generated parser. If the parsing is successfull,', + ' * returns a value explicitly or implicitly specified by the grammar from', + ' * which the parser was generated (see |PEG.buildParser|). If the parsing is', + ' * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.', + ' */', + ' parse: function(input, startRule) {', + ' var parseFunctions = {', + ' #for rule in node.rules', + ' #{string(rule.name) + ": parse_" + rule.name + (rule !== node.rules[node.rules.length - 1] ? "," : "")}', + ' #end', + ' };', + ' ', + ' if (startRule !== undefined) {', + ' if (parseFunctions[startRule] === undefined) {', + ' throw new Error("Invalid rule name: " + quote(startRule) + ".");', + ' }', + ' } else {', + ' startRule = #{string(node.startRule)};', + ' }', + ' ', + ' #{posInit("pos")};', + ' var reportFailures = 0;', // 0 = report, anything > 0 = do not report + ' #{posInit("rightmostFailuresPos")};', + ' var rightmostFailuresExpected = [];', + ' #if options.cache', + ' var cache = {};', + ' #end', + ' ', + /* This needs to be in sync with |padLeft| in utils.js. */ + ' function padLeft(input, padding, length) {', + ' var result = input;', + ' ', + ' var padLength = length - input.length;', + ' for (var i = 0; i < padLength; i++) {', + ' result = padding + result;', + ' }', + ' ', + ' return result;', + ' }', + ' ', + /* This needs to be in sync with |escape| in utils.js. */ + ' function escape(ch) {', + ' var charCode = ch.charCodeAt(0);', + ' var escapeChar;', + ' var length;', + ' ', + ' if (charCode <= 0xFF) {', + ' escapeChar = \'x\';', + ' length = 2;', + ' } else {', + ' escapeChar = \'u\';', + ' length = 4;', + ' }', + ' ', + ' return \'\\\\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), \'0\', length);', + ' }', + ' ', + ' #if options.trackLineAndColumn', + ' function clone(object) {', + ' var result = {};', + ' for (var key in object) {', + ' result[key] = object[key];', + ' }', + ' return result;', + ' }', + ' ', + ' function advance(pos, n) {', + ' var endOffset = pos.offset + n;', + ' ', + ' for (var offset = pos.offset; offset < endOffset; offset++) {', + ' var ch = input.charAt(offset);', + ' if (ch === "\\n") {', + ' if (!pos.seenCR) { pos.line++; }', + ' pos.column = 1;', + ' pos.seenCR = false;', + ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', + ' pos.line++;', + ' pos.column = 1;', + ' pos.seenCR = true;', + ' } else {', + ' pos.column++;', + ' pos.seenCR = false;', + ' }', + ' }', + ' ', + ' pos.offset += n;', + ' }', + ' ', + ' #end', + ' function matchFailed(failure) {', + ' if (#{posOffset("pos")} < #{posOffset("rightmostFailuresPos")}) {', + ' return;', + ' }', + ' ', + ' if (#{posOffset("pos")} > #{posOffset("rightmostFailuresPos")}) {', + ' rightmostFailuresPos = #{posClone("pos")};', + ' rightmostFailuresExpected = [];', + ' }', + ' ', + ' rightmostFailuresExpected.push(failure);', + ' }', + ' ', + ' #for rule in node.rules', + ' #block emit(rule)', + ' ', + ' #end', + ' ', + ' function cleanupExpected(expected) {', + ' expected.sort();', + ' ', + ' var lastExpected = null;', + ' var cleanExpected = [];', + ' for (var i = 0; i < expected.length; i++) {', + ' if (expected[i] !== lastExpected) {', + ' cleanExpected.push(expected[i]);', + ' lastExpected = expected[i];', + ' }', + ' }', + ' return cleanExpected;', + ' }', + ' ', + ' #if !options.trackLineAndColumn', + ' function computeErrorPosition() {', + ' /*', + ' * The first idea was to use |String.split| to break the input up to the', + ' * error position along newlines and derive the line and column from', + ' * there. However IE\'s |split| implementation is so broken that it was', + ' * enough to prevent it.', + ' */', + ' ', + ' var line = 1;', + ' var column = 1;', + ' var seenCR = false;', + ' ', + ' for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) {', + ' var ch = input.charAt(i);', + ' if (ch === "\\n") {', + ' if (!seenCR) { line++; }', + ' column = 1;', + ' seenCR = false;', + ' } else if (ch === "\\r" || ch === "\\u2028" || ch === "\\u2029") {', + ' line++;', + ' column = 1;', + ' seenCR = true;', + ' } else {', + ' column++;', + ' seenCR = false;', + ' }', + ' }', + ' ', + ' return { line: line, column: column };', + ' }', + ' #end', + ' ', + ' #if node.initializer', + ' #block emit(node.initializer)', + ' #end', + ' ', + ' var result = parseFunctions[startRule]();', + ' ', + ' /*', + ' * The parser is now in one of the following three states:', + ' *', + ' * 1. The parser successfully parsed the whole input.', + ' *', + ' * - |result !== null|', + ' * - |#{posOffset("pos")} === input.length|', + ' * - |rightmostFailuresExpected| may or may not contain something', + ' *', + ' * 2. The parser successfully parsed only a part of the input.', + ' *', + ' * - |result !== null|', + ' * - |#{posOffset("pos")} < input.length|', + ' * - |rightmostFailuresExpected| may or may not contain something', + ' *', + ' * 3. The parser did not successfully parse any part of the input.', + ' *', + ' * - |result === null|', + ' * - |#{posOffset("pos")} === 0|', + ' * - |rightmostFailuresExpected| contains at least one failure', + ' *', + ' * All code following this comment (including called functions) must', + ' * handle these states.', + ' */', + ' if (result === null || #{posOffset("pos")} !== input.length) {', + ' var offset = Math.max(#{posOffset("pos")}, #{posOffset("rightmostFailuresPos")});', + ' var found = offset < input.length ? input.charAt(offset) : null;', + ' #if options.trackLineAndColumn', + ' var errorPosition = #{posOffset("pos")} > #{posOffset("rightmostFailuresPos")} ? pos : rightmostFailuresPos;', + ' #else', + ' var errorPosition = computeErrorPosition();', + ' #end', + ' ', + ' throw new this.SyntaxError(', + ' cleanupExpected(rightmostFailuresExpected),', + ' found,', + ' offset,', + ' errorPosition.line,', + ' errorPosition.column', + ' );', + ' }', + ' ', + ' return result;', + ' },', + ' ', + ' /* Returns the parser source code. */', + ' toSource: function() { return this._source; }', + ' };', + ' ', + ' /* Thrown when a parser encounters a syntax error. */', + ' ', + ' result.SyntaxError = function(expected, found, offset, line, column) {', + ' function buildMessage(expected, found) {', + ' var expectedHumanized, foundHumanized;', + ' ', + ' switch (expected.length) {', + ' case 0:', + ' expectedHumanized = "end of input";', + ' break;', + ' case 1:', + ' expectedHumanized = expected[0];', + ' break;', + ' default:', + ' expectedHumanized = expected.slice(0, expected.length - 1).join(", ")', + ' + " or "', + ' + expected[expected.length - 1];', + ' }', + ' ', + ' foundHumanized = found ? quote(found) : "end of input";', + ' ', + ' return "Expected " + expectedHumanized + " but " + foundHumanized + " found.";', + ' }', + ' ', + ' this.name = "SyntaxError";', + ' this.expected = expected;', + ' this.found = found;', + ' this.message = buildMessage(expected, found);', + ' this.offset = offset;', + ' this.line = line;', + ' this.column = column;', + ' };', + ' ', + ' result.SyntaxError.prototype = Error.prototype;', + ' ', + ' return result;', + '})()' + ], + rule: [ + 'function parse_#{node.name}() {', + ' #if options.cache', + ' var cacheKey = "#{node.name}@" + #{posOffset("pos")};', + ' var cachedResult = cache[cacheKey];', + ' if (cachedResult) {', + ' pos = #{posClone("cachedResult.nextPos")};', + ' return cachedResult.result;', + ' }', + ' ', + ' #end', + ' #if node.resultVars.length > 0', + ' var #{node.resultVars.join(", ")};', + ' #end', + ' #if node.posVars.length > 0', + ' var #{node.posVars.join(", ")};', + ' #end', + ' ', + ' #if node.displayName !== null', + ' reportFailures++;', + ' #end', + ' #block emit(node.expression)', + ' #if node.displayName !== null', + ' reportFailures--;', + ' if (reportFailures === 0 && #{node.resultVar} === null) {', + ' matchFailed(#{string(node.displayName)});', + ' }', + ' #end', + ' #if options.cache', + ' ', + ' cache[cacheKey] = {', + ' nextPos: #{posClone("pos")},', + ' result: #{node.resultVar}', + ' };', + ' #end', + ' return #{node.resultVar};', + '}' + ], + choice: [ + '#block emit(alternative)', + '#block nextAlternativesCode' + ], + "choice.next": [ + 'if (#{node.resultVar} === null) {', + ' #block code', + '}' + ], + sequence: [ + '#{posSave(node)};', + '#block code' + ], + "sequence.iteration": [ + '#block emit(element)', + 'if (#{element.resultVar} !== null) {', + ' #block code', + '} else {', + ' #{node.resultVar} = null;', + ' #{posRestore(node)};', + '}' + ], + "sequence.inner": [ + '#{node.resultVar} = [#{pluck(node.elements, "resultVar").join(", ")}];' + ], + simple_and: [ + '#{posSave(node)};', + 'reportFailures++;', + '#block emit(node.expression)', + 'reportFailures--;', + 'if (#{node.resultVar} !== null) {', + ' #{node.resultVar} = "";', + ' #{posRestore(node)};', + '} else {', + ' #{node.resultVar} = null;', + '}' + ], + simple_not: [ + '#{posSave(node)};', + 'reportFailures++;', + '#block emit(node.expression)', + 'reportFailures--;', + 'if (#{node.resultVar} === null) {', + ' #{node.resultVar} = "";', + '} else {', + ' #{node.resultVar} = null;', + ' #{posRestore(node)};', + '}' + ], + semantic_and: [ + '#{node.resultVar} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? ["pos.offset", "pos.line", "pos.column"] : ["pos"]).concat(values(node.params)).join(", ")}) ? "" : null;' + ], + semantic_not: [ + '#{node.resultVar} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? ["pos.offset", "pos.line", "pos.column"] : ["pos"]).concat(values(node.params)).join(", ")}) ? null : "";' + ], + optional: [ + '#block emit(node.expression)', + '#{node.resultVar} = #{node.resultVar} !== null ? #{node.resultVar} : "";' + ], + zero_or_more: [ + '#{node.resultVar} = [];', + '#block emit(node.expression)', + 'while (#{node.expression.resultVar} !== null) {', + ' #{node.resultVar}.push(#{node.expression.resultVar});', + ' #block emit(node.expression)', + '}' + ], + one_or_more: [ + '#block emit(node.expression)', + 'if (#{node.expression.resultVar} !== null) {', + ' #{node.resultVar} = [];', + ' while (#{node.expression.resultVar} !== null) {', + ' #{node.resultVar}.push(#{node.expression.resultVar});', + ' #block emit(node.expression)', + ' }', + '} else {', + ' #{node.resultVar} = null;', + '}' + ], + action: [ + '#{posSave(node)};', + '#block emit(node.expression)', + 'if (#{node.resultVar} !== null) {', + ' #{node.resultVar} = (function(#{(options.trackLineAndColumn ? ["offset", "line", "column"] : ["offset"]).concat(keys(node.params)).join(", ")}) {#{node.code}})(#{(options.trackLineAndColumn ? [node.posVar + ".offset", node.posVar + ".line", node.posVar + ".column"] : [node.posVar]).concat(values(node.params)).join(", ")});', + '}', + 'if (#{node.resultVar} === null) {', + ' #{posRestore(node)};', + '}' + ], + rule_ref: [ + '#{node.resultVar} = parse_#{node.name}();' + ], + literal: [ + '#if node.value.length === 0', + ' #{node.resultVar} = "";', + '#else', + ' #if !node.ignoreCase', + ' #if node.value.length === 1', + ' if (input.charCodeAt(#{posOffset("pos")}) === #{node.value.charCodeAt(0)}) {', + ' #else', + ' if (input.substr(#{posOffset("pos")}, #{node.value.length}) === #{string(node.value)}) {', + ' #end', + ' #else', + /* + * One-char literals are not optimized when case-insensitive + * matching is enabled. This is because there is no simple way to + * lowercase a character code that works for character outside ASCII + * letters. Moreover, |toLowerCase| can change string length, + * meaning the result of lowercasing a character can be more + * characters. + */ + ' if (input.substr(#{posOffset("pos")}, #{node.value.length}).toLowerCase() === #{string(node.value.toLowerCase())}) {', + ' #end', + ' #if !node.ignoreCase', + ' #{node.resultVar} = #{string(node.value)};', + ' #else', + ' #{node.resultVar} = input.substr(#{posOffset("pos")}, #{node.value.length});', + ' #end', + ' #{posAdvance(node.value.length)};', + ' } else {', + ' #{node.resultVar} = null;', + ' if (reportFailures === 0) {', + ' matchFailed(#{string(string(node.value))});', + ' }', + ' }', + '#end' + ], + any: [ + 'if (input.length > #{posOffset("pos")}) {', + ' #{node.resultVar} = input.charAt(#{posOffset("pos")});', + ' #{posAdvance(1)};', + '} else {', + ' #{node.resultVar} = null;', + ' if (reportFailures === 0) {', + ' matchFailed("any character");', + ' }', + '}' + ], + "class": [ + 'if (#{regexp}.test(input.charAt(#{posOffset("pos")}))) {', + ' #{node.resultVar} = input.charAt(#{posOffset("pos")});', + ' #{posAdvance(1)};', + '} else {', + ' #{node.resultVar} = null;', + ' if (reportFailures === 0) {', + ' matchFailed(#{string(node.rawText)});', + ' }', + '}' + ] + }; + + for (name in sources) { + templates[name] = Codie.template(sources[name].join('\n')); + } + + return templates; + })(); + + function fill(name, vars) { + vars.string = quote; + vars.pluck = pluck; + vars.keys = keys; + vars.values = values; + vars.emit = emit; + vars.options = options; + + /* Position-handling macros */ + if (options.trackLineAndColumn) { + vars.posInit = function(name) { + return "var " + + name + + " = " + + "{ offset: 0, line: 1, column: 1, seenCR: false }"; + }; + vars.posClone = function(name) { return "clone(" + name + ")"; }; + vars.posOffset = function(name) { return name + ".offset"; }; + + vars.posAdvance = function(n) { return "advance(pos, " + n + ")"; }; + } else { + vars.posInit = function(name) { return "var " + name + " = 0"; }; + vars.posClone = function(name) { return name; }; + vars.posOffset = function(name) { return name; }; + + vars.posAdvance = function(n) { + return n === 1 ? "pos++" : "pos += " + n; + }; + } + vars.posSave = function(node) { + return node.posVar + " = " + vars.posClone("pos"); + }; + vars.posRestore = function(node) { + return "pos" + " = " + vars.posClone(node.posVar); + }; + + return templates[name](vars); + } + + function emitSimple(name) { + return function(node) { return fill(name, { node: node }); }; + } + + var emit = buildNodeVisitor({ + grammar: emitSimple("grammar"), + + initializer: function(node) { return node.code; }, + + rule: emitSimple("rule"), + /* * The contract for all code fragments generated by the following functions - * is as follows: + * is as follows. * - * * The code fragment should try to match a part of the input starting with - * the position indicated in |pos|. That position may point past the end of - * the input. + * The code fragment tries to match a part of the input starting with the + * position indicated in |pos|. That position may point past the end of the + * input. * - * * If the code fragment matches the input, it advances |pos| after the - * matched part of the input and sets variable with a name stored in - * |resultVar| to appropriate value, which is always non-null. + * * If the code fragment matches the input, it advances |pos| to point to + * the first chracter following the matched part of the input and sets + * variable with a name stored in |node.resultVar| to an appropriate + * value. This value is always non-|null|. * - * * If the code fragment does not match the input, it does not change |pos| - * and it sets a variable with a name stored in |resultVar| to |null|. + * * If the code fragment does not match the input, it returns with |pos| + * set to the original value and it sets a variable with a name stored in + * |node.resultVar| to |null|. + * + * The code can use variables with names stored in |resultVar| and |posVar| + * properties of the current node's subnodes. It can't use any other + * variables. */ - choice: function(node, resultVar) { - var code = formatCode( - "var ${resultVar} = null;", - { resultVar: resultVar } - ); + choice: function(node) { + var code, nextAlternativesCode; for (var i = node.alternatives.length - 1; i >= 0; i--) { - var alternativeResultVar = UID.next("result"); - code = formatCode( - "${alternativeCode}", - "if (${alternativeResultVar} !== null) {", - " var ${resultVar} = ${alternativeResultVar};", - "} else {", - " ${code};", - "}", - { - alternativeCode: emit(node.alternatives[i], alternativeResultVar), - alternativeResultVar: alternativeResultVar, - code: code, - resultVar: resultVar - } - ); + nextAlternativesCode = i !== node.alternatives.length - 1 + ? fill("choice.next", { node: node, code: code }) + : ''; + code = fill("choice", { + alternative: node.alternatives[i], + nextAlternativesCode: nextAlternativesCode + }); } return code; }, - sequence: function(node, resultVar) { - var savedPosVar = UID.next("savedPos"); - - var elementResultVars = map(node.elements, function() { - return UID.next("result") - }); - - var code = formatCode( - "var ${resultVar} = ${elementResultVarArray};", - { - resultVar: resultVar, - elementResultVarArray: "[" + elementResultVars.join(", ") + "]" - } - ); + sequence: function(node) { + var code = fill("sequence.inner", { node: node }); for (var i = node.elements.length - 1; i >= 0; i--) { - code = formatCode( - "${elementCode}", - "if (${elementResultVar} !== null) {", - " ${code}", - "} else {", - " var ${resultVar} = null;", - " pos = ${savedPosVar};", - "}", - { - elementCode: emit(node.elements[i], elementResultVars[i]), - elementResultVar: elementResultVars[i], - code: code, - savedPosVar: savedPosVar, - resultVar: resultVar - } - ); + code = fill("sequence.iteration", { + node: node, + element: node.elements[i], + code: code + }); } - return formatCode( - "var ${savedPosVar} = pos;", - "${code}", - { - code: code, - savedPosVar: savedPosVar - } - ); + return fill("sequence", { node: node, code: code }); }, - labeled: function(node, resultVar) { - return emit(node.expression, resultVar); - }, + labeled: function(node) { return emit(node.expression); }, - simple_and: function(node, resultVar) { - var savedPosVar = UID.next("savedPos"); - var savedReportMatchFailuresVar = UID.next("savedReportMatchFailuresVar"); - var expressionResultVar = UID.next("result"); + simple_and: emitSimple("simple_and"), + simple_not: emitSimple("simple_not"), + semantic_and: emitSimple("semantic_and"), + semantic_not: emitSimple("semantic_not"), + optional: emitSimple("optional"), + zero_or_more: emitSimple("zero_or_more"), + one_or_more: emitSimple("one_or_more"), + action: emitSimple("action"), + rule_ref: emitSimple("rule_ref"), + literal: emitSimple("literal"), + any: emitSimple("any"), - return formatCode( - "var ${savedPosVar} = pos;", - "var ${savedReportMatchFailuresVar} = reportMatchFailures;", - "reportMatchFailures = false;", - "${expressionCode}", - "reportMatchFailures = ${savedReportMatchFailuresVar};", - "if (${expressionResultVar} !== null) {", - " var ${resultVar} = '';", - " pos = ${savedPosVar};", - "} else {", - " var ${resultVar} = null;", - "}", - { - expressionCode: emit(node.expression, expressionResultVar), - expressionResultVar: expressionResultVar, - savedPosVar: savedPosVar, - savedReportMatchFailuresVar: savedReportMatchFailuresVar, - resultVar: resultVar - } - ); - }, + "class": function(node) { + var regexp; - simple_not: function(node, resultVar) { - var savedPosVar = UID.next("savedPos"); - var savedReportMatchFailuresVar = UID.next("savedReportMatchFailuresVar"); - var expressionResultVar = UID.next("result"); - - return formatCode( - "var ${savedPosVar} = pos;", - "var ${savedReportMatchFailuresVar} = reportMatchFailures;", - "reportMatchFailures = false;", - "${expressionCode}", - "reportMatchFailures = ${savedReportMatchFailuresVar};", - "if (${expressionResultVar} === null) {", - " var ${resultVar} = '';", - "} else {", - " var ${resultVar} = null;", - " pos = ${savedPosVar};", - "}", - { - expressionCode: emit(node.expression, expressionResultVar), - expressionResultVar: expressionResultVar, - savedPosVar: savedPosVar, - savedReportMatchFailuresVar: savedReportMatchFailuresVar, - resultVar: resultVar - } - ); - }, - - semantic_and: function(node, resultVar) { - return formatCode( - "var ${resultVar} = (function() {${actionCode}})() ? '' : null;", - { - actionCode: node.code, - resultVar: resultVar - } - ); - }, - - semantic_not: function(node, resultVar) { - return formatCode( - "var ${resultVar} = (function() {${actionCode}})() ? null : '';", - { - actionCode: node.code, - resultVar: resultVar - } - ); - }, - - optional: function(node, resultVar) { - var expressionResultVar = UID.next("result"); - - return formatCode( - "${expressionCode}", - "var ${resultVar} = ${expressionResultVar} !== null ? ${expressionResultVar} : '';", - { - expressionCode: emit(node.expression, expressionResultVar), - expressionResultVar: expressionResultVar, - resultVar: resultVar - } - ); - }, - - zero_or_more: function(node, resultVar) { - var expressionResultVar = UID.next("result"); - - return formatCode( - "var ${resultVar} = [];", - "${expressionCode}", - "while (${expressionResultVar} !== null) {", - " ${resultVar}.push(${expressionResultVar});", - " ${expressionCode}", - "}", - { - expressionCode: emit(node.expression, expressionResultVar), - expressionResultVar: expressionResultVar, - resultVar: resultVar - } - ); - }, - - one_or_more: function(node, resultVar) { - var expressionResultVar = UID.next("result"); - - return formatCode( - "${expressionCode}", - "if (${expressionResultVar} !== null) {", - " var ${resultVar} = [];", - " while (${expressionResultVar} !== null) {", - " ${resultVar}.push(${expressionResultVar});", - " ${expressionCode}", - " }", - "} else {", - " var ${resultVar} = null;", - "}", - { - expressionCode: emit(node.expression, expressionResultVar), - expressionResultVar: expressionResultVar, - resultVar: resultVar - } - ); - }, - - action: function(node, resultVar) { - /* - * In case of sequences, we splat their elements into function arguments - * one by one. Example: - * - * start: a:"a" b:"b" c:"c" { alert(arguments.length) } // => 3 - * - * This behavior is reflected in this function. - */ - - var expressionResultVar = UID.next("result"); - var actionResultVar = UID.next("result"); - var savedPosVar = UID.next("savedPos"); - - if (node.expression.type === "sequence") { - var formalParams = []; - var actualParams = []; - - var elements = node.expression.elements; - var elementsLength = elements.length; - for (var i = 0; i < elementsLength; i++) { - if (elements[i].type === "labeled") { - formalParams.push(elements[i].label); - actualParams.push(expressionResultVar + "[" + i + "]"); - } - } - } else if (node.expression.type === "labeled") { - var formalParams = [node.expression.label]; - var actualParams = [expressionResultVar]; - } else { - var formalParams = []; - var actualParams = []; - } - - return formatCode( - "var ${savedPosVar} = pos;", - "${expressionCode}", - "var ${actionResultVar} = ${expressionResultVar} !== null", - " ? (function(${formalParams}) {${actionCode}})(${actualParams})", - " : null;", - "if (${actionResultVar} !== null) {", - " var ${resultVar} = ${actionResultVar};", - "} else {", - " var ${resultVar} = null;", - " pos = ${savedPosVar};", - "}", - { - expressionCode: emit(node.expression, expressionResultVar), - expressionResultVar: expressionResultVar, - actionCode: node.code, - actionResultVar: actionResultVar, - formalParams: formalParams.join(", "), - actualParams: actualParams.join(", "), - savedPosVar: savedPosVar, - resultVar: resultVar - } - ); - }, - - rule_ref: function(node, resultVar) { - return formatCode( - "var ${resultVar} = ${ruleMethod}();", - { - ruleMethod: "parse_" + node.name, - resultVar: resultVar - } - ); - }, - - literal: function(node, resultVar) { - return formatCode( - "if (input.substr(pos, ${length}) === ${value|string}) {", - " var ${resultVar} = ${value|string};", - " pos += ${length};", - "} else {", - " var ${resultVar} = null;", - " if (reportMatchFailures) {", - " matchFailed(${valueQuoted|string});", - " }", - "}", - { - value: node.value, - valueQuoted: quote(node.value), - length: node.value.length, - resultVar: resultVar - } - ); - }, - - any: function(node, resultVar) { - return formatCode( - "if (input.length > pos) {", - " var ${resultVar} = input.charAt(pos);", - " pos++;", - "} else {", - " var ${resultVar} = null;", - " if (reportMatchFailures) {", - " matchFailed('any character');", - " }", - "}", - { resultVar: resultVar } - ); - }, - - "class": function(node, resultVar) { if (node.parts.length > 0) { - var regexp = "/^[" - + (node.inverted ? "^" : "") + regexp = '/^[' + + (node.inverted ? '^' : '') + map(node.parts, function(part) { return part instanceof Array ? quoteForRegexpClass(part[0]) - + "-" + + '-' + quoteForRegexpClass(part[1]) : quoteForRegexpClass(part); - }).join("") - + "]/"; + }).join('') + + ']/' + (node.ignoreCase ? 'i' : ''); } else { /* * Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so * we translate them into euqivalents it can handle. */ - var regexp = node.inverted ? "/^[\\S\\s]/" : "/^(?!)/"; + regexp = node.inverted ? '/^[\\S\\s]/' : '/^(?!)/'; } - return formatCode( - "if (input.substr(pos).match(${regexp}) !== null) {", - " var ${resultVar} = input.charAt(pos);", - " pos++;", - "} else {", - " var ${resultVar} = null;", - " if (reportMatchFailures) {", - " matchFailed(${rawText|string});", - " }", - "}", - { - regexp: regexp, - rawText: node.rawText, - resultVar: resultVar - } - ); + return fill("class", { node: node, regexp: regexp }); } }); return emit(ast); }; -if (typeof module === "object") { - module.exports = PEG; -} else if (typeof window === "object") { - window.PEG = PEG; -} else { - throw new Error("Can't export PEG library (no \"module\" nor \"window\" object detected)."); -} +return PEG; })(); + +if (typeof module !== "undefined") { + module.exports = PEG; +}