#!/usr/bin/env perl

my $VERSION = "0.6";
my $vernr = "0.9.2"; # this is the underlying version of Peter Palfraders script
########################################################################
#                                                                      #
# Code2HTML                                                            #
# ---------                                                            #
#                                                                      #
# Code2Html, peter AT palfrader.org                                    #
#                                                                      #
# $Date: 2002/01/12 21:17:02 $                                         #
# $Revision: 1.13 $                                                    #
# $Id: code2html,v 1.13 2002/01/12 21:17:02 weaselp Exp $              #
#                                                                      #
# AUTHOR                                                               #
#        Peter  Palfrader. Written in 1999, 2000, 2001, 2002.          #
#        A lot of other people.                                        #
#                                                                      #
# DESCRIPTION                                                          #
#        code2html is a  perlscript  which  converts  a  program       #
#        source  code  to syntax highlighted HTML by applying a set    #
#        of   regular   expressions   depending   on  the  language    #
#        the source code is written.                                   #
#                                                                      #
########################################################################

my $LANG_TEST_LENGTH = 1024;

use strict;
use POSIX;
use Term::ANSIColor;

use Getopt::Std;
our($opt_h, $opt_l, $opt_v);
getopts('l:hv') || exit 2;
Usage() if $opt_h;

my $filename;
if ($opt_l and $opt_l =~ /..*\../) { #dot within opt_l means file name
	$filename = $opt_l;
} elsif ($opt_l) {
	$filename = "dummy.$opt_l"
}
$opt_l = undef;

my %params = (
	infile       => $ARGV[0] || '-',
	langmode     => $opt_l ,
	filename     => $filename || '',
	alt_langmode =>  'html',    # language to use if can't tell
	verbose      =>  $opt_v,
);
my $text; # end result

# undefine the input record separator so everything gets loaded in one turn
local $/ = undef;

my $STYLESHEET = get_stylesheet();
my $LANGUAGE   = get_database();

# load the input file and set params{'langmode'}
# if it is not already. this is done by probing a
# set of rules defined in %LANGUAGE
print STDERR "loading input file...\n" if ($params{verbose});
my $code_ref = &get_input_file(\%params,
				$LANGUAGE,
				$params{langmode},
				$params{alt_langmode});

exit 0 if ! ref $code_ref;
# select the rules for out language.
my $language_rules_ref = $LANGUAGE->{lc($params{langmode})}{patterns};

print STDERR "applying stylesheet...\n" if ($params{verbose});
# Apply the Stylesheets
# set 'starttag' and 'endtag' for every rule according to
# its 'style' value the tags are defined in the stylesheet
apply_stylesheets_to_rules($language_rules_ref, $STYLESHEET);

my $snippetlist_ref = [];
print STDERR "creating snippet-list...\n" if $params{verbose};
create_snippetlist($language_rules_ref,
	$$code_ref, $snippetlist_ref, $STYLESHEET);

print STDERR "getting text converted code ...\n" if $params{verbose};
$text .= join '', @$snippetlist_ref;

# --- debug
# print " - debug :  \n";
# foreach my $key (keys %params) {
#   print " $key => " . $params{key} . "\n";
# }
#  return " - debug: done";
# ---------

