head	1.1;
access;
symbols;
locks; strict;
comment	@# @;


1.1
date	2021.12.09.04.15.26;	author agc;	state Exp;
branches;
next	;
commitid	hai3vSZTb5K0DVjD;


desc
@@


1.1
log
@Elex - an embeddable regexp-based lexer
=======================================

I have found myself fairly often needing a lexer utility to tokenise
input (for configuration files, for various file-scanning utilities,
and for other applciations), but using full-blown lex(1) program to do
this is overkill, or designed for a separate process, which doesn't fit
well with the design - syntax-coloring editors, for example.

This utility, elex, is a regexp-based tokenizer, an embedded lexer,
which can be used for various uses.  It usually takes a lexer file
(similar to lex input files), although the API allows lexers to be
built on the fly by just issuing the calls to make new rules.

Implementation
==============

Normal lex(1) is implemented (usually) as a conglomeration of all the
regular expressions for a start state - if multiple matches are found,
the largest match is the one used.  I've found that, in practice, this
constrains the way a number of things are done.  So I've implemented
elex, using a multiple, prioritised multiple regexp matching scheme.
This supercedes the usual way of distinguishing reserved words and
identifiers in the lexer - recognising all "words" first, and
searching for each word through a number of tables; if a match is not
found, then the word recognized is an identifier.  elex works around
this by using a regexp to match reserved words first, and then to
recognise the word as an identifier after that.  Since normal regular
expressions usually progress through the input trying to find a match,
the regular expressions used in elex are constrained by anchoring the
search, not allowing progression through the input.  In practice, this
makes for more efficient matching.

another side effect is the ability to use more modern regexp features,
such as perl escapes, UTF-8 matching, in-subexpression ignore case, etc.

elex implements start states, similar to flex.  These are useful for
recognising multiline comments (almost any language), or multi-line
strings (perl, python, lua etc).

elex dynamically sizes the regmatch arrays used to accommodate the
largest regexp in the input, and matching subexpressions can be
returned to the caller.  The 0'th subexpression is the whole matching
expression, and is the same as "yytext".

And so on to an elex definition which recognises C and some C++:

	# start state
	%state COMMENT

	# the types we define
	%type IDENT	0xdb8ea4d
	%type PUNCT	0xe454e3a
	%type NUMBER	0xca1edaec
	%type COMMENT	0xee5ae423
	%type CONSTANT	0xd497741f
	%type PREPROC	0xdcf9b98d
	%type RESWORD1	0xb5ac6a6a
	%type RESWORD2	0xb5ac6a6b

	# and finally... the rules

	<INITIAL>(auto|char|class|const|double|enum|extern|float|friend|inline|int|long|mutable|namespace|new|private|protected|public|register|requires|short|signed|static|this|struct|this|typedef|union|unsigned|void|volatile)\></> { return RESWORD1; }
	<INITIAL>(asm|break|case|catch|continue|default|do|else|for|goto|if|return|switch|throw|try|while)\></>	{ return RESWORD2; }
	<INITIAL>[a-zA-Z_][0-9a-zA-Z_]*</>	{ return IDENT; }

	<INITIAL>([1-9][0-9]*|0x[0-9a-f]|0X[0-9A-F]+|0[0-7]*|'(\\.|[^'])*')</>		{ return NUMBER; }
	<INITIAL>[ \t\n\r]+</>			{ return PUNCT; }

	<INITIAL>/\*</>				{ BEGIN(COMMENT); return COMMENT; }
	<COMMENT>[^\n]*\*/</>			{ BEGIN(INITIAL); return COMMENT; }
	<COMMENT>\n|[^\n]+</>			{ return COMMENT; }

	<INITIAL>//[^\n]*</>			{ return COMMENT; }

	<INITIAL>"(\\.|[^"])*"</>		{ return CONSTANT; }

	<INITIAL>(==|[-]>|!=|<=|>=|~=|%=|&=|[*]=|[-]=|[+]=|[|]=|(<<|>>)=?)</>	{ return PUNCT; }
	<INITIAL>[\u005b;(){}\u005d*<>,+/%~!\u005e&=|.?:\u002d]</>		{ return PUNCT; }

	<INITIAL>#[ \t]*(define|el(se|if)|endif|error|if|ifn?def|include|line|pragma|undef)[^\n]*</>	{ return PREPROC; }

