// CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.10 (2011-11-08)
// This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com)
CodeMirror.defineMode("perl",function(config,parserConfig){
	// http://perldoc.perl.org
	var PERL={				    	//   null - magic touch
							//   1 - keyword
							//   2 - def
							//   3 - atom
							//   4 - operator
							//   5 - variable-2 (predefined)
							//   [x,y] - x=1,2,3; y=must be defined if x{...}
						//	PERL operators
		'->'				:   4,
		'++'				:   4,
		'--'				:   4,
		'**'				:   4,
							//   ! ~ \ and unary + and -
		'=~'				:   4,
		'!~'				:   4,
		'*'				:   4,
		'/'				:   4,
		'%'				:   4,
		'x'				:   4,
		'+'				:   4,
		'-'				:   4,
		'.'				:   4,
		'<<'				:   4,
		'>>'				:   4,
							//   named unary operators
		'<'				:   4,
		'>'				:   4,
		'<='				:   4,
		'>='				:   4,
		'lt'				:   4,
		'gt'				:   4,
		'le'				:   4,
		'ge'				:   4,
		'=='				:   4,
		'!='				:   4,
		'<=>'				:   4,
		'eq'				:   4,
		'ne'				:   4,
		'cmp'				:   4,
		'~~'				:   4,
		'&'				:   4,
		'|'				:   4,
		'^'				:   4,
		'&&'				:   4,
		'||'				:   4,
		'//'				:   4,
		'..'				:   4,
		'...'				:   4,
		'?'				:   4,
		':'				:   4,
		'='				:   4,
		'+='				:   4,
		'-='				:   4,
		'*='				:   4,	//   etc. ???
		','				:   4,
		'=>'				:   4,
		'::'				:   4,
				   			//   list operators (rightward)
		'not'				:   4,
		'and'				:   4,
		'or'				:   4,
		'xor'				:   4,
						//	PERL predefined variables (I know, what this is a paranoid idea, but may be needed for people, who learn PERL, and for me as well, ...and may be for you?;)
		'BEGIN'				:   [5,1],
		'END'				:   [5,1],
		'PRINT'				:   [5,1],
		'PRINTF'			:   [5,1],
		'GETC'				:   [5,1],
		'READ'				:   [5,1],
		'READLINE'			:   [5,1],
		'DESTROY'			:   [5,1],
		'TIE'				:   [5,1],
		'TIEHANDLE'			:   [5,1],
		'UNTIE'				:   [5,1],
		'STDIN'				:    5,
		'STDIN_TOP'			:    5,
		'STDOUT'			:    5,
		'STDOUT_TOP'			:    5,
		'STDERR'			:    5,
		'STDERR_TOP'			:    5,
		'$ARG'				:    5,
		'$_'				:    5,
		'@ARG'				:    5,
		'@_'				:    5,
		'$LIST_SEPARATOR'		:    5,
		'$"'				:    5,
		'$PROCESS_ID'			:    5,
		'$PID'				:    5,
		'$$'				:    5,
		'$REAL_GROUP_ID'		:    5,
		'$GID'				:    5,
		'$('				:    5,
		'$EFFECTIVE_GROUP_ID'		:    5,
		'$EGID'				:    5,
		'$)'				:    5,
		'$PROGRAM_NAME'			:    5,
		'$0'				:    5,
		'$SUBSCRIPT_SEPARATOR'		:    5,
		'$SUBSEP'			:    5,
		'$;'				:    5,
		'$REAL_USER_ID'			:    5,
		'$UID'				:    5,
		'$<'				:    5,
		'$EFFECTIVE_USER_ID'		:    5,
		'$EUID'				:    5,
		'$>'				:    5,
		'$a'				:    5,
		'$b'				:    5,
		'$COMPILING'			:    5,
		'$^C'				:    5,
		'$DEBUGGING'			:    5,
		'$^D'				:    5,
		'${^ENCODING}'			:    5,
		'$ENV'				:    5,
		'%ENV'				:    5,
		'$SYSTEM_FD_MAX'		:    5,
		'$^F'				:    5,
		'@F'				:    5,
		'${^GLOBAL_PHASE}'		:    5,
		'$^H'				:    5,
		'%^H'				:    5,
		'@INC'				:    5,
		'%INC'				:    5,
		'$INPLACE_EDIT'			:    5,
		'$^I'				:    5,
		'$^M'				:    5,
		'$OSNAME'			:    5,
		'$^O'				:    5,
		'${^OPEN}'			:    5,
		'$PERLDB'			:    5,
		'$^P'				:    5,
		'$SIG'				:    5,
		'%SIG'				:    5,
		'$BASETIME'			:    5,
		'$^T'				:    5,
		'${^TAINT}'			:    5,
		'${^UNICODE}'			:    5,
		'${^UTF8CACHE}'			:    5,
		'${^UTF8LOCALE}'		:    5,
		'$PERL_VERSION'			:    5,
		'$^V'				:    5,
		'${^WIN32_SLOPPY_STAT}'		:    5,
		'$EXECUTABLE_NAME'		:    5,
		'$^X'				:    5,
		'$1'				:    5,	// - regexp $1, $2...
		'$MATCH'			:    5,
		'$&'				:    5,
		'${^MATCH}'			:    5,
		'$PREMATCH'			:    5,
		'$`'				:    5,
		'${^PREMATCH}'			:    5,
		'$POSTMATCH'			:    5,
		"$'"				:    5,
		'${^POSTMATCH}'			:    5,
		'$LAST_PAREN_MATCH'		:    5,
		'$+'				:    5,
		'$LAST_SUBMATCH_RESULT'		:    5,
		'$^N'				:    5,
		'@LAST_MATCH_END'		:    5,
		'@+'				:    5,
		'%LAST_PAREN_MATCH'		:    5,
		'%+'				:    5,
		'@LAST_MATCH_START'		:    5,
		'@-'				:    5,
		'%LAST_MATCH_START'		:    5,
		'%-'				:    5,
		'$LAST_REGEXP_CODE_RESULT'	:    5,
		'$^R'				:    5,
		'${^RE_DEBUG_FLAGS}'		:    5,
		'${^RE_TRIE_MAXBUF}'		:    5,
		'$ARGV'				:    5,
		'@ARGV'				:    5,
		'ARGV'				:    5,
		'ARGVOUT'			:    5,
		'$OUTPUT_FIELD_SEPARATOR'	:    5,
		'$OFS'				:    5,
		'$,'				:    5,
		'$INPUT_LINE_NUMBER'		:    5,
		'$NR'				:    5,
		'$.'				:    5,
		'$INPUT_RECORD_SEPARATOR'	:    5,
		'$RS'				:    5,
		'$/'				:    5,
		'$OUTPUT_RECORD_SEPARATOR'	:    5,
		'$ORS'				:    5,
		'$\\'				:    5,
		'$OUTPUT_AUTOFLUSH'		:    5,
		'$|'				:    5,
		'$ACCUMULATOR'			:    5,
		'$^A'				:    5,
		'$FORMAT_FORMFEED'		:    5,
		'$^L'				:    5,
		'$FORMAT_PAGE_NUMBER'		:    5,
		'$%'				:    5,
		'$FORMAT_LINES_LEFT'		:    5,
		'$-'				:    5,
		'$FORMAT_LINE_BREAK_CHARACTERS'	:    5,
		'$:'				:    5,
		'$FORMAT_LINES_PER_PAGE'	:    5,
		'$='				:    5,
		'$FORMAT_TOP_NAME'		:    5,
		'$^'				:    5,
		'$FORMAT_NAME'			:    5,
		'$~'				:    5,
		'${^CHILD_ERROR_NATIVE}'	:    5,
		'$EXTENDED_OS_ERROR'		:    5,
		'$^E'				:    5,
		'$EXCEPTIONS_BEING_CAUGHT'	:    5,
		'$^S'				:    5,
		'$WARNING'			:    5,
		'$^W'				:    5,
		'${^WARNING_BITS}'		:    5,
		'$OS_ERROR'			:    5,
		'$ERRNO'			:    5,
		'$!'				:    5,
		'%OS_ERROR'			:    5,
		'%ERRNO'			:    5,
		'%!'				:    5,
		'$CHILD_ERROR'			:    5,
		'$?'				:    5,
		'$EVAL_ERROR'			:    5,
		'$@'				:    5,
		'$OFMT'				:    5,
		'$#'				:    5,
		'$*'				:    5,
		'$ARRAY_BASE'			:    5,
		'$['				:    5,
		'$OLD_PERL_VERSION'		:    5,
		'$]'				:    5,
						//	PERL blocks
		'if'				:[1,1],
		elsif				:[1,1],
		'else'				:[1,1],
		'while'				:[1,1],
		unless				:[1,1],
		'for'				:[1,1],
		foreach				:[1,1],
						//	PERL functions
		'abs'				:1,	// - absolute value function
		accept				:1,	// - accept an incoming socket connect
		alarm				:1,	// - schedule a SIGALRM
		'atan2'				:1,	// - arctangent of Y/X in the range -PI to PI
		bind				:1,	// - binds an address to a socket
		binmode				:1,	// - prepare binary files for I/O
		bless				:1,	// - create an object
		bootstrap			:1,	//
		'break'				:1,	// - break out of a "given" block
		caller				:1,	// - get context of the current subroutine call
		chdir				:1,	// - change your current working directory
		chmod				:1,	// - changes the permissions on a list of files
		chomp				:1,	// - remove a trailing record separator from a string
		chop				:1,	// - remove the last character from a string
		chown				:1,	// - change the owership on a list of files
		chr				:1,	// - get character this number represents
		chroot				:1,	// - make directory new root for path lookups
		close				:1,	// - close file (or pipe or socket) handle
		closedir			:1,	// - close directory handle
		connect				:1,	// - connect to a remote socket
		'continue'			:[1,1],	// - optional trailing block in a while or foreach
		'cos'				:1,	// - cosine function
		crypt				:1,	// - one-way passwd-style encryption
		dbmclose			:1,	// - breaks binding on a tied dbm file
		dbmopen				:1,	// - create binding on a tied dbm file
		'default'			:1,	//
		defined				:1,	// - test whether a value, variable, or function is defined
		'delete'			:1,	// - deletes a value from a hash
		die				:1,	// - raise an exception or bail out
		'do'				:1,	// - turn a BLOCK into a TERM
		dump				:1,	// - create an immediate core dump
		each				:1,	// - retrieve the next key/value pair from a hash
		endgrent			:1,	// - be done using group file
		endhostent			:1,	// - be done using hosts file
		endnetent			:1,	// - be done using networks file
		endprotoent			:1,	// - be done using protocols file
		endpwent			:1,	// - be done using passwd file
		endservent			:1,	// - be done using services file
		eof				:1,	// - test a filehandle for its end
		'eval'				:1,	// - catch exceptions or compile and run code
		'exec'				:1,	// - abandon this program to run another
		exists				:1,	// - test whether a hash key is present
		exit				:1,	// - terminate this program
		'exp'				:1,	// - raise I to a power
		fcntl				:1,	// - file control system call
		fileno				:1,	// - return file descriptor from filehandle
		flock				:1,	// - lock an entire file with an advisory lock
		fork				:1,	// - create a new process just like this one
		format				:1,	// - declare a picture format with use by the write() function
		formline			:1,	// - internal function used for formats
		getc				:1,	// - get the next character from the filehandle
		getgrent			:1,	// - get next group record
		getgrgid			:1,	// - get group record given group user ID
		getgrnam			:1,	// - get group record given group name
		gethostbyaddr			:1,	// - get host record given its address
		gethostbyname			:1,	// - get host record given name
		gethostent			:1,	// - get next hosts record
		getlogin			:1,	// - return who logged in at this tty
		getnetbyaddr			:1,	// - get network record given its address
		getnetbyname			:1,	// - get networks record given name
		getnetent			:1,	// - get next networks record
		getpeername			:1,	// - find the other end of a socket connection
		getpgrp				:1,	// - get process group
		getppid				:1,	// - get parent process ID
		getpriority			:1,	// - get current nice value
		getprotobyname			:1,	// - get protocol record given name
		getprotobynumber		:1,	// - get protocol record numeric protocol
		getprotoent			:1,	// - get next protocols record
		getpwent			:1,	// - get next passwd record
		getpwnam			:1,	// - get passwd record given user login name
		getpwuid			:1,	// - get passwd record given user ID
		getservbyname			:1,	// - get services record given its name
		getservbyport			:1,	// - get services record given numeric port
		getservent			:1,	// - get next services record
		getsockname			:1,	// - retrieve the sockaddr for a given socket
		getsockopt			:1,	// - get socket options on a given socket
		given				:1,	//
		glob				:1,	// - expand filenames using wildcards
		gmtime				:1,	// - convert UNIX time into record or string using Greenwich time
		'goto'				:1,	// - create spaghetti code
		grep				:1,	// - locate elements in a list test true against a given criterion
		hex				:1,	// - convert a string to a hexadecimal number
		'import'			:1,	// - patch a module's namespace into your own
		index				:1,	// - find a substring within a string
		'int'				:1,	// - get the integer portion of a number
		ioctl				:1,	// - system-dependent device control system call
		'join'				:1,	// - join a list into a string using a separator
		keys				:1,	// - retrieve list of indices from a hash
		kill				:1,	// - send a signal to a process or process group
		last				:1,	// - exit a block prematurely
		lc				:1,	// - return lower-case version of a string
		lcfirst				:1,	// - return a string with just the next letter in lower case
		length				:1,	// - return the number of bytes in a string
		'link'				:1,	// - create a hard link in the filesytem
		listen				:1,	// - register your socket as a server
		local				: 2,	// - create a temporary value for a global variable (dynamic scoping)
		localtime			:1,	// - convert UNIX time into record or string using local time
		lock				:1,	// - get a thread lock on a variable, subroutine, or method
		'log'				:1,	// - retrieve the natural logarithm for a number
		lstat				:1,	// - stat a symbolic link
		m				:null,	// - match a string with a regular expression pattern
		map				:1,	// - apply a change to a list to get back a new list with the changes
		mkdir				:1,	// - create a directory
		msgctl				:1,	// - SysV IPC message control operations
		msgget				:1,	// - get SysV IPC message queue
		msgrcv				:1,	// - receive a SysV IPC message from a message queue
		msgsnd				:1,	// - send a SysV IPC message to a message queue
		my				: 2,	// - declare and assign a local variable (lexical scoping)
		'new'				:1,	//
		next				:1,	// - iterate a block prematurely
		no				:1,	// - unimport some module symbols or semantics at compile time
		oct				:1,	// - convert a string to an octal number
		open				:1,	// - open a file, pipe, or descriptor
		opendir				:1,	// - open a directory
		ord				:1,	// - find a character's numeric representation
		our				: 2,	// - declare and assign a package variable (lexical scoping)
		pack				:1,	// - convert a list into a binary representation
		'package'			:1,	// - declare a separate global namespace
		pipe				:1,	// - open a pair of connected filehandles
		pop				:1,	// - remove the last element from an array and return it
		pos				:1,	// - find or set the offset for the last/next m//g search
		print				:1,	// - output a list to a filehandle
		printf				:1,	// - output a formatted list to a filehandle
		prototype			:1,	// - get the prototype (if any) of a subroutine
		push				:1,	// - append one or more elements to an array
		q				:null,	// - singly quote a string
		qq				:null,	// - doubly quote a string
		qr				:null,	// - Compile pattern
		quotemeta			:null,	// - quote regular expression magic characters
		qw				:null,	// - quote a list of words
		qx				:null,	// - backquote quote a string
		rand				:1,	// - retrieve the next pseudorandom number
		read				:1,	// - fixed-length buffered input from a filehandle
		readdir				:1,	// - get a directory from a directory handle
		readline			:1,	// - fetch a record from a file
		readlink			:1,	// - determine where a symbolic link is pointing
		readpipe			:1,	// - execute a system command and collect standard output
		recv				:1,	// - receive a message over a Socket
		redo				:1,	// - start this loop iteration over again
		ref				:1,	// - find out the type of thing being referenced
		rename				:1,	// - change a filename
		require				:1,	// - load in external functions from a library at runtime
		reset				:1,	// - clear all variables of a given name
		'return'			:1,	// - get out of a function early
		reverse				:1,	// - flip a string or a list
		rewinddir			:1,	// - reset directory handle
		rindex				:1,	// - right-to-left substring search
		rmdir				:1,	// - remove a directory
		s				:null,	// - replace a pattern with a string
		say				:1,	// - print with newline
		scalar				:1,	// - force a scalar context
		seek				:1,	// - reposition file pointer for random-access I/O
		seekdir				:1,	// - reposition directory pointer
		select				:1,	// - reset default output or do I/O multiplexing
		semctl				:1,	// - SysV semaphore control operations
		semget				:1,	// - get set of SysV semaphores
		semop				:1,	// - SysV semaphore operations
		send				:1,	// - send a message over a socket
		setgrent			:1,	// - prepare group file for use
		sethostent			:1,	// - prepare hosts file for use
		setnetent			:1,	// - prepare networks file for use
		setpgrp				:1,	// - set the process group of a process
		setpriority			:1,	// - set a process's nice value
		setprotoent			:1,	// - prepare protocols file for use
		setpwent			:1,	// - prepare passwd file for use
		setservent			:1,	// - prepare services file for use
		setsockopt			:1,	// - set some socket options
		shift				:1,	// - remove the first element of an array, and return it
		shmctl				:1,	// - SysV shared memory operations
		shmget				:1,	// - get SysV shared memory segment identifier
		shmread				:1,	// - read SysV shared memory
		shmwrite			:1,	// - write SysV shared memory
		shutdown			:1,	// - close down just half of a socket connection
		'sin'				:1,	// - return the sine of a number
		sleep				:1,	// - block for some number of seconds
		socket				:1,	// - create a socket
		socketpair			:1,	// - create a pair of sockets
		'sort'				:1,	// - sort a list of values
		splice				:1,	// - add or remove elements anywhere in an array
		'split'				:1,	// - split up a string using a regexp delimiter
		sprintf				:1,	// - formatted print into a string
		'sqrt'				:1,	// - square root function
		srand				:1,	// - seed the random number generator
		stat				:1,	// - get a file's status information
		state				:1,	// - declare and assign a state variable (persistent lexical scoping)
		study				:1,	// - optimize input data for repeated searches
		'sub'				:1,	// - declare a subroutine, possibly anonymously
		'substr'			:1,	// - get or alter a portion of a stirng
		symlink				:1,	// - create a symbolic link to a file
		syscall				:1,	// - execute an arbitrary system call
		sysopen				:1,	// - open a file, pipe, or descriptor
		sysread				:1,	// - fixed-length unbuffered input from a filehandle
		sysseek				:1,	// - position I/O pointer on handle used with sysread and syswrite
		system				:1,	// - run a separate program
		syswrite			:1,	// - fixed-length unbuffered output to a filehandle
		tell				:1,	// - get current seekpointer on a filehandle
		telldir				:1,	// - get current seekpointer on a directory handle
		tie				:1,	// - bind a variable to an object class
		tied				:1,	// - get a reference to the object underlying a tied variable
		time				:1,	// - return number of seconds since 1970
		times				:1,	// - return elapsed time for self and child processes
		tr				:null,	// - transliterate a string
		truncate			:1,	// - shorten a file
		uc				:1,	// - return upper-case version of a string
		ucfirst				:1,	// - return a string with just the next letter in upper case
		umask				:1,	// - set file creation mode mask
		undef				:1,	// - remove a variable or function definition
		unlink				:1,	// - remove one link to a file
		unpack				:1,	// - convert binary structure into normal perl variables
		unshift				:1,	// - prepend more elements to the beginning of a list
		untie				:1,	// - break a tie binding to a variable
		use				:1,	// - load in a module at compile time
		utime				:1,	// - set a file's last access and modify times
		values				:1,	// - return a list of the values in a hash
		vec				:1,	// - test or set particular bits in a string
		wait				:1,	// - wait for any child process to die
		waitpid				:1,	// - wait for a particular child process to die
		wantarray			:1,	// - get void vs scalar vs list context of current subroutine call
		warn				:1,	// - print debugging info
		when				:1,	//
		write				:1,	// - print a picture record
		y				:null};	// - transliterate a string

	var RXstyle="string-2";
	var RXmodifiers=/[goseximacplud]/;		// NOTE: "m", "s", "y" and "tr" need to correct real modifiers for each regexp type

	function tokenChain(stream,state,chain,style,tail){	// NOTE: chain.length > 2 is not working now (it's for s[...][...]geos;)
		state.chain=null;                               //                                                          12   3tail
		state.style=null;
		state.tail=null;
		state.tokenize=function(stream,state){
			var e=false,c,i=0;
			while(c=stream.next()){
				if(c===chain[i]&&!e){
					if(chain[++i]!==undefined){
						state.chain=chain[i];
						state.style=style;
						state.tail=tail}
					else if(tail)
						stream.eatWhile(tail);
					state.tokenize=tokenPerl;
					return style}
				e=!e&&c=="\\"}
			return style};
		return state.tokenize(stream,state)}

	function tokenSOMETHING(stream,state,string){
		state.tokenize=function(stream,state){
			if(stream.string==string)
				state.tokenize=tokenPerl;
			stream.skipToEnd();
			return "string"};
		return state.tokenize(stream,state)}

	function tokenPerl(stream,state){
		if(stream.eatSpace())
			return null;
		if(state.chain)
			return tokenChain(stream,state,state.chain,state.style,state.tail);
		if(stream.match(/^\-?[\d\.]/,false))
			if(stream.match(/^(\-?(\d*\.\d+(e[+-]?\d+)?|\d+\.\d*)|0x[\da-fA-F]+|0b[01]+|\d+(e[+-]?\d+)?)/))
				return 'number';
		if(stream.match(/^<<(?=\w)/)){			// NOTE: <<SOMETHING\n...\nSOMETHING\n
			stream.eatWhile(/\w/);
			return tokenSOMETHING(stream,state,stream.current().substr(2))}
		if(stream.sol()&&stream.match(/^\=item(?!\w)/)){// NOTE: \n=item...\n=cut\n
			return tokenSOMETHING(stream,state,'=cut')}
		var ch=stream.next();
		if(ch=='"'||ch=="'"){				// NOTE: ' or " or <<'SOMETHING'\n...\nSOMETHING\n or <<"SOMETHING"\n...\nSOMETHING\n
			if(stream.prefix(3)=="<<"+ch){
				var p=stream.pos;
				stream.eatWhile(/\w/);
				var n=stream.current().substr(1);
				if(n&&stream.eat(ch))
					return tokenSOMETHING(stream,state,n);
				stream.pos=p}
			return tokenChain(stream,state,[ch],"string")}
		if(ch=="q"){
			var c=stream.look(-2);
			if(!(c&&/\w/.test(c))){
				c=stream.look(0);
				if(c=="x"){
					c=stream.look(1);
					if(c=="("){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[")"],RXstyle,RXmodifiers)}
					if(c=="["){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["]"],RXstyle,RXmodifiers)}
					if(c=="{"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["}"],RXstyle,RXmodifiers)}
					if(c=="<"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[">"],RXstyle,RXmodifiers)}
					if(/[\^'"!~\/]/.test(c)){
						stream.eatSuffix(1);
						return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers)}}
				else if(c=="q"){
					c=stream.look(1);
					if(c=="("){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[")"],"string")}
					if(c=="["){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["]"],"string")}
					if(c=="{"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["}"],"string")}
					if(c=="<"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[">"],"string")}
					if(/[\^'"!~\/]/.test(c)){
						stream.eatSuffix(1);
						return tokenChain(stream,state,[stream.eat(c)],"string")}}
				else if(c=="w"){
					c=stream.look(1);
					if(c=="("){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[")"],"bracket")}
					if(c=="["){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["]"],"bracket")}
					if(c=="{"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["}"],"bracket")}
					if(c=="<"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[">"],"bracket")}
					if(/[\^'"!~\/]/.test(c)){
						stream.eatSuffix(1);
						return tokenChain(stream,state,[stream.eat(c)],"bracket")}}
				else if(c=="r"){
					c=stream.look(1);
					if(c=="("){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[")"],RXstyle,RXmodifiers)}
					if(c=="["){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["]"],RXstyle,RXmodifiers)}
					if(c=="{"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,["}"],RXstyle,RXmodifiers)}
					if(c=="<"){
						stream.eatSuffix(2);
						return tokenChain(stream,state,[">"],RXstyle,RXmodifiers)}
					if(/[\^'"!~\/]/.test(c)){
						stream.eatSuffix(1);
						return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers)}}
				else if(/[\^'"!~\/(\[{<]/.test(c)){
					if(c=="("){
						stream.eatSuffix(1);
						return tokenChain(stream,state,[")"],"string")}
					if(c=="["){
						stream.eatSuffix(1);
						return tokenChain(stream,state,["]"],"string")}
					if(c=="{"){
						stream.eatSuffix(1);
						return tokenChain(stream,state,["}"],"string")}
					if(c=="<"){
						stream.eatSuffix(1);
						return tokenChain(stream,state,[">"],"string")}
					if(/[\^'"!~\/]/.test(c)){
						return tokenChain(stream,state,[stream.eat(c)],"string")}}}}
		if(ch=="m"){
			var c=stream.look(-2);
			if(!(c&&/\w/.test(c))){
				c=stream.eat(/[(\[{<\^'"!~\/]/);
				if(c){
					if(/[\^'"!~\/]/.test(c)){
						return tokenChain(stream,state,[c],RXstyle,RXmodifiers)}
					if(c=="("){
						return tokenChain(stream,state,[")"],RXstyle,RXmodifiers)}
					if(c=="["){
						return tokenChain(stream,state,["]"],RXstyle,RXmodifiers)}
					if(c=="{"){
						return tokenChain(stream,state,["}"],RXstyle,RXmodifiers)}
					if(c=="<"){
						return tokenChain(stream,state,[">"],RXstyle,RXmodifiers)}}}}
		if(ch=="s"){
			var c=/[\/>\]})\w]/.test(stream.look(-2));
			if(!c){
				c=stream.eat(/[(\[{<\^'"!~\/]/);
				if(c){
					if(c=="[")
						return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers);
					if(c=="{")
						return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers);
					if(c=="<")
						return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers);
					if(c=="(")
						return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers);
					return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers)}}}
		if(ch=="y"){
			var c=/[\/>\]})\w]/.test(stream.look(-2));
			if(!c){
				c=stream.eat(/[(\[{<\^'"!~\/]/);
				if(c){
					if(c=="[")
						return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers);
					if(c=="{")
						return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers);
					if(c=="<")
						return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers);
					if(c=="(")
						return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers);
					return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers)}}}
		if(ch=="t"){
			var c=/[\/>\]})\w]/.test(stream.look(-2));
			if(!c){
				c=stream.eat("r");if(c){
				c=stream.eat(/[(\[{<\^'"!~\/]/);
				if(c){
					if(c=="[")
						return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers);
					if(c=="{")
						return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers);
					if(c=="<")
						return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers);
					if(c=="(")
						return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers);
					return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers)}}}}
		if(ch=="`"){
			return tokenChain(stream,state,[ch],"variable-2")}
		if(ch=="/"){
			if(!/~\s*$/.test(stream.prefix()))
				return "operator";
			else
				return tokenChain(stream,state,[ch],RXstyle,RXmodifiers)}
		if(ch=="$"){
			var p=stream.pos;
			if(stream.eatWhile(/\d/)||stream.eat("{")&&stream.eatWhile(/\d/)&&stream.eat("}"))
				return "variable-2";
			else
				stream.pos=p}
		if(/[$@%]/.test(ch)){
			var p=stream.pos;
			if(stream.eat("^")&&stream.eat(/[A-Z]/)||!/[@$%&]/.test(stream.look(-2))&&stream.eat(/[=|\\\-#?@;:&`~\^!\[\]*'"$+.,\/<>()]/)){
				var c=stream.current();
				if(PERL[c])
					return "variable-2"}
			stream.pos=p}
		if(/[$@%&]/.test(ch)){
			if(stream.eatWhile(/[\w$\[\]]/)||stream.eat("{")&&stream.eatWhile(/[\w$\[\]]/)&&stream.eat("}")){
				var c=stream.current();
				if(PERL[c])
					return "variable-2";
				else
					return "variable"}}
		if(ch=="#"){
			if(stream.look(-2)!="$"){
				stream.skipToEnd();
				return "comment"}}
		if(/[:+\-\^*$&%@=<>!?|\/~\.]/.test(ch)){
			var p=stream.pos;
			stream.eatWhile(/[:+\-\^*$&%@=<>!?|\/~\.]/);
			if(PERL[stream.current()])
				return "operator";
			else
				stream.pos=p}
		if(ch=="_"){
			if(stream.pos==1){
				if(stream.suffix(6)=="_END__"){
					return tokenChain(stream,state,['\0'],"comment")}
				else if(stream.suffix(7)=="_DATA__"){
					return tokenChain(stream,state,['\0'],"variable-2")}
				else if(stream.suffix(7)=="_C__"){
					return tokenChain(stream,state,['\0'],"string")}}}
		if(/\w/.test(ch)){
			var p=stream.pos;
			if(stream.look(-2)=="{"&&(stream.look(0)=="}"||stream.eatWhile(/\w/)&&stream.look(0)=="}"))
				return "string";
			else
				stream.pos=p}
		if(/[A-Z]/.test(ch)){
			var l=stream.look(-2);
			var p=stream.pos;
			stream.eatWhile(/[A-Z_]/);
			if(/[\da-z]/.test(stream.look(0))){
				stream.pos=p}
			else{
				var c=PERL[stream.current()];
				if(!c)
					return "meta";
				if(c[1])
					c=c[0];
				if(l!=":"){
					if(c==1)
						return "keyword";
					else if(c==2)
						return "def";
					else if(c==3)
						return "atom";
					else if(c==4)
						return "operator";
					else if(c==5)
						return "variable-2";
					else
						return "meta"}
				else
					return "meta"}}
		if(/[a-zA-Z_]/.test(ch)){
			var l=stream.look(-2);
			stream.eatWhile(/\w/);
			var c=PERL[stream.current()];
			if(!c)
				return "meta";
			if(c[1])
				c=c[0];
			if(l!=":"){
				if(c==1)
					return "keyword";
				else if(c==2)
					return "def";
				else if(c==3)
					return "atom";
				else if(c==4)
					return "operator";
				else if(c==5)
					return "variable-2";
				else
					return "meta"}
			else
				return "meta"}
		return null}

	return{
		startState:function(){
			return{
				tokenize:tokenPerl,
				chain:null,
				style:null,
				tail:null}},
		token:function(stream,state){
			return (state.tokenize||tokenPerl)(stream,state)},
		electricChars:"{}"}});