$text =~ s/\e\[0m(\e\[\d\d?m)/$1/g;
1 while $text =~ s/(\e\[0m[^\e]+)\e\[0m/$1/g;

# Output code.
print "$text\n";

sub Usage {
	(my $prog = $0) =~ s!.*/!!;
	my $time = strftime("%F", localtime((stat($0))[9]));
	print <<EOF;
$prog V$VERSION $time based on Code2Html V$vernr (peter\@palfrader.org)
Convert a program source to syntax highlighted ASCII text

Usage: $prog [options] [input_file]
-l LANG  set language mode to LANG

Supported languages
	plain ada ada95 awk c c++ cc cpp cxx gpasm groff html xml
	java javascript js lisp m4 make makefile pas pascal perl
	pov povray python ruby sql patch shellscript sh
EOF
	exit;
}

##########################################################################
####################### get_input_file ###################################
##########################################################################
sub get_input_file {
	# in     : \%params
	# in     : %LANGUAGE;
	# in/out : $langmode;
	# in/out : $alt_langmode;
	# returns: input file

	my %PARAMS       = %{$_[0]};
	my %LANGUAGE     = %{$_[1]};
	my $langmode     = $_[2];
	my $alt_langmode = $_[3];

	if ($PARAMS{infile} eq '-') {
		*FILEHANDLE = *STDIN;
	} else {
		open(FILEHANDLE, $PARAMS{infile})
			|| return("While opening '$PARAMS{infile}' for input: ".$!."\n");
	}
	local $/=undef;
	my $code = <FILEHANDLE>;
	close(FILEHANDLE);

	if (not $langmode) {
		my $test_code = substr($code, 0, $LANG_TEST_LENGTH);
		#warn("language mode not given. guessing...\n");
		$langmode = '';

		for (sort keys %LANGUAGE) { # make it repeatable
			next if $LANGUAGE{$_}->{filename} eq '';
			if (($PARAMS{infile} =~ /$LANGUAGE{$_}->{filename}/
				|| ($PARAMS{filename} ne ''
				&& $PARAMS{filename} =~ /$LANGUAGE{$_}->{filename}/))
				|| (($LANGUAGE{$_}->{regex} ne '')
				&& ($test_code =~ /$LANGUAGE{$_}->{regex}/))
			) {
				$langmode = $_;
				last;
			};
		};

		if ($langmode eq '') {
			if (not $alt_langmode) {
				warn("Guessing language mode failed. " .
					"Using fallback mode: '$alt_langmode'\n");
				$langmode = $alt_langmode;
				$alt_langmode = '';
			} else {
				print $code;
				return("Guessing language mode failed.\n")
			};
		};
	};

	$_[2] = $langmode;
	$_[3] = $alt_langmode;
	print "==> append : to filename to switch off syntax highlighting\n"
		if ! $ENV{LESSQUIET};
	return \$code;
};

############################################################################
####################### apply_stylesheets_to_rules #########################
############################################################################
sub apply_stylesheets_to_rules {
	my ($regexps_ref, $style) = @_;

	for (@$regexps_ref) {
		my $re_style = $_->{style};
		warn ("Style '$re_style' not defined in stylesheet.\n")
			unless defined $style->{tags}{$_->{style}};
		$_->{starttag} = $style->{tags}{$re_style}{start};
		$_->{endtag} = $style->{tags}{$re_style}{stop};
		apply_stylesheets_to_rules($_->{childregex}, $style)
			if $_->{childregex};
	};
};

###########################################################################
####################### create_snippetlist ################################
###########################################################################
sub create_snippetlist {
	my ($regexps_ref, $code, $snippetlist_ref, $style_ref) = @_;
	my $length = length $code;

	## An array of regular expression structures, each of which is an
	## array. @res is kept sorted by starting position of the RExen and
	## then by the position of the regex in the language file. This allows
	## us to just evaluate $res[0], and to hand write fast code that typically
	## handles 90% of the cases without resorting to the _big_ guns.
	##
	## FWIW, I pronounce '@res' REEZE, as in the plural of '$re'.
	##
	my ($pos, @res);

	for (@$regexps_ref) {
		pos($code) = 0;
#++$m;
		next unless $code =~ /($_->{regex})/gms;

		$pos = pos($code);
#		$res[@res] = [
#			$_->{regex},
#			$ { $ { $$style_ref{'tags'} } { $_->{style} } } { 'start' },
#			$ { $ { $$style_ref{'tags'} } { $_->{style} } } { 'stop' },
#			$_->{childregex},
#			$pos - length( $1 ),
#			$pos,
#			scalar( @res ),
#		];
		$res[@res] = [
			$_->{regex},
			$_->{starttag},
			$_->{endtag},
			$_->{childregex},
			$pos - length($1),
			$pos,
			scalar(@res),
		];
	}

	## 90% of all child regexes end up with 0 or 1 regex that needs to be
	## worried about. Trimming out the 0's speeds things up a bit and
	## makes the below loop simpler, since there's always at least
	## 1 regexp. It donsn't speed things up much by itself: the percentage
	## of times this fires is really small. But it does simplify the loop
	## below and speed it up.
	unless (@res) {
		push @$snippetlist_ref, $code;
		return;
	}

	@res = sort {$a->[4] <=> $b->[4] || $a->[6] <=> $b->[6]} @res;

	## Add a dummy at the end, which makes the logic below simpler / faster.
	$res[@res] = [
		undef,
		undef,
		undef,
		undef,
		$length,
		$length,
		scalar( @res ),
	];

	## These are declared here for (minor) speed improvement.
	my ($re, $match_spos, $match_pos, $re_spos, $re_pos, $re_num, $prefix,
		$snippet, $rest, $i, $l, @changed_res, $j);

	$pos = 0;
MAIN:
	while ($pos < $length) {
		$re = $res[0];

		$match_spos = $re->[4];
		$match_pos  = $re->[5];

		if ($match_spos > $pos) {
			$prefix = substr($code, $pos, $match_spos - $pos);
			push @$snippetlist_ref, $prefix;
		}

		if ($match_pos > $match_spos) {
			$snippet = substr($code, $match_spos, $match_pos - $match_spos);
			if (@{$re->[3]}) {
				push @$snippetlist_ref, $re->[1];
				create_snippetlist($re->[3], $snippet, $snippetlist_ref, $style_ref);
				push @$snippetlist_ref, $re->[2];
			} else {
				push @$snippetlist_ref, $re->[1], $snippet, $re->[2];
			}
		}

		$pos = $match_pos;

		##
		## Hand coded optimizations. Luckily, the cases that arise most often
		## are the easiest to tune.
		##

# =pod

		if ($res[1]->[4] >= $pos) {
			## Only first regex needs to be moved, 2nd and later are still valid.
			## This is often 90% of the cases for Perl or C (others not tested,
			## just uncomment the $n, $o, and $p lines and try it yourself).
#++$n{1};
#++$m;
			pos($code) = $pos;
			unless ($code =~ /($re->[0])/gms) {
#++$o{'0'};
				if (@res == 2) {
					## If the only regexp left is the dummy, we're done.
					$rest = substr($code, $pos);
					push @$snippetlist_ref, $rest;
					last;
				}
				shift @res;
			} else {
				$re->[5] = $re_pos  = pos( $code );
				$re->[4] = $re_spos = $re_pos - length $1;

				## Walk down the array looking for $re's new home.
				## The first few loop iterations are unrolled and done manually
				## for speed, which handles 85 to 90% of the cases where only
				## $re needs to be moved.
				##
				## Here's where that dummy regexp at the end of the array comes
				## in handy: we don't need to worry about array size here, since
				## it will always be after $re no matter what. The unrolled
				## loop stuff is outdented to make the conditionals fit on one
				## 80 char line.
				## Element 4 in @{$res[x]} is the start position of the match.
				## Element 6 is the order in which it was declared in the lang file.
				$re_num = $re->[6];
				if (($re_spos <=> $res[1]->[4] || $re_num <=> $res[1]->[6]) <= 0) {
#++$o{'1'};
					next;
				}
				$res[0] = $res[1];

#++$o{'2'};
				if (($re_spos <=> $res[2]->[4] || $re_num <=> $res[2]->[6]) <= 0) {
					$res[1] = $re;
					next;
				}
				$res[1] = $res[2];

				if (($re_spos <=> $res[3]->[4] || $re_num <=> $res[3]->[6]) <= 0) {
#++$o{'3'};
					$res[2] = $re;
					next;
				}
				$res[2] = $res[3];

				if (($re_spos <=> $res[4]->[4] || $re_num <=> $res[4]->[6]) <= 0) {
#++$o{'3'};
					$res[3] = $re;
					next;
				}
				$res[3] = $res[4];

				if (($re_spos <=> $res[5]->[4] || $re_num <=> $res[5]->[6]) <= 0) {
#++$o{'4'};
					$res[4] = $re;
					next;
				}
				$res[4] = $res[5];

#++$o{'ugh'};
				$i = 6;
				$l = $#res;
				for (; $i < $l; ++$i) {
					last if (($re_spos <=> $res[$i]->[4] || $re_num <=> $res[$i]->[6]) <= 0);
					$res[$i-1] = $res[$i];
				}
#++$p{sprintf( "%2d", $i )};
				$res[$i-1] = $re;
			}

			next;
		}

# =cut

		##
		## End optimizations. You can comment them all out and this net
		## does all the work, just more slowly. If you do that, then
		## you also need to comment out the code below that deals with
		## the second entry in @res.
		##

#my $ni = 0;
		## First re always needs to be tweaked
#++$m;
#++$ni;
		pos($code) = $pos;
		unless ($code =~ /($re->[0])/gms) {
			if (@res == 2) {
				## If the only regexp left is the dummy, we're done.
				$rest = substr($code, $pos);
				push @$snippetlist_ref, $rest;
				last;
			}
			shift @res;
			@changed_res = ();
			$i = 0;
		} else {
			$re->[5] = $re_pos = pos($code);
			$re->[4] = $re_pos - length($1);
			@changed_res = ($re);
			$i = 1;
		}

		## If the optimizations above are in, the second one always
		## needs to be tweaked, too.
		$re = $res[$i];
#++$m;
#++$ni;
		pos($code) = $pos;
		unless ($code =~ /($re->[0])/gms) {
			if (@res == 2) {
				## If the only regexp left is the dummy, we're done.
				$rest = substr($code, $pos);
				push @$snippetlist_ref, $rest;
				last;
			}
			shift @res;
		} else {
			$re->[5] = $re_pos  = pos($code);
			$re->[4] = $re_spos = $re_pos - length $1;
			if (@changed_res &&
				($changed_res[0]->[4] <=> $re_spos ||
				$changed_res[0]->[6] <=> $re->[6]) > 0) {
				unshift @changed_res, $re;
			} else {
				$changed_res[$i] = $re;
			}
			++$i;
		}

		for (;; ++$i) {
			local $_ = $res[$i];
#++$m;
			last if $_->[4] >= $pos;
#++$ni;
#++$m;
			pos($code) = $pos;
			unless ($code =~ /($_->[0])/gms) {
				if (@res <= 2) {
					$rest = substr($code, $pos);
					push @$snippetlist_ref, $rest;
					last MAIN;
				}
				## If this regex is no longer needed, remove it by not pushing it
				## on to @changed_res. This means we need one less slot in @res.
				shift @res;
				redo;
			}

			$_->[5] = $re_pos  = pos($code);
			$_->[4] = $re_spos = $re_pos - length $1;

			## Insertion sort in to @changed_res
			$re_num = $_->[6];
			for ($j = $#changed_res; $j > -1; --$j) {
				last if (($changed_res[$j]->[4] <=> $re_spos ||
					$changed_res[$j]->[6] <=> $re_num) < 0);
				$changed_res[$j+1] = $changed_res[$j];
			}
			$changed_res[$j+1] = $_;
		}

		## Merge sort @changed_res and @res in to @res
		$j = 0;
		$l = $#res;
		for (@changed_res) {
			while (
				$i < $l &&
				($_->[4] <=> $res[$i]->[4] || $_->[6] <=> $res[$i]->[6]) > 0) {
				$res[$j++] = $res[$i++];
			}
			$res[$j++] = $_;
		}
# =cut
	}
};

############################################################################
####################### get_stylesheet #############################
############################################################################
sub get_stylesheet {
	my $STYLESHEET;

	my $bold = color('bold');
	my $underline = color('underline');
	my $reverse = color('reverse');
	my $reset = color('reset');
	my $red = color('red');
	my $green = color('green');
	my $yellow = color('yellow');
	my $blue = color('blue');
	my $magenta = color('magenta');
	my $cyan = color('cyan');

	$STYLESHEET = {
		'template'     => '%%code%%',
		'content-type' => 'text/html',
		'linenumbers'  => {
			'none'   => sub { return $_[0]; },
			'normal' => sub {
					# return a scalar made up of the joined lines including linenumbers
					my @lines = split(/\n/, $_[0]);
					my $nr = 0;
					my $lengthofnr = length($lines[-1]);
					my $format = qq{%${lengthofnr}u %s\n};
					join ('', map({$nr++; sprintf($format, $nr, $_)} @lines));
			},
			# is not defined for xterm output, therefore do nothing
			'linked' => sub { return $_[0]; },
		},
		'tags' => {
			'comment'                => {start => $blue},
			'doc comment'            => {start => "$bold$blue"},
			'string'                 => {start => $red},
			'esc string'             => {start => $magenta},
			'character'              => {start => $reset},
			'esc character'          => {start => $magenta},
			'numeric'                => {start => $red},
			'identifier'             => {start => $cyan},
			'predefined identifier'  => {start => $cyan},
			'type'                   => {start => $cyan},
			'predefined type'        => {start => $green},
			'reserved word'          => {start => $yellow},
			'library function'       => {start => $green},
			'include'                => {start => $green},
			'preprocessor'           => {start => $green},
			'braces'                 => {start => $reset},
			'symbol'                 => {start => $green},
			'function header'        => {start => "$bold$red"},
			'function header name'   => {start => "$bold$cyan"},
			'function header args'   => {start => $cyan},
			'regex'                  => {start => $magenta},
			'text'                   => {start => $red},
			# HTML
			'entity'                 => {start => $green},
			# MAKEFILE
			'assignment'             => {start => $green},
			'dependency line'        => {start => $cyan},
			'dependency target'      => {start => $blue},
			'dependency continuation'=> {start => $magenta},
			'continuation'           => {start => $magenta},
			'macro'                  => {start => $red},
			'int macro'              => {start => $red},
			'esc $$$'                => {start => $yellow},
			'separator'              => {start => $green},
			'line spec'              => {start => $cyan},
			'deletion'               => {start => $red},
			'insertion'              => {start => $blue},
			'modification'           => {start => $magenta},
		}
	};
	$STYLESHEET->{tags}{$_}{stop} = $reset for keys %{$STYLESHEET->{tags}};
	return $STYLESHEET;
};

#############################################################################
####################### get_database ################################
#############################################################################
sub get_database {

my %LANGUAGE;

# written by PP
$LANGUAGE{'plain'} = {
	'filename' => '',
	'regex'    => '',
	'patterns' => []
};

# taken from nedit
# modified by PP
$LANGUAGE{'ada'} = {
	'filename' => '(?i)\\.a(d[asb]?)?$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'Comments',
			'regex'      => '--.*?$',
			'style'      => 'comment',
			'childregex' => [],
		},
		{
			'name'       => 'String Literals',
			'regex'      => '".*?("|$)',
			'style'      => 'string',
			'childregex' => []
		},
		{
			'name'       => 'Character Literals',
			'regex'      => '\'.\'',
			'style'      => 'character',
			'childregex' => []
		},
		{
			'name'       => 'Ada Attributes',
			'regex'      => '\'[a-zA-Z][a-zA-Z_]+\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'Numeric Literals',
			'regex'      => '(((2|8|10|16)#[_0-9a-fA-F]*#)|[0-9.]+)',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'Withs Pragmas Use',
			'regex'      => '\\b(?i)((with|pragma|use)[ \\t\\n\\f\\r]+[a-zA-Z0-9_.]+;)+\\b',
			'style'      => 'include',
			'childregex' => []
		},
		{
			'name'       => 'Predefined Types',
			'regex'      => '\\b(?i)(boolean|character|count|duration|float|integer|long_float|long_integer|priority|short_float|short_integer|string)\\b',
			'style'      => 'predefined type',
			'childregex' => []
		},
		{
			'name'       => 'Predefined Subtypes',
			'regex'      => '\\b(?i)field|natural|number_base|positive|priority\\b',
			'style'      => 'predefined type',
			'childregex' => []
		},
		{
			'name'       => 'Reserved Words',
			'regex'      => '\\b(?i)(abort|abs|accept|access|and|array|at|begin|body|case|constant|declare|delay|delta|digits|do|else|elsif|end|entry|exception|exit|for|function|generic|goto|if|in|is|limited|loop|mod|new|not|null|of|or|others|out|package|pragma|private|procedure|raise|range|record|rem|renames|return|reverse|select|separate|subtype|task|terminate|then|type|use|when|while|with|xor)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'Ada 95 Only',
			'regex'      => '\\b(?i)(abstract|tagged|all|protected|aliased|requeue|until)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'Identifiers',
			'regex'      => '\\b[a-zA-Z][a-zA-Z0-9_]*\\b',
			'style'      => 'identifier',
			'childregex' => []
		},
		{
			'name'       => 'Dot All',
			'regex'      => '(?i)\\.all\\b',
			'style'      => 'predefined identifier',
			'childregex' => []
		}
	]
};
$LANGUAGE{'ada95'} = $LANGUAGE{'ada'};

# written by JA
$LANGUAGE{'awk'} = {
	'filename' => '(?i)\\.awk$',
	'regex'    => '^\\s*#\\s*![^\\s]*awk',
	'patterns' => [
		{
			'name'       => 'comment',
			'regex'      => '#.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#			'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'string',
			'regex'      => '\'\'|\'.*?([^\\\\](\\\\\\\\)*)\'|\'\\\\\\\\\'',
#			'regex'      => '\'\'|\'\\\\\\\\\'|\'[^\'\\\\]\'|\'[^\'].*?[^\\\\]\'',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'function header',
			'regex'      => 'function[\\t ]+([a-zA-Z0-9_]+)[\\t \\n]*(\\{|\\n)',
			'style'      => 'function header',
			'childregex' => [
				{
					'name'       => 'function coloring',
					'regex'      => '[\\t ]([a-zA-Z0-9_]+)',
					'style'      => 'function header name',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'regex matching I 1',
			'regex'      => '(\\b| )?(/)(\\\\/|[^/\\n])*(/[gimesox]*)',
			'style'      => 'regex',
			'childregex' => []
		},
		{
			'name'       => 'regex matching I 2',
			'regex'      => '(?:\\b| )(?:(?:m|q|qq)([!"#$%&\'*+-/]))(\\\\\\2|[^\\2\\n])*(\\2[gimesox]*)',
			'style'      => 'regex',
			'childregex' => []
		},
		{
			'name'       => 'regex matching II',
			'regex'      => '(?:\\b| )?(?:s([!"#$%&\'*+-/]))(?:\\\\\\2|[^\\2\\n])*?(\\2)[^(\\2)\\n]*?(\\2[gimesox]*)',
			'style'      => 'regex',
			'childregex' => []
		},
		{
			'name'       => 'translate',
			'regex'      => '(?:\\b| )(?:(?:tr|y)([^\w\s]))(?:\\\\\\2|[^\\2\\n])*?(\\2)[^(\\2)\\n]*?(\\2[gimesox]*)',
			'style'      => 'regex',
			'childregex' => []
		},
		{
			'name'       => 'keywords',
			'regex'      => '\\b(BEGIN|END|ARGC|ARGIND|ARGV|CONVFMT|ENVIRON|ERRNO|FIELDWIDTHS|FILENAME|FNR|FS|IGNORECASE|NF|NR|OFMT|OFS|ORS|RS|RT|RSTART|RLENGTH|SUBSEP)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'keywords 2',
			'regex'      => '\\b(if|while|do|for|in|break|continue|delete|exit|next|nextfile|function)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'library fns',
			'regex'      => '\\b(close|getline|print|printf|system|fflush|atan2|cos|exp|int|log|rand|sin|sqrt|srand|gensub|gsub|index|length|split|sprintf|sub|substr|tolower|toupper|systime|strftime)\\b',
			'style'      => 'library function',
			'childregex' => []
		},
		{
			'name'       => 'braces and parens',
			'regex'      => '[\\[\\]\\{\\}\\(\\)]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => '<< stuff',
			'regex'      => '<<\'([^\\n]*)\';.*?^\\2$',
			'style'      => 'text',
			'childregex' => []
		},
		{
			'name'       => '<< stuff',
			'regex'      => '<<([^\\n]*).*?^\\2$',
			'style'      => 'text',
			'childregex' => []
		}
	]
};

# taken from nedit
# modified by PP
$LANGUAGE{'c'} = {
	'filename' => '\\.[ch]$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'comment',
			'regex'      => '/\\*.*?\\*/',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'doc comment',
			'regex'      => '/\\*\\*.*?\\*/',
			'style'      => 'doc comment',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#			'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'preprocessor line',
			'regex'      => '^[ \\t]*#.*?$',
			'style'      => 'preprocessor',
			'childregex' => [
				{
					'name'       => 'string',
					'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#					'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
					'style'      => 'string',
					'childregex' => [
						{
							'name'       => 'esc character',
							'regex'      => '\\\\.',
							'style'      => 'esc character',
							'childregex' => []
						}
					]
				},
				{
					'name'       => '<files>',
					'regex'      => '<.*?>',
					'style'      => 'string',
					'childregex' => []
				},
				{
					'name'       => 'comment',
					'regex'      => '[^/]/\\*.*?\\*/',
					'style'      => 'comment',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'character constant',
			'regex'      => '\'(\\\\)?.\'',
			'style'      => 'character',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'numeric constant',
			'regex'      => '\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\b',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'storage keyword',
			'regex'      => '\\b(const|extern|auto|register|static|unsigned|signed|volatile|char|double|float|int|long|short|void|typedef|struct|union|enum)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'keyword',
			'regex'      => '\\b(return|goto|if|else|case|default|switch|break|continue|while|do|for|sizeof)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'braces',
			'regex'      => '[\\{\\}]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => 'symbols',
			'regex'      => '([\\*\\-\\+=:;%&\\|<>\\(\\)\\[\\]!])',
			'style'      => 'symbol',
			'childregex' => []
		},
		{
			'name'       => 'identifiers',
			'regex'      => '([a-zA-Z_][a-zA-Z_0-9]*)',
			'style'      => 'identifier',
			'childregex' => []
		}
	]
};

# taken from nedit
# modified by PP
$LANGUAGE{'c++'} = {
	'filename' => '\\.(c(c|pp|xx)|h(h|pp|xx)|C(C|PP|XX)?|H(H|PP|XX)?|i)$',
	'regex'    => '',
	'patterns' => [
			{
				'name'       => 'comment',
				'regex'      => '/\\*.*?\\*/',
				'style'      => 'comment',
				'childregex' => []
			},
			{
				'name'       => 'doc comment',
				'regex'      => '/\\*\\*.*?\\*/',
				'style'      => 'doc comment',
				'childregex' => []
			},
			{
				'name'       => 'cplus comment',
				'regex'      => '//.*?$',
				'style'      => 'comment',
				'childregex' => []
			},
			{
				'name'       => 'string',
				'regex'      => '""|"\\\\\\\\"|".*?([^\\\\](\\\\\\\\)*)"',
#				'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
				'style'      => 'string',
				'childregex' => [
					{
						'name'       => 'esc character',
						'regex'      => '\\\\.',
						'style'      => 'esc character',
						'childregex' => []
					}
				]
			},
			{
				'name'       => 'preprocessor line',
				'regex'      => '^[ \\t]*#.*?$',
				'style'      => 'preprocessor',
				'childregex' => [
					{
						'name'       => 'string',
						'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#						'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
						'style'      => 'string',
						'childregex' => [
							{
								'name'       => 'esc character',
								'regex'      => '\\\\.',
								'style'      => 'esc character',
								'childregex' => []
							}
						]
					},
					{
						'name'       => '<files>',
						'regex'      => '<.*?>',
						'style'      => 'string',
						'childregex' => []
					},
					{
						'name'       => 'comment',
						'regex'      => '[^/]/\\*.*?\\*/',
						'style'      => 'comment',
						'childregex' => []
					},
					{
						'name'       => 'cplus comment',
						'regex'      => '//.*?$',
						'style'      => 'comment',
						'childregex' => []
					}
				]
			},
			{
				'name'       => 'character constant',
				'regex'      => '\'(\\\\)?.\'',
				'style'      => 'character',
				'childregex' => [
					{
						'name'       => 'esc character',
						'regex'      => '\\\\.',
						'style'      => 'esc character',
						'childregex' => []
					}
				]
			},
			{
				'name'       => 'numeric constant',
				'regex'      => '\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\b',
				'style'      => 'numeric',
				'childregex' => []
			},
			{
				'name'       => 'storage keyword',
				'regex'      => '\\b(class|typename|typeid|template|friend|virtual|inline|explicit|operator|overload|public|private|protected|const|extern|auto|register|static|mutable|unsigned|signed|volatile|char|double|float|int|long|short|bool|wchar_t|void|typedef|struct|union|enum)\\b',
				'style'      => 'reserved word',
				'childregex' => [],
			},
			{
				'name'       => 'keyword',
				'regex'      => '\\b(new|delete|this|return|goto|if|else|case|default|switch|break|continue|while|do|for|catch|throw|sizeof|true|false|namespace|using|dynamic_cast|static_cast|reinterpret_cast)\\b',
				'style'      => 'reserved word',
				'childregex' => []
			},
			{
				'name'       => 'braces',
				'regex'      => '[\\{\\}]',
				'style'      => 'braces',
				'childregex' => []
			},
			{
				'name'       => 'symbols',
				'regex'      => '([\\*\\-\\+=:;%&\\|<>\\(\\)\\[\\]!])',
				'style'      => 'symbol',
				'childregex' => []
			},
			{
				'name'       => 'identifiers',
				'regex'      => '([a-zA-Z_][a-zA-Z_0-9]*)',
				'style'      => 'identifier',
				'childregex' => []
			}
	]
};
$LANGUAGE{'cc'} = $LANGUAGE{'c++'};
$LANGUAGE{'cpp'} = $LANGUAGE{'c++'};
$LANGUAGE{'cxx'} = $LANGUAGE{'c++'};

# written by VRS
$LANGUAGE{'gpasm'} = {
	'filename' => '(?i)\\.(asm|inc)$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'args',
			'regex'      => '^.*$',
			'style'      => 'symbol',
			'childregex' => [
				{
					'name'       => 'comment',
					'regex'      => ';.*?$',
					'style'      => 'comment',
					'childregex' => []
				},
				{
					'name'       => 'labels',
					'regex'      => '^[A-Za-z_][A-Za-z_0-9]*:?',
					'style'      => 'identifier',
					'childregex' => []
				},

				{
					'name'       => 'menonics',
					'regex'      => '^[ \t]+[A-Za-z_][A-Za-z_0-9]*',
					'style'      => 'reserved word',
					'childregex' => []
				},
				{
					'name'       => 'string',
					'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
					'style'      => 'string',
					'childregex' => [
						{
							'name'       => 'esc character',
							'regex'      => '\\\\.',
							'style'      => 'esc character',
							'childregex' => []
						}
					]
				}
			]
		}
	]
};

# written by JA
$LANGUAGE{'groff'} = {
	'filename' => '\\.groff$',
	'regex'    => '',
	'patterns' => [
			{
				'name'       => 'comment',
				'regex'      => '\\\\".*?$',
				'style'      => 'comment',
				'childregex' => []
			}
	]
};

# taken from nedit
# modified by PP
$LANGUAGE{'html'} = {
	'filename' => '(?i)\\.(html?|mhtml|php)$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'comment',
			'regex'      => '<!--.*?-->',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'entity',
			'regex'      => '\\&[-.a-zA-Z0-9#]*;?',
			'style'      => 'entity',
			'childregex' => []
		},
		{
			'name'       => 'tag',
			'regex'      => '<(/|!)?[-.a-zA-Z0-9]*.*?>',
			'style'      => 'predefined identifier',
			'childregex' => [
				{
					'name'       => 'double quote string',
					'regex'      => '".*?"',
					'style'      => 'string',
					'childregex' => []
				},
				{
					'name'       => 'single quote string',
					'regex'      => '\'.*?\'',
					'style'      => 'string',
					'childregex' => []
				},
				{
					'name'       => 'brackets',
					'regex'      => '[<>]',
					'style'      => 'braces',
					'childregex' => []
				},
				{
					'name'       => 'attribute',
					'regex'      => '[^\'" ]+(?=.)',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		}
	]
};

# Added May 17, 2002, Jim M.
$LANGUAGE{'xml'} = {
	'filename' => '(?i)\\.(xml|xps|xsl|axp|ppd)?$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'comment',
			'regex'      => '<!--.*?-->',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'entity',
			'regex'      => '\\&[-.a-zA-Z0-9#]*;?',
			'style'      => 'entity',
			'childregex' => []
		},
		{
			'name'        => 'tag',
			'regex'      => '<(/|!)?[-.a-zA-Z0-9]*.*?>',
			'style'      => 'predefined identifier',
			'childregex' => [
				{
					'name'      => 'double quote string',
					'regex'      => '".*?"',
					'style'      => 'string',
					'childregex' => []
				},
				{
					'name'      => 'single quote string',
					'regex'      => '\'.*?\'',
					'style'      => 'string',
					'childregex' => []
				},
				{
					'name'      => 'brackets',
					'regex'      => '[<>]',
					'style'      => 'braces',
					'childregex' => []
				},
				{
					'name'      => 'attribute',
					'regex'      => '[^\'" ]+(?=.)',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		}
	]
};

# taken from nedit
# modified by PP
$LANGUAGE{'java'} = {
	'filename' => '\\.java$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'doc comment',
			'regex'      => '/\\*\\*.*?\\*/',
			'style'      => 'doc comment',
			'childregex' => []
		},
		{
			'name'       => 'comment',
			'regex'      => '/\\*.*?\\*/',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'cplus comment',
			'regex'      => '//.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#			'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
			'style'      => 'string',
			'childregex' => [
				{
					'name'      => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'single quoted',
			'regex'      => '\'\'|\'.*?([^\\\\](\\\\\\\\)*)\'|\'\\\\\\\\\'',
#			'regex'      => '\'\'|\'\\\\\\\\\'|\'[^\'\\\\]\'|\'[^\'].*?[^\\\\]\'',
			'style'      => 'string',
			'childregex' => []
		},
		{
			'name'       => 'numeric constant',
			'regex'      => '\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\b',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'include',
			'regex'      => '\\b(import|package)\\b.*?$',
			'style'      => 'include',
			'childregex' => [
				{
					'name'      => 'esc character',
					'regex'      => '\\\\(.|\\n)',
					'style'      => 'esc character',
					'childregex' => []
				},
				{
					'name'      => 'comment',
					'regex'      => '[^/]/\\*.*?\\*/',
					'style'      => 'comment',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'storage keyword',
			'regex'      => '\\b(abstract|boolean|byte|char|class|double|extends|final|float|int|interface|long|native|private|protected|public|short|static|transient|synchronized|void|volatile|implements)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'keyword',
			'regex'      => '\\b(break|case|catch|continue|default|do|else|false|finally|for|if|instanceof|new|null|return|super|switch|this|throw|throws|true|try|while)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'braces and parens',
			'regex'      => '[\\{\\}\\(\\)\\[\\]]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => 'Identifiers',
			'regex'      => '\\b[a-zA-Z_][a-zA-Z0-9_]*\\b',
			'style'      => 'identifier',
			'childregex' => []
		},
		{
			'name'       => 'symbols',
			'regex'      => '([\\*\\-\\+=:;%&\\|<>!])',
			'style'      => 'symbol',
			'childregex' => []
		}
	]
};

# taken from nedit
# modified by PP
$LANGUAGE{'javascript'} = {
	'filename' => '(?i)\\.js$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'comment',
			'regex'      => '/\\*.*?\\*/',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'cplus comment',
			'regex'      => '//.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'numeric constant',
			'regex'      => '\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\b',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'events',
			'regex'      => '\\b(onAbort|onBlur|onClick|onChange|onDblClick|onDragDrop|onError|onFocus|onKeyDown|onKeyPress|onLoad|onMouseDown|onMouseMove|onMouseOut|onMouseOver|onMouseUp|onMove|onResize|onSelect|onSubmit|onUnload)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'braces',
			'regex'      => '[\\{\\}]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => 'statements',
			'regex'      => '\\b(break|continue|else|for|if|in|new|return|this|typeof|var|while|with)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'function',
			'regex'      => 'function[\\t ]+([a-zA-Z0-9_]+)[\\t \\(]+.*?[\\n{]',
			'style'      => 'function header',
			'childregex' => [
				{
					'name'      => 'function args',
					'regex'      => '\\(.*?\\)',
					'style'      => 'function header args',
					'childregex' => []
				},
				{
					'name'      => 'function name',
					'regex'      => '[\\t ][a-zA-Z0-9_]+',
					'style'      => 'function header name',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'built in object type',
			'regex'      => '\\b(anchor|Applet|Area|Array|button|checkbox|Date|document|elements|FileUpload|form|frame|Function|hidden|history|Image|link|location|Math|navigator|Option|password|Plugin|radio|reset|select|string|submit|text|textarea|window)\\b',
			'style'      => 'predefined type',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '".*?("|$)',
			'style'      => 'string',
			'childregex' => [
				{
					'name'      => 'colors',
					'regex'      => '(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|#008000|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen|#[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9])',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'string',
			'regex'      => '\'.*?(\'|$)',
			'style'      => 'string',
			'childregex' => [
				{
					'name'      => 'colors',
					'regex'      => '(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|#008000|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen|#[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9])',
					'style'      => 'identifier',
					'childregex' => [],
				}
			]
		},
		{
			'name'       => 'event capturing',
			'regex'      => '\\b(captureEvents|releaseEvents|routeEvent|handleEvent)\\b.*?(\\)|$)',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'predefined methods',
			'regex'      => '\\b(abs|acos|alert|anchor|asin|atan|atan2|back|big|blink|blur|bold|ceil|charAt|clear|clearTimeout|click|close|confirm|cos|escape|eval|exp|fixed|floor|focus|fontcolor|fontsize|forward|getDate|getDay|getHours|getMinutes|getMonth|getSeconds|getTime|getTimezoneOffset|getYear|go|indexOf|isNaN|italics|javaEnabled|join|lastIndexOf|link|log|max|min|open|parse|parseFloat|parseInt|pow|prompt|random|reload|replace|reset|reverse|round|scroll|select|setDate|setHours|setMinutes|setMonth|setSeconds|setTimeout|setTime|setYear|sin|small|sort|split|sqrt|strike|sub|submit|substring|sup|taint|tan|toGMTString|toLocaleString|toLowerCase|toString|toUpperCase|unescape|untaint|UTC|write|writeln)\\b',
			'style'      => 'library function',
			'childregex' => []
		},
		{
			'name'       => 'properties',
			'regex'      => '\\b(action|alinkColor|anchors|appCodeName|appName|appVersion|bgColor|border|checked|complete|cookie|defaultChecked|defaultSelected|defaultStatus|defaultValue|description|E|elements|enabledPlugin|encoding|fgColor|filename|forms|frames|hash|height|host|hostname|href|hspace|index|lastModified|length|linkColor|links|LN2|LN10|LOG2E|LOG10E|lowsrc|method|name|opener|options|parent|pathname|PI|port|protocol|prototype|referrer|search|selected|selectedIndex|self|SQRT1_2|SQRT2|src|status|target|text|title|top|type|URL|userAgent|value|vlinkColor|vspace|width|window)\\b',
			'style'      => 'predefined identifier',
			'childregex' => []
		},
		{
			'name'       => 'operators',
			'regex'      => '([=;->/&|])',
			'style'      => 'symbol',
			'childregex' => []
		}
	]
};
$LANGUAGE{'js'} = $LANGUAGE{'javascript'};

# written by Andreas Krennmair
# extremely incomplete

$LANGUAGE{'lisp'} = {
	'filename' => '\\.(lsp|l)$',
	'regex' => '',
	'patterns' => [
		{
			'name'       => 'parens',
			'regex'      => '[()]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => 'comment',
			'regex'      => ';.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '".*?("|$)',
			'style'      => 'string',
			'childregex' => []
		},
		{
			'name'       => 'keywords',
			'regex'      => '\\b(defun |xyz)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'numeric constant',
			'regex'      => '(#\([0-9]+ [0-9]+\)|[0-9]+)',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'identifiers',
			'regex'      => '([-a-zA-Z]+)',
			'style'      => 'identifier',
			'childregex' => []
		}
	]
};

# written by JA
$LANGUAGE{'m4'} = {
	'filename' => '\\.m4$',
	'regex'    => '',
	'patterns' => [
		{
			'regex'        => 'dnl.*?$',
			'style'        => 'doc comment',
			'childregex' => []
		},
		{
			'regex'        => '#.*?$',
			'style'        => 'comment',
			'childregex' => []
		},
		{
			'regex'        => '\\b(define|undefine|defn|pushdef|popdef|indir|builtin|changequote|changecom|changeword|m4wrap|m4exit|include|sinclude|divert|undivert|divnum|cleardiv|shift|dumpdef|traceon|traceoff|debugfile|debugmode|len|index|regexp|substr|translit|patsubst|format|incr|decr|syscmd|esyscmd|sysval|maketemp|errprint)\\b',
			'style'        => 'reserved word',
			'childregex' => []
		},
		{
			'regex'        => '\\b(ifdef|ifelse|loops)\\b',
			'style'        => 'reserved word',
			'childregex' => [
				{
				'regex'      => '[$]\\$?({[^}]*}|[^a-zA-Z0-9_/\\t\\n\\.,\\\\[\\\\{\\\\(]|[0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)?',
				'style'      => 'identifier',
				'childregex' => []
				}
			]
		}
	]
};

# taken from nedit
# modified by PP
$LANGUAGE{'make'} = {
	'filename' => '[Mm]akefile.*',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'Comment',
			'regex'      => '#.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'Assignment',
			'regex'      => '^( *| [ \\t]*)[A-Za-z0-9_+]*[ \\t]*(\\+|:)?=',
			'style'      => 'assignment',
			'childregex' => []
		},
		{
			'name'       => 'Dependency Line',
			'regex'      => '^ *([A-Za-z0-9./$(){} _%+-]|\\n)*::?',
			'style'      => 'dependency line',
			'childregex' => [
				{
					'name'      => 'Dependency Target',
					'regex'      => '[A-Za-z0-9./$(){} _%+-]+',
					'style'      => 'dependency target',
					'childregex' => []
				},
				{
					'name'      => 'Dependency Continuation',
					'regex'      => '\\\\\\n',
					'style'      => 'dependency continuation',
					'childregex' => []
				},
				{
					'name'      => 'comment',
					'regex'      => '#.*?$',
					'style'      => 'comment',
					'childregex' => []
				},
				{
					'name'      => 'macro',
					'regex'      => '\\$([A-Za-z0-9_]|\\([^)]*\\)|{[^}]*})',
					'style'      => 'macro',
					'childregex' => []
				},
				{
					'name'      => 'int macro',
					'regex'      => '\\$([<@*?%]|\\$@)',
					'style'      => 'int macro',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'Continuation',
			'regex'      => '\\\\$',
			'style'      => 'continuation',
			'childregex' => []
		},
		{
			'name'       => 'Macro',
			'regex'      => '\\$([A-Za-z0-9_]|\\([^)]*\\)|{[^}]*})',
			'style'      => 'macro',
			'childregex' => []
		},
		{
			'name'       => 'Internal Macro',
			'regex'      => '\\$([<@*?%]|\\$@)',
			'style'      => 'int macro',
			'childregex' => []
		},
		{
			'name'       => 'Escaped $$$',
			'regex'      => '\\$\\$',
			'style'      => 'esc $$$',
			'childregex' => []
		},
		{
			'name'       => 'Include',
			'regex'      => '^include[ \\t]',
			'style'      => 'include',
			'childregex' => []
		}
	]
};
$LANGUAGE{'makefile'} = $LANGUAGE{'make'};

# taken from nedit
# modified by PP
$LANGUAGE{'pas'} = {
	'filename' => '(?i)\\.p(as)?$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'comment1 (*    *)',
			'regex'      => '\\(\\*.*?\\*\\)',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'comment2 {    }',
			'regex'      => '\\{.*?\\}',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '\'.*?(\'|$)',
			'style'      => 'string',
			'childregex' => []
		},
		{
			'name'       => 'preprocessor line',
			'regex'      => '^[ \\t]*#.*?$',
			'style'      => 'preprocessor',
			'childregex' => [
				{
					'name'      => 'comment1 (*    *)',
					'regex'      => '\\(\\*.*?\\*\\)',
					'style'      => 'comment',
					'childregex' => []
				},
				{
					'name'      => 'comment2 {    }',
					'regex'      => '\\{.*?\\}',
					'style'      => 'comment',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'character constant',
			'regex'      => '\'.\'',
			'style'      => 'character',
			'childregex' => []
		},
		{
			'name'       => 'numeric constant',
			'regex'      => '\\b((0(x|X)[0-9a-fA-F]*)|[0-9.]+((e|E)(\\+|-)?)?[0-9]*)(L|l|UL|ul|u|U|F|f)?\\b',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'storage and ops',
			'regex'      => '\\b(?i)(and|array|const|div|export|file|function|import|in|label|mod|module|nil|not|only|or|packed|pow|pragma|procedure|program|protected|qualified|record|restricted|set|type|var)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'keywords',
			'regex'      => '\\b(?i)(begin|case|do|downto|else|end|for|goto|if|of|otherwise|repeat|then|to|until|while|with)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'sumbols',
			'regex'      => '([\\*\\-\\+=:;<>\\(\\)\\[\\]!]|[^/]/[^/])',
			'style'      => 'symbol',
			'childregex' => []
		},
		{
			'name'       => 'identifiers',
			'regex'      => '([a-zA-Z_][a-zA-Z_0-9.^]*[a-zA-Z_0-9]|[a-zA-Z_][a-zA-Z_0-9]*)',
			'style'      => 'identifier',
			'childregex' => [
				{
					'regex'      => '(\\.|\\^)+',
					'style'      => 'symbol',
					'childregex' => []
				}
			]
		}
	],
};
$LANGUAGE{'pascal'} = $LANGUAGE{'pas'};

# taken from nedit
# modified by PP
# modified by BS
# modified by JD
# modified by JP
$LANGUAGE{'perl'} = {
	'filename' => '(?i)\\.p([lm5]|od)$',
	'regex'    => '^\\s*#\\s*!([^\\s]*\\b|.*env\\s+)perl',
	'patterns' => [
		{
			'name'       => 'comment',
			'regex'      => '(?:#.*?(?:\r?\n\s*)+)+',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'variables',
			'regex'      => '[\\$@%]\\$?(?:{[^}]*}|[^a-zA-Z0-9_/\\t\\n\\.,\\\\[\\\\{\\\\(]|[0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)?',
			'style'      => 'identifier',
			'childregex' => []
		},
		{
			'name'       => '"" string',
			'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#			'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				},
				{
					'name'       => 'variables',
					'regex'      => '[\\$@%]\\$?(?:{[^}]*}|[^a-zA-Z0-9_/\\t\\n\\.,\\\\[\\\\{\\\\(]|[0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)?',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		},
		{
			'name'       => '\'\' string',
			'regex'      => '\'\'|\'.*?([^\\\\](\\\\\\\\)*)\'|\'\\\\\\\\\'',
#			'regex'      => '\'\'|\'\\\\\\\\\'|\'[^\'\\\\]\'|\'[^\'].*?[^\\\\]\'',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'more strings - q// qw//',
			'regex'      => '(?:\\b| )(?:q|qw)([^\w\s])(?:\\\\\\2|[^\\2\\n])*\\2',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'more strings - qq// qx//',
			'regex'      => '(?:\\b| )(?:qq|qx)([^\w\s])(?:\\\\\\2|[^\\2\\n])*\\2',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				},
				{
					'name'       => 'variables',
					'regex'      => '[\\$@%]\\$?(?:{[^}]*}|[^a-zA-Z0-9_/\\t\\n\\.,\\\\[\\\\{\\\\(]|[0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)?',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'subroutine header',
			'regex'      => 'sub[\\t ]+(?:[a-zA-Z0-9_]+)[\\t \\n]*(?:\\{|\\(|\\n)',
			'style'      => 'function header',
			'childregex' => [
				{
					'name'      => 'subroutine header coloring',
					'regex'      => '[\\t ][a-zA-Z0-9_]+',
					'style'      => 'function header name',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'regex matching I',
			'regex'      => '(?:\\b| )?(?:/(?:\\\\/|[^/\\n])*(?:/[gimesox]*)|s([^\w\s])(?:\\\\\\2|[^\\2\\n])*?(\\2)[^(\\2)\\n]*?(\\2[gimesox]*))',
			'style'      => 'regex',
			'childregex' => []
		},
		{
			'name'       => 'regex matching II',
			'regex'      => '(?:\\b| )(?:m|qq?|tr|y)([^\w\s])(?:\\\\\\2|[^\\2\\n])*(?:\\2[gimesox]*)',
			'style'      => 'regex',
			'childregex' => []
		},
		{
			'name'       => 'keywords',
			'regex'      => '\\b(my|local|new|if|until|while|elsif|else|eval|unless|for|foreach|continue|exit|die|last|goto|next|redo|return|local|exec|do|use|require|package|eval|BEGIN|END|eq|ne|not|\\|\\||\\&\\&|and|or)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'library functions',
			'regex'      => '\\b(?:a(?:bs|ccept|larm|tan2)|b(?:ind|inmode|less)|c(?:aller|hdir|hmod|homp|hop|hr|hroot|hown|losedir|lose|onnect|os|rypt)|d(?:bmclose|bmopen|efined|elete|ie|ump)|e(?:ach|nd(?:grent|hostent|netent|protoent|pwent|servent)|of|xec|xists|xp)|f(?:ctnl|ileno|lock|ork|ormat|ormline)|g(?:et(?:c|grent|grgid|grnam|hostbyaddr|hostbyname|hostent|login|netbyaddr|netbyname|netent|peername|pgrp|ppid|priority|protobyname|protobynumber|protoent|pwent|pwnam|pwuid|servbyname|servbyport|servent|sockname|sockopt)|lob|mtime|rep)|hex|i(?:mport|ndex|nt|octl)|join|keys|kill|l(?:cfirst|c|ength|ink|isten|og|ocaltime|stat)|m(?:ap|kdir|sgctl|sgget|sgrcv)|no|o(?:ct|pendir|pen|rd)|p(?:ack|ipe|op|os|rintf|rint|ush)|quotemeta|r(?:and|eaddir|ead|eadlink|ecv|ef|ename|eset|everse|ewinddir|index|mdir)|s(?:calar|eekdir|eek|elect|emctl|emget|emop|end|et(?:grent|hostent|netent|pgrp|priority|protoent|pwent|sockopt)|hift|hmctl|hmget|hmread|hmwrite|hutdown|in|leep|ocket|ocketpair|ort|plice|plit|printf|qrt|rand|tat|tudy|ubstr|ymlink|yscall|ysopen|ysread|ystem|yswrite)|t(?:elldir|ell|ie|ied|ime|imes|runcate)|u(?:c|cfirst|mask|ndef|nlink|npack|nshift|ntie|time)|values|vec|w(?:ait|aitpid|antarray|arn|rite)|qw|-[rwxoRWXOezsfdlpSbctugkTBMAC])\\b',
			'style'      => 'library function',
			'childregex' => []
		},
		{
			'name'       => 'braces, parens and brakets',
			'regex'      => '[\\[\\]\\{\\}\\(\\)]',
			'style'      => 'braces',
			'childregex' => []
		},
			{
			'name'       => '<< stuff',
			'regex'      => '<<(?:("|\')([^\\n]*)\\2|\\w*).*?^\\3$',
			'style'      => 'text',
			'childregex' => []
		},
		{
			'name'       => 'POD',
			'regex'      => '^=.*?^(?:=cut|\\Z)',
			'style'      => 'doc comment',
			'childregex' => []
		}
	]
};

# Thanks to Matt Giwer <jull43 AT ij.net>
$LANGUAGE{'pov'} = {
	'filename' => '(?i)\\.pov$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'doc comment',
			'regex'      => '/\\*\\*.*?\\*/',
			'style'      => 'doc comment',
			'childregex' => []
		},
		{
			'name'       => 'comment',
			'regex'      => '/\\*.*?\\*/',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'cplus comment',
			'regex'      => '//.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#			'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'preprocessor line',
			'regex'      => '^[ \\t]*#.*?$',
			'style'      => 'preprocessor',
			'childregex' => [
				{
					'name'       => 'string',
					'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
#					'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
					'style'      => 'string',
					'childregex' => [
						{
							'name'       => 'esc character',
							'regex'      => '\\\\.',
							'style'      => 'esc character',
							'childregex' => []
						}
					]
				},
				{
					'name'       => '<files>',
					'regex'      => '<.*?>',
					'style'      => 'string',
					'childregex' => []
				},
				{
					'name'       => 'comment',
					'regex'      => '[^/]/\\*.*?\\*/',
					'style'      => 'comment',
					'childregex' => []
				},
				{
					'name'       => 'cplus comment',
					'regex'      => '//.*?$',
					'style'      => 'comment',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'character constant',
			'regex'      => '\'(\\\\)?.\'',
			'style'      => 'character',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'numeric constant',
			'regex'      => '\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\b',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'keyword',
			'regex'      => '\\b(abs|absorption|acos|acosh|adaptive|adc_bailout|agate|agate_turb|all|alpha|ambient|ambient_light|angle|aperture|append|arc_angle|area_light|array|asc|asin|asinh|assumed_gamma|atan|atan2|atanh|average|background|bezier_spline|bicubic_patch|black_hole|blob|blue|blur_samples|bounded_by|box|boxed|bozo|break|brick|brick_size|brightness|brilliance|bumps|bump_map|bump_size|camera|case|caustics|ceil|checker|chr|clipped_by|clock|clock_delta|color|color_map|colour|colour_map|component|composite|concat|cone|confidence|conic_sweep|control0|control1|cos|cosh|count|crackle|crand|cube|cubic|cubic_spline|cubic_wave|cylinder|cylindrical|debug|declare|default|defined|degrees|density|density_file|density_map|dents|difference|diffuse|dimensions|dimension_size|direction|disc|distance|distance_maximum|div|eccentricity|else|emission|end|error|error_bound|exp|extinction|fade_distance|fade_power|falloff|falloff_angle|false|fclose|file_exists|filter|finish|fisheye|flatness|flip|floor|focal_point|fog|fog_alt|fog_offset|fog_type|fopen|frequency|gif|global_settings|gradient|granite|gray_threshold|green|height_field|hexagon|hf_gray_16|hierarchy|hollow|hypercomplex|if|ifdef|iff|ifndef|image_map|include|int|interior|interpolate|intersection|intervals|inverse|ior|irid|irid_wavelength|jitter|julia_fractal|lambda|lathe|leopard|light_source|linear_spline|linear_sweep|local|location|log|looks_like|look_at|low_error_factor|macro|mandel|map_type|marble|material|material_map|matrix|max|max_intersections|max_iteration|max_trace_level|media|media_attenuation|media_interaction|merge|mesh|metallic|min|minimum_reuse|mod|mortar|nearest_count|no|normal|normal_map|no_shadow|number_of_waves|object|octaves|off|offset|omega|omnimax|on|once|onion|open|orthographic|panoramic|perspective|pgm|phase|phong|phong_size|pi|pigment|pigment_map|planar|plane|png|point_at|poly|polygon|poly_wave|pot|pow|ppm|precision|prism|pwr|quadratic_spline|quadric|quartic|quaternion|quick_color|quick_colour|quilted|radial|radians|radiosity|radius|rainbow|ramp_wave|rand|range|ratio|read|reciprocal|recursion_limit|red|reflection|reflection_exponent|refraction|render|repeat|rgb|rgbf|rgbft|rgbt|right|ripples|rotate|roughness|samples|scale|scallop_wave|scattering|seed|shadowless|sin|sine_wave|sinh|sky|sky_sphere|slice|slope_map|smooth|smooth_triangle|sor|specular|sphere|spherical|spiral1|spiral2|spotlight|spotted|sqr|sqrt|statistics|str|strcmp|strength|strlen|strlwr|strupr|sturm|substr|superellipsoid|switch|sys|t|tan|tanh|text|texture|texture_map|tga|thickness|threshold|tightness|tile2|tiles|torus|track|transform|translate|transmit|triangle|triangle_wave|true|ttf|turbulence|turb_depth|type|u|ultra_wide_angle|undef|union|up|use_color|use_colour|use_index|u_steps|v|val|variance|vaxis_rotate|vcross|vdot|version|vlength|vnormalize|vrotate|v_steps|warning|warp|water_level|waves|while|width|wood|wrinkles|write|x|y|yes|z)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'braces',
			'regex'      => '[\\{\\}]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => 'symbols',
			'regex'      => '([\\*\\-\\+=:;%&\\|<>\\(\\)\\[\\]!])',
			'style'      => 'symbol',
			'childregex' => []
		},
		{
			'name'       => 'identifiers',
			'regex'      => '([a-zA-Z_][a-zA-Z_0-9]*)',
			'style'      => 'identifier',
			'childregex' => []
		}
	]
};
$LANGUAGE{'povray'} = $LANGUAGE{'pov'};

# by Tom Good
$LANGUAGE{'python'} = {
	'filename' => '(?i)\\.py$',
	'regex'    => '^\\s*#\\s*!([^\\s]*\\b|.*env\\s+)*python',
	'patterns' => [
		{
			'name'       => 'python comment',
			'regex'      => '#.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'single quote string',
			'regex'      => '\'.*?\'',
			'style'      => 'string',
			'childregex' => []
		},

		{
			'name'       => 'string',
			'regex'      => '""|"\\\\\\\\"|".*?([^\\\\](\\\\\\\\)*)"',
			'regex'      => '""|".*?([^\\\\](\\\\\\\\)*)"|"\\\\\\\\"',
			'regex'      => '""|"\\\\\\\\"|"[^"\\\\]"|"[^"].*?[^\\\\]"',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'character constant',
			'regex'      => '\'(\\\\)?.\'',
			'style'      => 'character',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\.',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'numeric constant',
			'regex'      => '\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)(L|l|UL|ul|u|U|F|f)?\\b',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'keyword',
			'regex'      => '\\b(and|assert|break|class|continue|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'braces',
			'regex'      => '[\\{\\}]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => 'symbols',
			'regex'      => '([\\*\\-\\+=:;%&\\|<>\\(\\)\\[\\]!])',
			'style'      => 'symbol',
			'childregex' => []
		},
		{
			'name'       => 'identifiers',
			'regex'      => '([a-zA-Z_][a-zA-Z_0-9]*)',
			'style'      => 'identifier',
			'childregex' => []
		},
		{
			'name'       => 'function',
			'regex'      => '[\\t ]*def[\\t ]+([a-zA-Z0-9_]+)[\\t \\(]+.*?[\\n{]',
			'style'      => 'function header',
			'childregex' => [
				{
					'name'       => 'function args',
					'regex'      => '\\(.*?\\)',
					'style'      => 'function header args',
					'childregex' => []
				},
				{
					'name'       => 'function name',
					'regex'      => '[\\t ][a-zA-Z0-9_]+',
					'style'      => 'function header name',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'library functions',
			'regex'      => '\\b(__import__|abs|apply|buffer|callable|chr|cmp|coerce|compile|complex|delatter|dir|divmod|eval|execfile|filter|float|getattr|globals|hasattr|hash|hex|id|input|int|intern|isinstance|issubclass|len|list|locals|long|map|max|min|oct|open|ord|pow|range|raw_input|reduce|reload|repr|round|setattr|slice|str|tuple|type|unichr|unicode|vars|xrange|zip)\\b',
			'style'      => 'library function',
			'childregex' => []
		},
	]
};

# by Joshua Swink <jswink AT pacbell.net>
$LANGUAGE{'ruby'} = {
	'filename' => '\\.rb$',
	'regex'    => '^\\s*#\\s*!([^\\s]*\\b|.*env\\s+)ruby\\b',
	'patterns' => [
		{
			'name'       => 'comment',
			'regex'      => '(?:#.*?(?:\r?\n\s*)+)+',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'predefined variables',
			'regex'      => '(?:\\$(?:[!@&`\'+\\d~=/\\\\,;.<>_*\\$?:"]|DEBUG|FILENAME|LOAD_PATH|stdin|stdout|stderr|VERBOSE|-[0adFiIlpv])|\\b(?:TRUE|FALSE|NIL|STDIN|STDOUT|STDERR|ENV|ARGF|ARGV|DATA|RUBY_VERSION|RUBY_RELEASE_DATE|RUBY_PLATFORM)\\b)',
			'style'      => 'predefined identifier',
			'childregex' => []
		},
		{
			'name'       => 'variables',
			'regex'      => '[\\$@](?:{[^}]*}|[^\\w/\\t\\n\\.,\\\\[\\\\{\\\\(]|[0-9]+|[a-zA-Z_][\\w.]*)?',
			'style'      => 'identifier',
			'childregex' => []
		},
		{
			'name'       => '"" string',
			'regex'      => '""|"(?:\\\\\\\\)+"|".*?(?:[^\\\\](?:\\\\\\\\)*)"|%[Qwx]?([^\\w\\[\\](){}<>])\\2|%[Qwx]?([^\\w\\[\\](){}<>]).*?(?:[^\\\\](?:\\\\\\\\)*)\\3|%[Qwx]?([^\\w\\[\\](){}<>])\\\\\\\\\\4|%[Qwx]?\\[\\]|%[Qwx]?\\[.*?([^\\\\](\\\\\\\\)*)\\]|%[Qwx]?\\[\\\\\\\\\\]|%[Qwx]?\\{\\}|%[Qwx]?\\{.*?([^\\\\](\\\\\\\\)*)\\}|%[Qwx]?\\{\\\\\\\\\\}|%[Qwx]?\\(\\)|%[Qwx]?\\(.*?([^\\\\](\\\\\\\\)*)\\)|%[Qwx]?\\(\\\\\\\\\\)|%[Qwx]?<>|%[Qwx]?<.*?([^\\\\](\\\\\\\\)*)>|%[Qwx]?<\\\\\\\\>',

			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '\\\\(?:x[\\da-fA-F]{2}|\d\d\d|c.|M-\\\\C-.|M-.|C-.|.)',
					'style'      => 'esc character',
					'childregex' => []
				},
				{
					'name'       => 'string expression',
					'regex'      => '#[\\$\\@][a-zA-Z_][\\w.]*|#\\{[\\$\\@]?[^\\}]*\\}',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		},
		{
			'name'       => '\'\' string',
			'regex'      => '\'\'|\'(?:\\\\\\\\)+\'|\'.*?(?:[^\\\\](?:\\\\\\\\)*)\'|%q([^\\w\\[\\](){}<>])\\2|%q([^\\w\\[\\](){}<>]).*?(?:[^\\\\](?:\\\\\\\\)*)\\3|%q([^\\w\\[\\](){}<>])\\\\\\\\\\4|%q\\[\\]|%q\\[.*?([^\\\\](\\\\\\\\)*)\\]|%q\\[\\\\\\\\\\]|%q\\{\\}|%q\\{.*?([^\\\\](\\\\\\\\)*)\\}|%q\\{\\\\\\\\\\}|%q\\(\\)|%q\\(.*?([^\\\\](\\\\\\\\)*)\\)|%q\\(\\\\\\\\\\)|%q<>|%q<.*?([^\\\\](\\\\\\\\)*)>|%q<\\\\\\\\>',
			'style'      => 'string',
			'childregex' => [
				{
					'name'       => 'esc character',
					'regex'      => '(?:\\\\\'|\\\\\\\\)',
					'style'      => 'esc character',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'subroutine header',
			'regex'      => 'def[\\t ]+\\w[\\w.]*(?:\\([^)]*\\))?',
			'style'      => 'function header',
			'childregex' => [
				{
					'name'       => 'arg list',
					'regex'      => '\\(.*\\)',
					'style'      => 'function header args',
					'childregex' => [
						{
							'name'       => 'arg list parens',
							'regex'      => '[\\(\\)]',
							'style'      => 'symbol',
							'childregex' => []
						}
					]
				},
				{
					'name'       => 'subroutine header',
					'regex'      => '[\\t ]\w+',
					'style'      => 'function header name',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'class header',
			'regex'      => 'class[\\t ]+\\w+(?:\\s*<\\s*\\w+)?',
			'style'      => 'function header',
			'childregex' => [
				{
					'name'       => 'class ancestor',
					'regex'      => '<\\s*\\w+',
					'style'      => 'include',
					'childregex' => [
						{
							'name'       => 'inheritance doohickey',
							'regex'      => '<',
							'style'      => 'symbol',
							'childregex' => []
						}
					]
				},
				{
					'name'       => 'class main',
					'regex'      => '[\\t ]\\w+',
					'style'      => 'type',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'regex matching 0',
			'regex'      => '(?:%r([^\\w\\[\\](){}<>])\\2|%r([^\\w\\[\\](){}<>]).*?(?:[^\\\\](?:\\\\\\\\)*)\\3|%r([^\\w\\[\\](){}<>])\\\\\\\\\\4|%r\\[\\]|%r\\[.*?([^\\\\](\\\\\\\\)*)\\]|%r\\[\\\\\\\\\\]|%r\\{\\}|%r\\{.*?([^\\\\](\\\\\\\\)*)\\}|%r\\{\\\\\\\\\\}|%r\\(\\)|%r\\(.*?([^\\\\](\\\\\\\\)*)\\)|%r\\(\\\\\\\\\\)|%r<>|%r<.*?([^\\\\](\\\\\\\\)*)>|%r<\\\\\\\\>)[ixpno]*',
			'style'      => 'regex',
			'childregex' => [
				{
					'name'       => 'string expression',
					'regex'      => '#[\\$\\@][a-zA-Z_][\\w.]*|#\\{[\\$\\@]?[a-zA-Z_][^\\}]*\\}',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'regex matching I',
			'regex'      => '(?:\\b| )?(?:/(?:\\\\/|[^/\\n])*(?:/[ixpno]*))',
			'style'      => 'regex',
			'childregex' => [
				{
					'name'       => 'string expression',
					'regex'      => '#[\\$\\@][a-zA-Z_][\\w.]*|#\\{[\\$\\@]?[a-zA-Z_][^\\}]*\\}',
					'style'      => 'identifier',
					'childregex' => []
				}
			]
		},
		{
			'name'       => 'reserved words',
			'regex'      => '\\b(BEGIN|class|ensure|nil|self|when|END|def|false|not|super|while|alias|defined|for|or|then|yield|and|do|if|redo|true|begin|else|in|rescue|undef|break|elsif|module|retry|unless|case|end|next|return|until)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'kernel module methods',
			'regex',     => '\\b(Array|Float|Integer|String|at_exit|autoload|binding|caller|catch|chop|chomp|chomp!|eval|exec|exit|fail|fork|format|gets|global_variables|gsub|iterator|lambda|load|local_variables|loop|open|p|print|printf|proc|putc|puts|raise|rand|readline|readlines|require|select|sleep|split|sprintf|srand|sub|syscall|system|test|trace_var|trap|untrace_var)\\b',
			'style'      => 'library function',
			'childregex' => []
		},
		{
			'name'       => 'braces, parens and brakets',
			'regex'      => '[\\[\\]\\{\\}\\(\\)]',
			'style'      => 'braces',
			'childregex' => []
		},
		{
			'name'       => '<< stuff',
			'regex'      => '<<(?:("|\')([^\\n]*)\\2|\\w*).*?^\\3$',
			'style'      => 'text',
			'childregex' => []
		},
		{
			'name'       => 'symbols',
			'regex'      => '(?:[:*-+<>=^!,/]+|\.\.+)',
			'style'      => 'symbol',
			'childregex' => []
		},
		{
			'name'       => 'numbers',
			'regex'      => '\d[\d.]*',
			'style'      => 'numeric',
			'childregex' => []
		},
		{
			'name'       => 'embedded documentation',
			'regex'      => '^=.*?^(?:=end|\\Z)',
			'style'      => 'doc comment',
			'childregex' => []
		}
	]
};

# taken from nedit
# modified by PP
# very inclomplete!
$LANGUAGE{'sql'} = {
	'filename' => '(?i)\\.sql$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'keywords I',
			'regex'      => '(?i)(,|%|<|>|:=|=|\\(|\\)|\\bselect|on|from|order by|desc|where|and|or|not|null|true|false)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'comment I',
			'regex'      => '--.*?$',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'comment II',
			'regex'      => '/\\*.*?\\*/',
			'style'      => 'comment',
			'childregex' => []
		},
		{
			'name'       => 'string',
			'regex'      => '\'\'|\'.*?([^\\\\](\\\\\\\\)*)\'|\'\\\\\\\\\'',
#			'regex'      => '(\'\'|\'[^\'\\\\]\'|\'[^\'].*?[^\\\\]\')',
			'style'      => 'string',
			'childregex' => []
		},
		{
			'name'       => 'keywords II',
			'regex'      => '(?i)end if;|\\b(create|replace|begin|end|function|return|fetch|open|close|into|is|in|when|others|grant|on|to|exception|show|set|out|pragma|as|package)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'keywords III',
			'regex'      => '(?i)\\balter\\b',
			'style'      => 'reserved word',
			'childregex' => []
		},
		{
			'name'       => 'datatypes',
			'regex'      => '(?i)\\b(integer|blol|date|numeric|character|varying|varchar|char)\\b',
			'style'      => 'predefined type',
			'childregex' => []
		},
		{
			'name'       => 'words',
			'regex'      => '(?i)\\b(constraint|key|references|primary|table|foreign|add|insert|group by)\\b',
			'style'      => 'reserved word',
			'childregex' => []
		}
	]
};

# enhanced by W. Friebel
$LANGUAGE{'patch'} = {
	'filename' => '(?i)\\.patch$|\\.diff$',
	'regex'    => '',
	'patterns' => [
		{
			'name'       => 'header',
			'regex'      => '^Index: .*?$|^===== .*?$|^diff .*?$|^--- .*?$|^\+\+\+ .*?$|^\*\*\* .*?$',
			'style'      => 'separator',
			'childregex' => []
		},
		{
			'name'       => 'hunk',
			'regex'      => '^@@ .*?$',
			'style'      => 'line spec',
			'childregex' => []
		},
		{
			'name'       => 'from',
			'regex'      => '^-.*?$',
			'style'      => 'deletion',
			'childregex' => []
		},
		{
			'name'       => 'to',
			'regex'      => '^\+.*?$',
			'style'      => 'insertion',
			'childregex' => []
		},
		{
			'name'       => 'mod',
			'regex'      => '^\!.*?$',
			'style'      => 'modification',
			'childregex' => []
		},
	]
};

#####
#
# LANGUAGE: shell script
#

$LANGUAGE{'shellscript'} = {
	'filename' => '\\.(ebuild|eclass|sh|shell)$',
	'regex'    => '^\\s*#\\s*![^\\s]*(sh|bash|ash|zsh|ksh)',
	'patterns' => [
		{
			'name' => 'comment',
#			'regex' => '^[ \t]*[^$]?\#[^!]?.*?$',
			'regex' => '(^| )#([^\\!].)*?$',
			'style' => 'comment',
			'childregex' => []
		},
		{
			'name' => 'identifier',
			'regex' => '[a-zA-Z][a-zA-Z0-9_]*=',
			'style' => 'identifier',
			'childregex' => [
				{
					'name' => 'identifier',
				'regex' => '[a-zA-Z][a-zA-Z0-9_]*',
				'style' => 'identifier',
				'childregex' => []
				}
			]
		},
		{
			'name' => 'identifier',
			'regex' => '\\$([0-9#\\*]|[a-zA-Z][a-zA-Z0-9_]*)',
			'style' => 'identifier',
			'childregex' => []
		},
		{
			'name' => 'interpreter line',
			'regex' => '^[ \t]*#!.*?$',
			'style' => 'preprocessor',
			childregex => []
		},
		{
			'name' => 'string',
				'regex' => '""|"(\\\\"|[^\\"])*"',
			'style' => 'string',
			childregex => [
				{
					'name' => 'identifier',
					'regex' => '\\$([0-9#\\*]|[a-zA-Z][a-zA-Z0-9_]*)',
					'style' => 'identifier',
					'childregex' => []
				}
			]
		}
	]
};

$LANGUAGE{'sh'} = $LANGUAGE{'shellscript'};
$LANGUAGE{'ebuild'} = $LANGUAGE{'shellscript'};
$LANGUAGE{'eclass'} = $LANGUAGE{'shellscript'};
return \%LANGUAGE;

};

__END__

=head1 code2color

 Convert source code (c,java,perl,html,...) into color-coded ASCII text.

=head1 SYNOPSIS

 code2color [-l LANG] [input_file]

=head1 DESCRIPTION

This script is an adaptation of Peter Palfrader's code2html perl package.
It accepts input from STDIN or from an input_file. The language mode isr
guessed from the input and the file name. If this is not correct, it canr
be changed using the -l option. Language modes provided are

	ada, ada95, awk, c, c++, cc, cxx, groff, html,
	java, javascript, js, m4, make, makefile, pas,
	pas, pascal, perl, plain, pov, povray, ruby, sql.

=head1 ORIGINAL AUTHORS

Jim Mahoney (mahoney AT marlboro.edu), Peter Palfrader, and others.

=head1 COPYRIGHT and LICENSE

 Copyright (c) 1999, 2000 by Peter Palfrader and others.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
``Software''), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

=head1 SEE ALSO

 Peter Palfrader's Code2HTML page at http://www.palfrader.org/code2html/