Start states are explicitly used for rules, since it is easier to read
in practice.  Elex comments are eol-style comments, beginning '#' and
ending with '\n'.  Types can be defined using the "%type" directive,
and the unsigned 32bit value they take will be returned.  This is more
work than using magic constants, but much more readable in practice -
see the example calling program below.

A rule is of the form:

	<startstate>regexp</>	{ BEGIN(newstate); return TYPE; }

(the BEGIN() is optional.  </> terminates the regexp - it's clearer
than wrapping everything in double quotes or some other normal
character which would then have to be escaped.  The return is also
optional, but is usually present in practice.)

Hex codepoints can be used to specify wildcard characters in character
classes - the ones enclosed in [] above).

Start states can be defined using the %state (or %x) directive, and
transitioned to using the BEGIN() action, in the same way as standard
lex(1).

Elex provides bookmarks, which are numbered numerically from 0.
Assuming a mark has already been successfully created using
"set-mark", the bookmark offset can be retrieved by using its index
using "get-mark", and the user can then seek to that offset.  This
makes it possible to set up, and re-visit sync points, in case there
was ever a programming language or other input devised which was
ambiguous to some degree.

A heavier-weight alternative to bookmarks is to simply clone the elex
structure at the point of possible divergence, and perform the
alternatives on the clones.  A deep copy is made during the clone
operation.

elex is called from a C (or other language) program in much the same
way as lex is:

	int
	main(int argc, char **argv)
	{
		uint64_t	 len;
		elex_t		*elex;
		size_t		 size;
		char		*lexfile;
		char		*input;
		char		*text;
		char		*s;
		int		 graphic;
		int		 type;
		int		 i;

		input = lexfile = NULL;
		s = NULL;
		size = 0;
		graphic = 0;
		while ((i = getopt(argc, argv, "Vf:gi:")) != -1) {
			switch(i) {
			case 'V':
				printf("elex version " ELEX_VERSION_S(ELEX_VERSION_NUM) "\n");
				exit(EXIT_SUCCESS);
			case 'f':
				lexfile = optarg;
				break;
			case 'g':
				graphic = 1;
				break;
			case 'i':
				input = optarg;
				break;
			}
		}
		if ((elex = elex_new()) == NULL) {
			errx(EXIT_FAILURE, "can't create new elex structure");
		}
		if (!elex_exec(elex, "read-defs-file", lexfile, 0)) {
			errx(EXIT_FAILURE, "can't read defs file '%s'", lexfile);
		}
		if (input == NULL) {
			input = argv[optind];
		}
		if ((s = readfile(input, &size)) == NULL || size == 0) {
			errx(EXIT_FAILURE, "can't read input file '%s'", input);
		}
		if (!elex_exec(elex, "insert", s, size)) {
			errx(EXIT_FAILURE, "can't set input");
		}
		while (elex_exec(elex, "next-token", NULL, 0) != 0) {
			type = elex_exec(elex, "get-yytype", NULL, 0);
			text = elex_exec_str(elex, "get-yytext", 0, &len);
			if (graphic) {
				switch(type) {
				case /* "IDENT" */ 0xdb8ea4d:
				case /* "PUNCT" */ 0xe454e3a:
				case 1:
					/* no color, standard */
					printf("%.*s", (int)len, text);
					break;
				case /* "NUMBER" */ 0xca1edaec:
				case 2:
					p(CYAN, len, text);
					break;
				case /* "COMMENT" */ 0xee5ae423:
				case 3:
					p(BRIGHT_RED, len, text);
					break;
				case /* "CONSTANT" */ 0xd497741f:
				case 4:
					p(MAGENTA, len, text);
					break;
				case /* "PREPROC" */ 0xdcf9b98d:
				case 5:
					p(GREEN, len, text);
					break;
				case /* "RESWORD1" */ 0xb5ac6a6a:
				case 6:
					p(BRIGHT_GREEN, len, text);
					break;
				case /* "RESWORD2" */ 0xb5ac6a6b:
				case 7:
					p(BRIGHT_YELLOW, len, text);
					break;
				default:
					printf("%.*s", (int)len, text);
					break;
				}
			} else {
				printf("[%d] %d '%s'\n", type, (int)len, text);
			}
			free(text);
		}
		elex_dispose(&elex);
		exit(EXIT_SUCCESS);
	}

