diff options
Diffstat (limited to 'media/CodeMirror-0.62/contrib/python/js/parsepython.js')
-rw-r--r-- | media/CodeMirror-0.62/contrib/python/js/parsepython.js | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/media/CodeMirror-0.62/contrib/python/js/parsepython.js b/media/CodeMirror-0.62/contrib/python/js/parsepython.js new file mode 100644 index 0000000..4847c76 --- /dev/null +++ b/media/CodeMirror-0.62/contrib/python/js/parsepython.js @@ -0,0 +1,544 @@ +var PythonParser = Editor.Parser = (function() { + function wordRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$"); + } + var DELIMITERCLASS = 'py-delimiter'; + var LITERALCLASS = 'py-literal'; + var ERRORCLASS = 'py-error'; + var OPERATORCLASS = 'py-operator'; + var IDENTIFIERCLASS = 'py-identifier'; + var STRINGCLASS = 'py-string'; + var BYTESCLASS = 'py-bytes'; + var UNICODECLASS = 'py-unicode'; + var RAWCLASS = 'py-raw'; + var NORMALCONTEXT = 'normal'; + var STRINGCONTEXT = 'string'; + var singleOperators = '+-*/%&|^~<>'; + var doubleOperators = wordRegexp(['==', '!=', '\\<=', '\\>=', '\\<\\>', + '\\<\\<', '\\>\\>', '\\/\\/', '\\*\\*']); + var singleDelimiters = '()[]{}@,:.`=;'; + var doubleDelimiters = ['\\+=', '\\-=', '\\*=', '/=', '%=', '&=', '\\|=', + '\\^=']; + var tripleDelimiters = wordRegexp(['//=','\\>\\>=','\\<\\<=','\\*\\*=']); + var singleStarters = singleOperators + singleDelimiters + '=!'; + var doubleStarters = '=<>*/'; + var identifierStarters = /[_A-Za-z]/; + + var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']); + var commonkeywords = ['as', 'assert', 'break', 'class', 'continue', + 'def', 'del', 'elif', 'else', 'except', 'finally', + 'for', 'from', 'global', 'if', 'import', + 'lambda', 'pass', 'raise', 'return', + 'try', 'while', 'with', 'yield']; + var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate', + 'float', 'frozenset', 'int', 'list', 'object', + 'property', 'reversed', 'set', 'slice', 'staticmethod', + 'str', 'super', 'tuple', 'type']; + var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode', + 'xrange'], + 'keywords': ['exec', 'print'], + 'version': 2 }; + var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview', + 'open', 'range', 'zip'], + 'keywords': ['nonlocal'], + 'version': 3}; + + var py, keywords, types, stringStarters, stringTypes, config; + + function configure(conf) { + if (!conf.hasOwnProperty('pythonVersion')) { + conf.pythonVersion = 2; + } + if (!conf.hasOwnProperty('strictErrors')) { + conf.strictErrors = true; + } + if (conf.pythonVersion != 2 && conf.pythonVersion != 3) { + alert('CodeMirror: Unknown Python Version "' + + conf.pythonVersion + + '", defaulting to Python 2.x.'); + conf.pythonVersion = 2; + } + if (conf.pythonVersion == 3) { + py = py3; + stringStarters = /[\'\"rbRB]/; + stringTypes = /[rb]/; + doubleDelimiters.push('\\-\\>'); + } else { + py = py2; + stringStarters = /['"RUru]/; + stringTypes = /[ru]/; + } + config = conf; + keywords = wordRegexp(commonkeywords.concat(py.keywords)); + types = wordRegexp(commontypes.concat(py.types)); + doubleDelimiters = wordRegexp(doubleDelimiters); + } + + var tokenizePython = (function() { + function normal(source, setState) { + var stringDelim, threeStr, temp, type, word, possible = {}; + var ch = source.next(); + + function filterPossible(token, styleIfPossible) { + if (!possible.style && !possible.content) { + return token; + } else if (typeof(token) == STRINGCONTEXT) { + token = {content: source.get(), style: token}; + } + if (possible.style || styleIfPossible) { + token.style = styleIfPossible ? styleIfPossible : possible.style; + } + if (possible.content) { + token.content = possible.content + token.content; + } + possible = {}; + return token; + } + + // Handle comments + if (ch == '#') { + while (!source.endOfLine()) { + source.next(); + } + return 'py-comment'; + } + // Handle special chars + if (ch == '\\') { + if (source.peek() != '\n') { + var whitespace = true; + while (!source.endOfLine()) { + if(!(/\s/.test(source.next()))) { + whitespace = false; + } + } + if (!whitespace) { + return ERRORCLASS; + } + } + return 'py-special'; + } + // Handle operators and delimiters + if (singleStarters.indexOf(ch) != -1) { + if (doubleStarters.indexOf(source.peek()) != -1) { + temp = ch + source.peek(); + // It must be a double delimiter or operator or triple delimiter + if (doubleOperators.test(temp)) { + source.next(); + if (tripleDelimiters.test(temp + source.peek())) { + source.next(); + return DELIMITERCLASS; + } else { + return OPERATORCLASS; + } + } else if (doubleDelimiters.test(temp)) { + source.next(); + return DELIMITERCLASS; + } + } + // It must be a single delimiter or operator + if (singleOperators.indexOf(ch) != -1) { + return OPERATORCLASS; + } else if (singleDelimiters.indexOf(ch) != -1) { + if (ch == '@' && /\w/.test(source.peek())) { + possible = {style:'py-decorator', + content: source.get()}; + ch = source.next(); + } else if (ch == '.' && /\d/.test(source.peek())) { + possible = {style:LITERALCLASS, + content: source.get()}; + ch = source.next(); + } else { + return DELIMITERCLASS; + } + } else { + return ERRORCLASS; + } + } + // Handle number literals + if (/\d/.test(ch)) { + if (ch === '0' && !source.endOfLine()) { + switch (source.peek()) { + case 'o': + case 'O': + source.next(); + source.nextWhileMatches(/[0-7]/); + return filterPossible(LITERALCLASS, ERRORCLASS); + case 'x': + case 'X': + source.next(); + source.nextWhileMatches(/[0-9A-Fa-f]/); + return filterPossible(LITERALCLASS, ERRORCLASS); + case 'b': + case 'B': + source.next(); + source.nextWhileMatches(/[01]/); + return filterPossible(LITERALCLASS, ERRORCLASS); + } + } + source.nextWhileMatches(/\d/); + if (source.peek() == '.') { + source.next(); + source.nextWhileMatches(/\d/); + } + // Grab an exponent + if (source.peek().toLowerCase() == 'e') { + source.next(); + if (source.peek() == '+' || source.peek() == '-') { + source.next(); + } + if (/\d/.test(source.peek())) { + source.nextWhileMatches(/\d/); + } else { + return filterPossible(ERRORCLASS); + } + } + // Grab a complex number + if (source.peek().toLowerCase() == 'j') { + source.next(); + } + + return filterPossible(LITERALCLASS); + } + // Handle strings + if (stringStarters.test(ch)) { + var peek = source.peek(); + var stringType = STRINGCLASS; + if ((stringTypes.test(ch)) && (peek == '"' || peek == "'")) { + switch (ch.toLowerCase()) { + case 'b': + stringType = BYTESCLASS; + break; + case 'r': + stringType = RAWCLASS; + break; + case 'u': + stringType = UNICODECLASS; + break; + } + ch = source.next(); + stringDelim = ch; + if (source.peek() != stringDelim) { + setState(inString(stringType, stringDelim)); + return null; + } else { + source.next(); + if (source.peek() == stringDelim) { + source.next(); + threeStr = stringDelim + stringDelim + stringDelim; + setState(inString(stringType, threeStr)); + return null; + } else { + return stringType; + } + } + } else if (ch == "'" || ch == '"') { + stringDelim = ch; + if (source.peek() != stringDelim) { + setState(inString(stringType, stringDelim)); + return null; + } else { + source.next(); + if (source.peek() == stringDelim) { + source.next(); + threeStr = stringDelim + stringDelim + stringDelim; + setState(inString(stringType, threeStr)); + return null; + } else { + return stringType; + } + } + } + } + // Handle Identifier + if (identifierStarters.test(ch)) { + source.nextWhileMatches(/[\w\d]/); + word = source.get(); + if (wordOperators.test(word)) { + type = OPERATORCLASS; + } else if (keywords.test(word)) { + type = 'py-keyword'; + } else if (types.test(word)) { + type = 'py-type'; + } else { + type = IDENTIFIERCLASS; + while (source.peek() == '.') { + source.next(); + if (identifierStarters.test(source.peek())) { + source.nextWhileMatches(/[\w\d]/); + } else { + type = ERRORCLASS; + break; + } + } + word = word + source.get(); + } + return filterPossible({style: type, content: word}); + } + + // Register Dollar sign and Question mark as errors. Always! + if (/\$\?/.test(ch)) { + return filterPossible(ERRORCLASS); + } + + return filterPossible(ERRORCLASS); + } + + function inString(style, terminator) { + return function(source, setState) { + var matches = []; + var found = false; + while (!found && !source.endOfLine()) { + var ch = source.next(), newMatches = []; + // Skip escaped characters + if (ch == '\\') { + if (source.peek() == '\n') { + break; + } + ch = source.next(); + ch = source.next(); + } + if (ch == terminator.charAt(0)) { + matches.push(terminator); + } + for (var i = 0; i < matches.length; i++) { + var match = matches[i]; + if (match.charAt(0) == ch) { + if (match.length == 1) { + setState(normal); + found = true; + break; + } else { + newMatches.push(match.slice(1)); + } + } + } + matches = newMatches; + } + return style; + }; + } + + return function(source, startState) { + return tokenizer(source, startState || normal); + }; + })(); + + function parsePython(source) { + if (!keywords) { + configure({}); + } + + var tokens = tokenizePython(source); + var lastToken = null; + var column = 0; + var context = {prev: null, + endOfScope: false, + startNewScope: false, + level: 0, + next: null, + type: NORMALCONTEXT + }; + + function pushContext(level, type) { + type = type ? type : NORMALCONTEXT; + context = {prev: context, + endOfScope: false, + startNewScope: false, + level: level, + next: null, + type: type + }; + } + + function popContext(remove) { + remove = remove ? remove : false; + if (context.prev) { + if (remove) { + context = context.prev; + context.next = null; + } else { + context.prev.next = context; + context = context.prev; + } + } + } + + function indentPython(context) { + var temp; + return function(nextChars, currentLevel, direction) { + if (direction === null || direction === undefined) { + if (nextChars) { + while (context.next) { + context = context.next; + } + } + return context.level; + } + else if (direction === true) { + if (currentLevel == context.level) { + if (context.next) { + return context.next.level; + } else { + return context.level; + } + } else { + temp = context; + while (temp.prev && temp.prev.level > currentLevel) { + temp = temp.prev; + } + return temp.level; + } + } else if (direction === false) { + if (currentLevel > context.level) { + return context.level; + } else if (context.prev) { + temp = context; + while (temp.prev && temp.prev.level >= currentLevel) { + temp = temp.prev; + } + if (temp.prev) { + return temp.prev.level; + } else { + return temp.level; + } + } + } + return context.level; + }; + } + + var iter = { + next: function() { + var token = tokens.next(); + var type = token.style; + var content = token.content; + + if (lastToken) { + if (lastToken.content == 'def' && type == IDENTIFIERCLASS) { + token.style = 'py-func'; + } + if (lastToken.content == '\n') { + var tempCtx = context; + // Check for a different scope + if (type == 'whitespace' && context.type == NORMALCONTEXT) { + if (token.value.length < context.level) { + while (token.value.length < context.level) { + popContext(); + } + + if (token.value.length != context.level) { + context = tempCtx; + if (config.strictErrors) { + token.style = ERRORCLASS; + } + } else { + context.next = null; + } + } + } else if (context.level !== 0 && + context.type == NORMALCONTEXT) { + while (0 !== context.level) { + popContext(); + } + + if (context.level !== 0) { + context = tempCtx; + if (config.strictErrors) { + token.style = ERRORCLASS; + } + } + } + } + } + + // Handle Scope Changes + switch(type) { + case STRINGCLASS: + case BYTESCLASS: + case RAWCLASS: + case UNICODECLASS: + if (context.type !== STRINGCONTEXT) { + pushContext(context.level + 1, STRINGCONTEXT); + } + break; + default: + if (context.type === STRINGCONTEXT) { + popContext(true); + } + break; + } + switch(content) { + case '.': + case '@': + // These delimiters don't appear by themselves + if (content !== token.value) { + token.style = ERRORCLASS; + } + break; + case ':': + // Colons only delimit scope inside a normal scope + if (context.type === NORMALCONTEXT) { + context.startNewScope = context.level+indentUnit; + } + break; + case '(': + case '[': + case '{': + // These start a sequence scope + pushContext(column + content.length, 'sequence'); + break; + case ')': + case ']': + case '}': + // These end a sequence scope + popContext(true); + break; + case 'pass': + case 'return': + // These end a normal scope + if (context.type === NORMALCONTEXT) { + context.endOfScope = true; + } + break; + case '\n': + // Reset our column + column = 0; + // Make any scope changes + if (context.endOfScope) { + context.endOfScope = false; + popContext(); + } else if (context.startNewScope !== false) { + var temp = context.startNewScope; + context.startNewScope = false; + pushContext(temp, NORMALCONTEXT); + } + // Newlines require an indentation function wrapped in a closure for proper context. + token.indentation = indentPython(context); + break; + } + + // Keep track of current column for certain scopes. + if (content != '\n') { + column += token.value.length; + } + + lastToken = token; + return token; + }, + + copy: function() { + var _context = context, _tokenState = tokens.state; + return function(source) { + tokens = tokenizePython(source, _tokenState); + context = _context; + return iter; + }; + } + }; + return iter; + } + + return {make: parsePython, + electricChars: "", + configure: configure}; +})(); |