/** * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This file is also a Helma module, referenced by its path! */ AceLexer = (function lexer_init() { // utility functions, make this file self-contained function forEach(array, func) { for(var i=0;i 0) { var state = statesToProcess.shift(); var stateReady = true; forEach(regexData[state], function (c) { if ((typeof c) == "object" && c.include) { var otherState = c.include; if (/!$/.exec(otherState)) { otherState = otherState.substring(0, otherState.length-1); } if (! sortedStatesMap[otherState]) { stateReady = false; return true; } } }); if (stateReady) { sortedStates.push(state); sortedStatesMap[state] = true; } else { // move to end of queue statesToProcess.push(state); } } forEach(sortedStates, function(state) { var cases = regexData[state]; var procCases = []; forEach(cases, function (c) { if ((typeof c) == "object" && c.include) { var otherState = c.include; var isBang = false; if (/!$/.exec(otherState)) { // "bang" include, returns to other state otherState = otherState.substring(0, otherState.length-1); isBang = true; } forEach(procCasesMap[otherState], function (d) { var dd = [d[0], d[1], d[2]]; if (isBang) { if (! (d[2] && d[2][0] && d[2][0].indexOf('#pop') != 0)) { dd[2] = ['#pop', otherState].concat(d[2] || []); } } procCases.push(dd); }); } else procCases.push(c); }); procCasesMap[state] = procCases; data[state] = { switcher: makeRegexSwitch(map(procCases, function(x) { return x[0]; }), flags), tokenTypes: map(procCases, function(x) { return x[1]; }), stateEffects: map(procCases, function(y) { var x = y[2]; if (!x) return []; if (isArray(x)) return x; return [x]; }) } }); // mutates stateStack, calls tokenFunc on each new token in order, returns new index return function(string, startIndex, stateStack, tokenFunc) { var stateBefore = stateStack.join('/'); while (true) { // loop until non-zero-length token var stateData = data[stateStack[stateStack.length-1]]; var switcherResult = stateData.switcher(string, startIndex); var whichCase = switcherResult.whichCase; var regexResult = switcherResult.result; var tokenTypes, stateEffects; if (whichCase < 0) { tokenTypes = 'Error'; stateEffects = null; } else { tokenTypes = stateData.tokenTypes[whichCase]; stateEffects = stateData.stateEffects[whichCase]; } if (stateEffects) { forEach(stateEffects, function (se) { if (se === '#pop') stateStack.pop(); else if (se === '#popall') { while (stateStack.length > 0) stateStack.pop(); } else stateStack.push(se); }); } var stateAfter = stateStack.join('/'); if (regexResult[0].length > 0) { if ((typeof tokenTypes) === "object" && tokenTypes.bygroups) { var types = tokenTypes.bygroups; forEach(types, function (t,i) { var tkn = { width:regexResult[i+1].length, type:t }; if (i == 0) tkn.stateBefore = stateBefore; if (i == (types.length-1)) tkn.stateAfter = stateAfter; tokenFunc(tkn); }); } else { tokenFunc({ width:regexResult[0].length, type:tokenTypes, stateBefore:stateBefore, stateAfter:stateAfter }); } return startIndex + regexResult[0].length; } } } } function makeSimpleLexer(tokenProducer) { function lexString(str, tokenFunc) { var state = ['root']; var idx = 0; while (idx < str.length) { var i = idx; idx = tokenProducer(str, idx, state, function (tkn) { tokenFunc(str.substr(i, tkn.width), tkn.type); i += tkn.width; }); } } function lexAsLines(str, tokenFunc, newLineFunc) { str += "\n"; var nextNewline = str.indexOf('\n'); var curIndex = 0; lexString(str, function(txt, typ) { var wid = txt.length; var widthLeft = wid; while (widthLeft > 0 && curIndex + wid > nextNewline) { var w = nextNewline - curIndex; if (w > 0) { tokenFunc(str.substr(curIndex, w), typ); } curIndex += (w+1); widthLeft -= (w+1); if (curIndex < str.length) { newLineFunc(); nextNewline = str.indexOf("\n", curIndex); } } if (widthLeft > 0) { tokenFunc(str.substr(curIndex, widthLeft), typ); curIndex += widthLeft; } }); } return {lexString:lexString, lexAsLines:lexAsLines}; } var txtTokenProducer = makeTokenProducer( { 'root': [ [/.*?\n/, 'Text'], [/.+/, 'Text'] ] }, 's'); var jsTokenProducer = makeTokenProducer( { 'root': [ [/\/\*[^\w\n]+appjet:version[^\w\n]+[0-9.]+[^\w\n]+\*\/[^\w\n]*(?=\n)/, 'Comment.Special', 'main'], [/(?:)/m, 'Text', ['main', 'regex-ready', 'linestart']] ], 'whitespace' : [ [/\n/, 'Text', 'linestart'], [/[^\S\n]+/, 'Text'], [/\/\*/, 'Comment', 'longcomment'] ], 'common' : [ {include:'whitespace'}, [/\"/, 'String.Double', 'dstr'], [/\'/, 'String.Single', 'sstr'] ], 'regex-ready' : [ {include:'whitespace'}, [/\/(?:[^[\\\n\/]|\\.|\[\^?]?(?:[^\\\]\n]|\\.)+\]?)+\/[gim]*/, 'String.Regex'], [/(?:)/m, 'Text', '#pop'] ], 'main': [ [/\"\"\"/, 'String.Doc', 'mstr'], {include:"common"}, [/