This program, when invoked with -g, and using the elex definition above,
will perform the same syntax coloring on C programs as is performed in
my editor.

All access to elex is through an opaque elex_t structure pointer.

There are 5 functions in the API:

	/* create and destroy */
	elex_t *elex_new(void);
	int elex_dispose(elex_t **/*elex*/);

	/* these functions do ALL the work */
	int64_t	elex_exec(elex_t */*elex*/, const char */*info*/, const char */*s*/, int64_t /*n*/);
	void *elex_exec_str(elex_t */*elex*/, const char */*info*/, uint64_t /*n*/, uint64_t */*size*/);

	/* with one exeception - deal with states */
	int elex_make_new_rule(elex_t */*elex*/, const char */*startstate*/,
		const char */*pat*/, const char */*newstate*/, int /*ret*/);

An editable input buffer is held in the elex structure.  This can be
set, additional input inserted, and bytes deleted from it.  Callers
can seek within the input buffer, although it is the caller's
responsibility to set the start states correctly after any seeking.

Usually a single file is used (which can be re-used by other programs),
although new start states, types and rules can be done manually.

Note that the type returned on EOF is 0, as in lex, so it is best to
start your types at 1; an alternative is to use stringswitch methods
by returning a hash value of the state name. See the elex file above.

There are 2 main accessor functions:  elex_exec(), which executes an
action and returns a numeric result, and elex_exec_str() which
executes an action and returns a string (allocated using calloc(1),
and it is the caller's responsibility to free this storage).  Within
each of these functions, the second, string argument gives the action
to be performed.  These actions are:

elex_exec:
"delete" - delete n chars from the input
"get-input-length" - return the length of the input buffer
"get-mark" - return bookmark number n (see set-mark)
"get-yyleng" - get the length of the pattern matched
"get-yyline" - get the current line number in the input buffer
"get-yytype" - return the type of the pattern matched
"hash" - return djbhash value for a string
"insert" - insert bytes into the input buffer
"new-state" - make a new start state
"new-type" - make a new type
"next-token" - perform the equivalent of yylex(). Return the type of
	the pattern matched.  If no pattern is matched, print the
	current byte on stdout and advance 1 byte.
"read-defs" - read the elex definitions from memory
"read-defs-file" - read the elex definitions from a file
"seek" - move to an ofset in the input buffer. It is the caller's
	responsibility to set the start state after using this action
"tell" - return the current offset in the input buffer
"set-mark" - set a bookmark at the current offset, and return the index of
	the mark
"set-start-state" - set the current start state

elex_exec_str:
"clone" - make a deep copy of the elex struct
"get-rule" - return the full rule for the n'th rule
"get-subexpr" - for the current match, get the n'th matching subexpression in allocated space
"get-yyrule-pat" - get the input pattern for the matching regexp in
	allocated space
"get-yystate" - get the name of the current state in allocated space
"get-yytext" - get the full text of the current match in allocated space

There is also a function to add a new rule, elex_make_new_rule. In future,
this will be extended to allow changing rules on the fly (an example use
of this would be to use a regexp to recognise identifiers in the language).

Designing elex definition files
===============================

Firstly, any start states are defined. Start states are states in elex
which are different from the initial state, aka INITIAL. They are generally
used for complex recognition, like multi-line strings or comments - they
work more effectively, and the definitions are clearer.

After start states, the next section to be defined is the types
section.  This allows us to define the return types that elex will
return to the calling program.  Since a 0 type usually denotes end of
input, it is advised to define return types starting at 1.
Historically, in lex definitions, the user-defined types started at
256, and it was common to return ASCII values for single characters up
to 256.  Since this is no longer acceptable in a world with multibyte
characters, and because we tend to tokenise based on types of input
tokens, hopefully this practice will never be used again.

Current start states are specified at the start of the rule.

	<INITIAL>/\*</>				{ BEGIN(COMMENT); return COMMENT; }
	<COMMENT>[^\n]*\*/</>			{ BEGIN(INITIAL); return COMMENT; }
	<COMMENT>\n|[^\n]+</>			{ return COMMENT; }

To use the start state above as an example, if we are in the initial state, and
get a "/*" pattern in the input, we transition to the COMMENT state using the
"BEGIN" action, and return the COMMENT type.

Once we are in the COMMENT state, if we receive any text terminated by
a "*/" pattern, we transition to the INITIAL state, and return a
COMMENT type.  Having specified the termination of the COMMENT state,
if we receive any other text, we simply return a COMMENT at the end of
each line.

It is also possible just to specify a COMMENT action as

	<INITIAL>/\*.*\*/</>

but any special actions to be taken on a newline character would be missed
in this.

Usually, when tokenising programming language, there would be a number
of definitions for reserved words, and standard identifiers. There would
also be definitions for punctuation, and numeric and string constants.
Some languages have definitions for multiline strings.

Alistair Croooks
Thu Nov 18 16:57:44 PST 2021
@
text
@[7] 3 'use'
[1] 1 ' '
[1] 6 'strict'
[8] 1 ';'
[1] 2 '

'
[7] 2 'my'
[1] 1 ' '
[1] 16 '$SYMBIAN_VERSION'
[8] 1 ';'
[1] 1 '
'
[7] 2 'my'
[1] 1 ' '
[1] 13 '$SYMBIAN_ROOT'
[8] 1 ';'
[1] 1 '
'
[7] 2 'my'
[1] 1 ' '
[1] 9 '$SDK_NAME'
[8] 1 ';'
[1] 1 '
'
[7] 2 'my'
[1] 1 ' '
[1] 12 '$SDK_VARIANT'
[8] 1 ';'
[1] 1 '
'
[7] 2 'my'
[1] 1 ' '
[1] 12 '$SDK_VERSION'
[8] 1 ';'
[1] 1 '
'
[7] 2 'my'
[1] 1 ' '
[1] 4 '$WIN'
[8] 1 ';'
[1] 2 '

'
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[1] 4 '$ENV'
[8] 1 '{'
[1] 4 'PATH'
[8] 1 '}'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 45 'm!\\Symbian\\(.+?)\\(.+?)\\Epoc32\\gcc\\bin!i'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 5 '
    '
[1] 16 '$SYMBIAN_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[2] 2 '$1'
[8] 1 ';'
[1] 5 '
    '
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[2] 2 '$2'
[8] 1 ';'
[1] 5 '
    '
[1] 4 '$WIN'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[8] 1 '('
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 7 'm!_CW!i'
[1] 1 ' '
[8] 1 '|'
[8] 1 '|'
[1] 1 ' '
[1] 9 '$SDK_NAME'
[1] 1 ' '
[7] 2 'eq'
[1] 1 ' '
[4] 6 ''8.1a''
[8] 1 ')'
[1] 1 ' '
[8] 1 '?'
[1] 2 '
	'
[4] 8 ''winscw''
[1] 1 ' '
[8] 1 ':'
[1] 1 ' '
[4] 6 ''wins''
[8] 1 ';'
[1] 5 '
    '
[1] 4 '$ENV'
[8] 1 '{'
[1] 3 'WIN'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$WIN'
[8] 1 ';'
[1] 5 '
    '
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 15 'm!Series60_v20!'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''S60''
[8] 1 ';'
[1] 2 '
	'
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''2.0''
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 15 'm!Series60_v21!'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''S60''
[8] 1 ';'
[1] 2 '
	'
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''2.1''
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 14 'm!S60_2nd_FP2!'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''S60''
[8] 1 ';'
[1] 2 '
	'
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''2.6''
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 14 'm!S60_2nd_FP3!'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''S60''
[8] 1 ';'
[1] 2 '
	'
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''2.8''
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 16 'm!S80_DP2_0_SDK!'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''S80''
[8] 1 ';'
[1] 2 '
	'
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S80SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''2.0''
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 17 'm!Nokia_7710_SDK!'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''S90''
[8] 1 ';'
[1] 2 '
	'
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S90SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''1.1''
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 1 '
'
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 4 '$ENV'
[8] 1 '{'
[1] 4 'PATH'
[8] 1 '}'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 43 'm!\\S60\\devices\\(.+?)\\epoc32\\gcc\\bin!i'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''S60''
[8] 1 ';'
[1] 2 '
	'
[1] 9 '$SDK_NAME'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[2] 2 '$1'
[8] 1 ';'
[1] 2 '
	'
[1] 4 '$WIN'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 3 'WIN'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 8 ''winscw''
[8] 1 ';'
[1] 2 '
	'
[1] 16 '$SYMBIAN_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''9.4''
[8] 1 ';'
[1] 2 '
	'
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''5.0''
[8] 1 ';'
[1] 2 '
	'
[1] 13 '$SYMBIAN_ROOT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 8 'EPOCROOT'
[8] 1 '}'
[8] 1 ';'
[1] 1 '
'
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 4 '$ENV'
[8] 1 '{'
[1] 4 'PATH'
[8] 1 '}'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 45 'm!\\Symbian\\UIQ_(\d)(\d)\\Epoc32\\gcc\\bin!i'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 5 '
    '
[1] 9 '$SDK_NAME'
[1] 4 '    '
[8] 1 '='
[1] 1 ' '
[4] 5 ''UIQ''
[8] 1 ';'
[1] 5 '
    '
[1] 12 '$SDK_VARIANT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''UIQ''
[8] 1 ';'
[1] 5 '
    '
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'UIQSDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 7 '"$1.$2"'
[8] 1 ';'
[1] 5 '
    '
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 2 '=~'
[1] 1 ' '
[4] 6 '/^2\./'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[1] 16 '$SYMBIAN_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 6 ''7.0s''
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 1 ' '
[7] 4 'else'
[1] 1 ' '
[8] 1 '{'
[1] 2 '
	'
[7] 3 'die'
[1] 1 ' '
[4] 42 '"$0: Unknown UIQ version '$SDK_VERSION'\n"'
[8] 1 ';'
[1] 5 '
    '
[8] 1 '}'
[1] 5 '
    '
[1] 4 '$WIN'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 8 ''winscw''
[8] 1 ';'
[1] 1 ' '
[3] 41 '# This is CodeWarrior, how about Borland?'
[1] 5 '
    '
[1] 4 '$ENV'
[8] 1 '{'
[1] 3 'WIN'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$WIN'
[8] 1 ';'
[1] 1 '
'
[8] 1 '}'
[1] 2 '

'
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[1] 4 'open'
[8] 1 '('
[1] 3 'GCC'
[8] 1 ','
[1] 1 ' '
[4] 15 '"gcc -v 2>&1 |"'
[8] 1 ')'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 4 '
   '
[7] 5 'while'
[1] 1 ' '
[8] 1 '('
[8] 1 '<'
[1] 3 'GCC'
[8] 1 '>'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 6 '
     '
[3] 8 '# print;'
[1] 6 '
     '
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[4] 37 '/Reading specs from (.+?)\\Epoc32\\/i'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 8 '
       '
[1] 13 '$SYMBIAN_ROOT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[2] 2 '$1'
[8] 1 ';'
[1] 8 '
       '
[3] 45 '# The S60SDK tells the Series 60 SDK version.'
[1] 8 '
       '
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 5 '
	   '
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[1] 13 '$SYMBIAN_ROOT'
[1] 1 ' '
[7] 2 'eq'
[1] 1 ' '
[4] 23 ''C:\Symbian\6.1\Shared''
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 1 ' '
[3] 11 '# Visual C.'
[1] 9 '
	       '
[1] 13 '$SYMBIAN_ROOT'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 25 ''C:\Symbian\6.1\Series60''
[8] 1 ';'
[1] 9 '
	       '
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''1.2''
[8] 1 ';'
[1] 5 '
	   '
[8] 1 '}'
[1] 1 ' '
[7] 5 'elsif'
[1] 1 ' '
[8] 1 '('
[1] 13 '$SYMBIAN_ROOT'
[1] 1 ' '
[7] 2 'eq'
[1] 1 ' '
[4] 28 ''C:\Symbian\Series60_1_2_CW''
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 1 ' '
[3] 14 '# CodeWarrior.'
[1] 9 '
	       '
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 6 'S60SDK'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 5 ''1.2''
[8] 1 ';'
[1] 5 '
	   '
[8] 1 '}'
[1] 8 '
       '
[8] 1 '}'
[1] 8 '
       '
[7] 4 'last'
[8] 1 ';'
[1] 6 '
     '
[8] 1 '}'
[1] 4 '
   '
[8] 1 '}'
[1] 4 '
   '
[1] 5 'close'
[1] 1 ' '
[1] 3 'GCC'
[8] 1 ';'
[1] 1 '
'
[8] 1 '}'
[1] 1 ' '
[7] 4 'else'
[1] 1 ' '
[8] 1 '{'
[1] 3 '
  '
[7] 3 'die'
[1] 1 ' '
[4] 29 '"$0: failed to run gcc: $!\n"'
[8] 1 ';'
[1] 1 '
'
[8] 1 '}'
[1] 2 '

'
[7] 3 'die'
[1] 1 ' '
[4] 40 '"$0: failed to locate the Symbian SDK\n"'
[1] 1 ' '
[7] 6 'unless'
[1] 1 ' '
[7] 7 'defined'
[1] 1 ' '
[1] 13 '$SYMBIAN_ROOT'
[8] 1 ';'
[1] 2 '

'
[7] 2 'my'
[1] 1 ' '
[1] 5 '$UARM'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 4 'UARM'
[8] 1 '}'
[1] 1 ' '
[8] 1 '?'
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 4 'UARM'
[8] 1 '}'
[1] 1 ' '
[8] 1 ':'
[1] 1 ' '
[4] 6 '"urel"'
[8] 1 ';'
[1] 1 '
'
[7] 2 'my'
[1] 1 ' '
[1] 5 '$UREL'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 46 '"$SYMBIAN_ROOT\\epoc32\\release\\-ARM-\\$UARM"'
[8] 1 ';'
[1] 1 '
'
[7] 2 'if'
[1] 1 ' '
[8] 1 '('
[1] 13 '$SYMBIAN_ROOT'
[1] 1 ' '
[7] 2 'eq'
[1] 1 ' '
[4] 25 ''C:\Symbian\6.1\Series60''
[1] 1 ' '
[8] 1 '&'
[8] 1 '&'
[1] 1 ' '
[1] 4 '$ENV'
[8] 1 '{'
[1] 3 'WIN'
[8] 1 '}'
[1] 1 ' '
[7] 2 'eq'
[1] 1 ' '
[4] 8 ''winscw''
[8] 1 ')'
[1] 1 ' '
[8] 1 '{'
[1] 5 '
    '
[1] 5 '$UREL'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[4] 60 '"C:\\Symbian\\Series60_1_2_CW\\epoc32\\release\\-ARM-\\urel"'
[8] 1 ';'
[1] 1 '
'
[8] 1 '}'
[1] 1 '
'
[1] 4 '$ENV'
[8] 1 '{'
[1] 4 'UREL'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 5 '$UREL'
[8] 1 ';'
[1] 1 '
'
[1] 4 '$ENV'
[8] 1 '{'
[1] 4 'UARM'
[8] 1 '}'
[1] 1 ' '
[8] 1 '='
[1] 1 ' '
[1] 5 '$UARM'
[8] 1 ';'
[1] 2 '

'
[8] 1 '['
[1] 1 ' '
[1] 13 '$SYMBIAN_ROOT'
[8] 1 ','
[1] 1 ' '
[1] 16 '$SYMBIAN_VERSION'
[8] 1 ','
[1] 1 ' '
[1] 9 '$SDK_NAME'
[8] 1 ','
[1] 1 ' '
[1] 12 '$SDK_VARIANT'
[8] 1 ','
[1] 1 ' '
[1] 12 '$SDK_VERSION'
[1] 1 ' '
[8] 1 ']'
[8] 1 ';'
[1] 2 '

'
[3] 68 '# The following is a cheat sheet for the right S60/S80 SDK settings.'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 20 '# symbiancommon.bat:'
[1] 1 '
'
[3] 62 '# set EPOC_BIN=%EPOCROOT%Epoc32\gcc\bin;%EPOCROOT%Epoc32\Tools'
[1] 1 '
'
[3] 71 '# set MWCW=C:\Program Files\Metrowerks\CodeWarrior for Symbian OEM v2.8'
[1] 1 '
'
[3] 51 '# set MSVC=C:\Program Files\Microsoft Visual Studio'
[1] 1 '
'
[3] 53 '# set MSVC_BIN=%MSVC%\VC98\Bin;%MSVC%\Aux\MSDev98\Bin'
[1] 1 '
'
[3] 92 '# set MSVC_INC=%MSVC%\VC98\atl\include;%MSVC%\VC98\include;%MSVC%\mfc\include;%MSVC%\include'
[1] 1 '
'
[3] 40 '# set MSVC_LIB=%MSVC%\mfc\lib;%MSVC%\lib'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 54 '# Note that if you are using Microsoft Visual Studio 8'
[1] 1 '
'
[3] 61 '# (for example because you are using the Microsoft Visual C++'
[1] 1 '
'
[3] 62 '#  2005 Express Edition), the MSVC settings will be different:'
[1] 1 '
'
[3] 56 '# set MSVC=C:\Program Files\Microsoft Visual Studio 8\VC'
[1] 1 '
'
[3] 25 '# set MSVC_BIN=%MSVC%\bin'
[1] 1 '
'
[3] 29 '# set MSVC_INC=%MSVC%\include'
[1] 1 '
'
[3] 25 '# set MSVC_LIB=%MSVC%\lib'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-1.2-cw:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 40 '# set EPOCROOT=\Symbian\Series60_1_2_CW\'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 131 '# set PATH=%EPOC_BIN%;%MSVC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 107 '# set USERDEFS=%USERDEFS% -D__SERIES60_12__ -D__SERIES60_MAJOR__=1 -D__SERIES60_MINOR__=2 -D__SERIES60_1X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-1.2-vc:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 37 '# set EPOCROOT=\Symbian\6.1\Series60\'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 126 '# set PATH=\Symbian\6.1\Shared\Epoc32\gcc\bin;\Symbian\6.1\Shared\Epoc32\Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 24 '# set INCLUDE=%MSVC_INC%'
[1] 1 '
'
[3] 20 '# set LIB=%MSVC_LIB%'
[1] 1 '
'
[3] 107 '# set USERDEFS=%USERDEFS% -D__SERIES60_12__ -D__SERIES60_MAJOR__=1 -D__SERIES60_MINOR__=2 -D__SERIES60_1X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-2.0-cw:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 45 '# set EPOCROOT=\Symbian\7.0s\Series60_v20_CW\'
[1] 1 '
'
[3] 58 '# set EPOCDEVICE=Series60_2_0_CW:com.Nokia.Series60_2_0_CW'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 120 '# set PATH=%EPOC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 107 '# set USERDEFS=%USERDEFS% -D__SERIES60_20__ -D__SERIES60_MAJOR__=2 -D__SERIES60_MINOR__=0 -D__SERIES60_2X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-2.0-vc:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 42 '# set EPOCROOT=\Symbian\7.0s\Series60_v20\'
[1] 1 '
'
[3] 48 '# set EPOCDEVICE=Series60_v20:com.nokia.series60'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 69 '# set PATH=%EPOC_BIN%;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 24 '# set INCLUDE=%MSVC_INC%'
[1] 1 '
'
[3] 20 '# set LIB=%MSVC_LIB%'
[1] 1 '
'
[3] 107 '# set USERDEFS=%USERDEFS% -D__SERIES60_20__ -D__SERIES60_MAJOR__=2 -D__SERIES60_MINOR__=0 -D__SERIES60_2X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-2.1-cw:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 45 '# set EPOCROOT=\Symbian\7.0s\Series60_v21_CW\'
[1] 1 '
'
[3] 51 '# set EPOCDEVICE=Series60_v21_CW:com.Nokia.series60'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 120 '# set PATH=%EPOC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 107 '# set USERDEFS=%USERDEFS% -D__SERIES60_21__ -D__SERIES60_MAJOR__=2 -D__SERIES60_MINOR__=1 -D__SERIES60_2X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-2.6-cw:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 44 '# set EPOCROOT=\Symbian\8.0a\S60_2nd_FP2_CW\'
[1] 1 '
'
[3] 50 '# set EPOCDEVICE=S60_2nd_FP2_CW:com.nokia.series60'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 120 '# set PATH=%EPOC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 130 '# set USERDEFS=%USERDEFS% -D__SERIES60_26__ -D__SERIES60_MAJOR__=2 -D__SERIES60_MINOR__=6 -D__SERIES60_2X__ -D__BLUETOOTH_API_V2__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-2.6-vc:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 41 '# set EPOCROOT=\Symbian\8.0a\S60_2nd_FP2\'
[1] 1 '
'
[3] 47 '# set EPOCDEVICE=S60_2nd_FP2:com.nokia.Series60'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 69 '# set PATH=%EPOC_BIN%;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 24 '# set INCLUDE=%MSVC_INC%'
[1] 1 '
'
[3] 20 '# set LIB=%MSVC_LIB%'
[1] 1 '
'
[3] 130 '# set USERDEFS=%USERDEFS% -D__SERIES60_26__ -D__SERIES60_MAJOR__=2 -D__SERIES60_MINOR__=6 -D__SERIES60_2X__ -D__BLUETOOTH_API_V2__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-2.8-cw:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 41 '# set EPOCROOT=\Symbian\8.1a\S60_2nd_FP3\'
[1] 1 '
'
[3] 47 '# set EPOCDEVICE=S60_2nd_FP3:com.nokia.series60'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 120 '# set PATH=%EPOC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 130 '# set USERDEFS=%USERDEFS% -D__SERIES60_28__ -D__SERIES60_MAJOR__=2 -D__SERIES60_MINOR__=8 -D__SERIES60_2X__ -D__BLUETOOTH_API_V2__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s60-2.8-vc:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 41 '# set EPOCROOT=\Symbian\8.1a\S60_2nd_FP3\'
[1] 1 '
'
[3] 47 '# set EPOCDEVICE=S60_2nd_FP3:com.nokia.series60'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 69 '# set PATH=%EPOC_BIN%;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 130 '# set USERDEFS=%USERDEFS% -D__SERIES60_28__ -D__SERIES60_MAJOR__=2 -D__SERIES60_MINOR__=8 -D__SERIES60_2X__ -D__BLUETOOTH_API_V2__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 38 '# s60-5.0  - S60 5th Edition SDK v1.0:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 53 '# set EPOCROOT=\S60\devices\S60_5th_Edition_SDK_v1.0\'
[1] 1 '
'
[3] 65 '# set PATH=%EPOCROOT%Epoc32\gcc\bin;%EPOCROOT%Epoc32\tools;%PATH%'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s80-2.0-cw:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 46 '# set EPOCROOT=\Symbian\7.0s\S80_DP2_0_SDK_CW\'
[1] 1 '
'
[3] 57 '# set EPOCDEVICE=Series80_DP2_0_SDK_CW:com.nokia.Series80'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 120 '# set PATH=%EPOC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 107 '# set USERDEFS=%USERDEFS% -D__SERIES80_20__ -D__SERIES80_MAJOR__=2 -D__SERIES80_MINOR__=0 -D__SERIES80_2X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# s80-2.0-vc:'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 43 '# set EPOCROOT=\Symbian\7.0s\S80_DP2_0_SDK\'
[1] 1 '
'
[3] 54 '# set EPOCDEVICE=Series80_DP2_0_SDK:com.nokia.Series80'
[1] 1 '
'
[3] 15 '# symbiancommon'
[1] 1 '
'
[3] 120 '# set PATH=%EPOC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 107 '# set USERDEFS=%USERDEFS% -D__SERIES80_20__ -D__SERIES80_MAJOR__=2 -D__SERIES80_MINOR__=0 -D__SERIES80_2X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 13 '# UIQ-2.1-vc:'
[1] 1 '
'
[3] 31 '# set EPOCROOT=\Symbian\UIQ_21\'
[1] 1 '
'
[3] 17 '# set EPOCDEVICE='
[1] 1 '
'
[3] 62 '# set EPOC_BIN=%EPOCROOT%Epoc32\gcc\bin;%EPOCROOT%Epoc32\Tools'
[1] 1 '
'
[3] 34 '# set MWCW=C:\APPS\codewarrior_3.0'
[1] 1 '
'
[3] 51 '# set MSVC=C:\Program Files\Microsoft Visual Studio'
[1] 1 '
'
[3] 47 '# set MSVC_BIN=%MSVC%;%MSVC%\Common\MSDev98\Bin'
[1] 1 '
'
[3] 72 '# set MSVC_INC=%MSVC%\VC98\atl\include;%MSVC%\mfc\include;%MSVC%\include'
[1] 1 '
'
[3] 40 '# set MSVC_LIB=%MSVC%\mfc\lib;%MSVC%\lib'
[1] 1 '
'
[3] 120 '# set PATH=%EPOC_BIN%;%MWCW%\Bin;%MWCW%\Symbian_Tools\Command_Line_Tools;%MSVC_BIN%;C:\perl\bin;C:\winnt\system32;%PATH%'
[1] 1 '
'
[3] 87 '# set USERDEFS=%USERDEFS% -D__UIQ_21__ -D__UIQ_MAJOR__=2 -D__UIQ_MINOR__=1 -D__UIQ_2X__'
[1] 1 '
'
[3] 1 '#'
[1] 1 '
'
[3] 5 '# EOF'
[1] 1 '
'
@