CodeMirror.defineMIME("text/x-perl", "perl");

// it's like "peek", but need for look-ahead or look-behind if index < 0
CodeMirror.StringStream.prototype.look=function(c){
	return this.string.charAt(this.pos+(c||0))};

// return a part of prefix of current stream from current position
CodeMirror.StringStream.prototype.prefix=function(c){
	if(c){
		var x=this.pos-c;
		return this.string.substr((x>=0?x:0),c)}
	else{
		return this.string.substr(0,this.pos-1)}};

// return a part of suffix of current stream from current position
CodeMirror.StringStream.prototype.suffix=function(c){
	var y=this.string.length;
	var x=y-this.pos+1;
	return this.string.substr(this.pos,(c&&c<y?c:x))};

// return a part of suffix of current stream from current position and change current position
CodeMirror.StringStream.prototype.nsuffix=function(c){
	var p=this.pos;
	var l=c||(this.string.length-this.pos+1);
	this.pos+=l;
	return this.string.substr(p,l)};

// eating and vomiting a part of stream from current position
CodeMirror.StringStream.prototype.eatSuffix=function(c){
	var x=this.pos+c;
	var y;
	if(x<=0)
		this.pos=0;
	else if(x>=(y=this.string.length-1))
		this.pos=y;
	else
		this.pos=x};
