/*
Program WinCaml: Graphical User Interface
for interactive use of Caml-Light and Ocaml.
Copyright (C) 2005-2017 Jean Mouric 35700 Rennes France
email: jean.mouric@orange.fr

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. 
*/

// File OCamlIndenter.cpp

#include "platform.h"
#include "OCamlIndenter.h"

using namespace std;

class token
{
public:
	int num;
	size_t offset;
	size_t length;
	int leading;
	int trailing;
	int newline;

	token(int n, size_t t, size_t l, int ld, int tr, int nl)
	{
		num = n;
		offset = t;
		length = l;
		leading = ld;
		trailing = tr;
		newline = nl;
	}
};

class WinCamlException
{
};

string convert(const wstring& str);
static bool is_operator_name(int la, wstring s);
static void match(int t, int k);
static void toplevel_phrase(int k);
static void definition_or_specification(int k);
static void specification(int k);
static void definition(int k);
static void let_binding(int k);
static void class_binding_or_spec(int k);
static void class_spec(int k);
static void value_name(int k);
static void directive_argument(int k);
static void external_declaration(int k);
static void class_binding(int k);
static void classtype_def(int k);
static void module_type(int k);
static void begin_module_type(int k);
static void end_module_type(int k);
static void module_expr(int k);
static void begin_module_expr(int k);
static void end_module_expr(int k);
static void ident(int k);
static void module_path(int k);
static void value_path(int k);
static void operator_name(int k);
static void type_parameters(int k);
static void class_body_type(int k);
static void parameter(int k);
static void class_type(int k);
static void class_expr(int k);
static void begin_class_expr(int k);
static void end_class_expr(int k);
static void arguments(int k);
static void argument(int k);
static void type_def(int k);
static void type_params(int k);
static void type_param(int k);
static void type_information(int k);
static void type_equation(int k);
static void type_representation(int k);
static void field_decl(int k);
static void constr_decl(int k);
static void class_field_spec(int k);
static void class_path(int k);
static void modtype_path(int k);
static void extended_module_path(int k);
static void poly_typexpr(int k);
static void begin_poly_typexpr(int k);
static void end_poly_typexpr(int k);
static void class_field(int k);
static void typexpr(int k);
static void begin_typexpr(int k);
static void mod_constraint(int k);
static void typeconstr(int k);
static void tag_spec(int k);
static void tag_spec_full(int k);
static void method_type(int k);
static void expr(int k);
static void stream_component(int k);
static void stream_matching(int k);
static void stream_pattern(int k);
static void stream_pat_comp(int k);
static bool infix_op(int k);
static void error();
static void pattern(int k);
static void simple_pattern(int k);
static void begin_simple_pattern(int k);
static void end_simple_pattern(int k);
static void field(int k);
static void pattern_matching(int k);
static void multiple_matching(int k);
static void class_body(int k);
static void simple_expr(int k);
static void very_simple_expr(int k);
static void end_very_simple_expr(int k);
static int wclex();
static void package_type(int k);
static void package_constraint(int k);

typedef struct CIndentLexerContext
{
	size_t tokenIndex;
	size_t length;
} CIndentLexerContext;

#define YYEOF 0

typedef struct yy_buffer_state *BUFFER_STATE;
extern BUFFER_STATE tt_scan_string (const char *);
extern size_t ttleng;
extern int ttlex();
extern int leading;
extern int trailing;
extern int newline;
extern char* token_start;
extern size_t token_length;
extern size_t currentPos;
wstring* scannedString;
WinCamlException wce;

static int indentAfterIn;
static int indentFilter;

static int cnt;
static wstring tab;
static int la;
static deque<token*> tokenArray;
static size_t tokenIndex;
wstring lexbuf;
static wstring result;

wstring doOCamlIndent(wstring* str, int count, int indent_after_in, int indent_filter);
CIndentLexerContext saveContext();
void restoreContext(CIndentLexerContext context);


CIndentLexerContext saveContext()
{
	CIndentLexerContext context;
	context.tokenIndex = tokenIndex - 1;
	context.length = result.length();
	return context;
}

void restoreContext(CIndentLexerContext context)
{
	tokenIndex = context.tokenIndex;
	la = wclex();
	result = result.substr(0, context.length);
}

int wclex()
{
	token* tk = (token*)(tokenArray[tokenIndex++]);
	lexbuf = scannedString->substr(tk->offset, tk->length);
	leading = tk->leading;
	trailing = tk->trailing;
	newline = tk->newline;
	return tk->num;
}

wstring doOCamlIndent(wstring* str, int count, int indent_after_in, int indent_filter)
{
	if (str->length() == 0) return *str;
	token_start = 0;
	token_length = 0;
	tokenIndex = 0;
	indentAfterIn = indent_after_in;
	indentFilter = indent_filter;
	la = -1;
	newline = 0;
	leading = 0;
	trailing = 0;
	cnt = (count == 0 ? 1 : count);
	tab = (count == 0 ? L"\t": wstring(count, L' '));
	result = L"";
	lexbuf = L"";
    scannedString = str;
    tt_scan_string(convert(*str).c_str());
	currentPos = 0;
	while (la != YYEOF)
	{
		newline = 0;
		la = ttlex();
		token* tk;        
		if (la == STRING || la == COMMENT)
		{

			tk = new token(la, currentPos - token_length, token_length, leading, trailing, newline);
		}
		else
		{
			tk = new token(la, currentPos - ttleng, ttleng, leading, trailing, newline);
		}
        tokenArray.push_back(tk);
	}
	la = wclex();
	try
	{
		while (la != ENDMARK && la != YYEOF)
		{
			toplevel_phrase(0);
			if (la == ENDMARK)
			{
				match(la, 0);
			}
		}
		if (la == ENDMARK) match(ENDMARK, 0);
	}
	catch (WinCamlException)
	{
		tokenArray.clear();
		return L"";
	}
	tokenArray.clear();
	return result;
}

bool is_operator_name(int la, wstring s)
{
	return la == PREFIX_SYMBOL || la == INFIX_SYMBOL || s == L":=" || s == L"mod" || s == L"land" || s == L"lor"
		|| s == L"lxor" || s == L"lsl" || s == L"lsr" || s == L"asr";
}	

void match(int t, int k)
{
	if (t != la) {
		throw wce;
	}
	while (newline > 1) {
		result += L"\n";
		newline--;
	}
	if (newline) {
		result += L"\n";
		for(int i = 0; i < k; i++) result += tab;
		newline--;
	}
	int tl = trailing;
	int la1 = la;
	wchar_t c1 = lexbuf[0];
	wstring lexbuf1 = lexbuf;
	la = wclex();
	if (la == YYEOF) {
		result += lexbuf1;
		return;
	}
	wchar_t c2 = lexbuf[0];
	if (la1 == UNIT){
		if (leading) {
			result += L"() ";
		}
		else {
			result += L"()";
		}
	}
	else if (c1 == L'(' && is_operator_name(la, lexbuf)) {
		result += lexbuf1;
	}
	else if (c2 == L')' && is_operator_name(la1, lexbuf1)) {
		result += L" " + lexbuf1 + L" ";
	}
	else if (la1 == QUOTE) {
		size_t l = lexbuf.length();
        if ((l > 1 && lexbuf[1] == L'\'') || (l == 1 && tokenIndex < tokenArray.size() && (*scannedString)[tokenArray[tokenIndex]->offset] == L'\''))
		{
			result += (lexbuf1 + L" ");
		}
		else {
			result += lexbuf1;
		}
	}
	else if (newline)
	{
		result += lexbuf1;
	}
	else if (tl && leading)
	{
		result += (lexbuf1 + L" ");
	}
	else
		result += lexbuf1;

	if (la == COMMENT) match(la, k);
}

void toplevel_phrase(int k)
{
	if (la == COMMENT) {
		match(la, k);
	}
	if (la == LET || la == EXTERNAL || la == TYPE || la == EXCEPTION || la == CLASS || la == MODULE
		|| la == OPEN || la == INCLUDE || la == VAL) {
			definition_or_specification(k);
	}
	else if (la == SHARP) {
		match(la, k);
		ident(k + 1);
		directive_argument(k + 1);
	}
	else
	{
		expr(k);
		match(ENDMARK, k);
	}
}

void definition_or_specification(int k)
{	
	switch (la)
	{
	case VAL:
		match(la, k);
		value_name(k + 1);
		match(COLON, k);
		typexpr(k + 1);
		break;
	case LET:
		match(la, k);
		if (la == REC) match(la, k);
		let_binding(k + 1);
		while (la == AND) {
			match(la, k);
			let_binding(k + 1);
		}
		if (la == IN1) {
			match(la, k);
			expr(k + indentAfterIn);
		}
		break;
	case EXTERNAL:
		match(la, k);
		value_name(k);
		match(COLON, k);
		typexpr(k);
		if (lexbuf == L"=") {
			external_declaration(k + 1);
		}
		else {
			error();
		}

		break;
	case TYPE:
		match(la, k);
		type_def(k + 1);
		while (la == AND) {
			match(la, k);
			type_def(k + 1);
		}
		break;
	case EXCEPTION:
		match(la, k);
		match(CAPITALIZED_IDENT, k);
		if (la == OF) {
			match(la, k);
			typexpr(k + 1);
			while(lexbuf == L"*") {
				match(la, k);
				typexpr(k + 1);
			}
		}
		else if (lexbuf == L"=") {
			match(la, k);
			match(CAPITALIZED_IDENT, k + 1);
			while (la == DOT) {
				match(la, k + 1);
				match(CAPITALIZED_IDENT, k + 1);
			}
		}
		break;
	case CLASS:
		match(la, k);
		if (la == TYPE) {
			match(la, k);
			classtype_def(k);
			while (la == AND) {
				match(la, k);
				classtype_def(k);
			}
		}
		else {
			class_binding(k);
			while (la == AND) {
				match(la, k);
				class_binding_or_spec(k);
			}
		}
		break;
	case MODULE:
		match(la, k);
		if (la == TYPE) {
			match(la, k);
			ident(k + 1);
			if (lexbuf == L"=") {
				match(la, k + 1);
				module_type(k + 1);
			}
		}
		else if (la == REC) {
			match(la, k);
			match(CAPITALIZED_IDENT, k + 1);
			match(COLON, k + 1);
			module_type(k + 1);
			if (lexbuf == L"=") {
				match(la, k + 1);
				module_expr(k + 1);
			}
			while (la == AND) {
				match(la, k);
				match(CAPITALIZED_IDENT, k + 1);
				match(COLON, k + 1);
				module_type(k + 1);
				if (lexbuf == L"=") {
					match(la, k + 1);
					module_expr(k + 1);
				}
			}
		}
		else {
			match(CAPITALIZED_IDENT, k);
			while (la == LPAR) {
				match(la, k);
				match(CAPITALIZED_IDENT, k + 1);
				match(COLON, k + 1);
				module_type(k + 1);
				match(RPAR, k);
			}
			if (la == COLON) {
				match(la, k);
				module_type(k + 1);
			}
			if (lexbuf == L"=") {
				match(la, k + 1);
				module_expr(k + 1);
			}
		}
		break;
	case OPEN:
		match(la, k);
		module_path(k + 1);
		break;
	case INCLUDE:
		{
			match(la, k);
			CIndentLexerContext context = saveContext();
			try {
				module_expr(k + 1);
			}
			catch (WinCamlException) {
				restoreContext(context);
				module_type(k + 1);
			}
			break;
		}
	default:
		break;
	}
}

void specification(int k)
{
	if (la == VAL) {
		match(la, k);
		value_name(k);
		match(COLON, k);
		typexpr(k + 1);
	}
	else if (la == EXTERNAL) {
		match(la, k);
		value_name(k);
		match(COLON, k);
		typexpr(k + 1);
		if(lexbuf == L"="){
			match(la, k);
		}
		else {
			error();
		}
		external_declaration(k + 1);
	}
	else if (la == TYPE) {
		match(la, k);
		type_def(k + 1);
		while (la == AND) {
			match(la, k);
			type_def(k + 1);
		}
	}
	else if (la == EXCEPTION) {
		match(la, k);
		constr_decl(k + 1);
	}
	else if (la == CLASS) {
		match(la, k);
		if (la == TYPE) {
			match(la, k);
			classtype_def(k);
			while (la == AND) {
				match(la, k);
				classtype_def(k);
			}
		}
		else {
			class_spec(k);
			while (la == AND) {
				match(AND, k);
				class_spec(k);
			}
		}
	}
	else if (la == MODULE) {
		match(la, k);
		if (la == REC) {
			match(la, k);
			match(CAPITALIZED_IDENT, k);
			match(COLON, k);
			module_type(k + 1);
			while (la == AND) {
				match(la, k);
				match(CAPITALIZED_IDENT, k);
				match(COLON, k);
				module_type(k + 1);
			}
		}
		else if (la == TYPE) {
			match(la, k);
			ident(k + 1);
			if (lexbuf == L"=") {
				match(la, k);
				module_type(k + 1);
			}
		}
		else {
			match(la, k);
			match(CAPITALIZED_IDENT, k);
			while (la == LPAR) {
				match(la, k);
				match(CAPITALIZED_IDENT, k + 1);
				match(COLON, k + 1);
				module_type(k + 1);
				match(RPAR, k);
			}
			match(COLON, k);
			module_type(k + 1);
		}
	}
	else if (la == OPEN) {
		match(la, k);
		module_path(k + 1);
	}
	else {
		match(INCLUDE, k);
		module_type(k + 1);
	}
}

void definition(int k)
{	
	switch (la)
	{
	case LET:
		match(la, k);
		if (la == REC) match(la, k);
		let_binding(k);
		while (la == AND) {
			match(la, k);
			let_binding(k);
		}
		if (la == IN1) {
			match(la, k);
			expr(k + indentAfterIn);
		}
		break;
	case EXTERNAL:
		match(la, k);
		value_name(k);
		match(COLON, k);
		typexpr(k);
		if (lexbuf == L"=") {
			external_declaration(k);
		}
		else {
			error();
		}

		break;
	case TYPE:
		match(la, k);
		type_def(k);
		while (la == AND) {
			type_def(k);
		}
		break;
	case EXCEPTION:
		match(la, k);
		match(CAPITALIZED_IDENT, k);
		if (la == OF) {
			match(la, k);
			typexpr(k);
			while(lexbuf == L"*") {
				match(la, k);
				typexpr(k);
			}
		}
		else {
			if (lexbuf == L"=") {
				match(la, k);
				match(CAPITALIZED_IDENT, k);
				while (la == DOT) {
					match(la, k);
					match(CAPITALIZED_IDENT, k);
				}
			}
		}
		break;
	case CLASS:
		match(la, k);
		if (la == TYPE) {
			match(la, k);
			classtype_def(k);
			while (la == AND) {
				match(la, k);
				classtype_def(k);
			}
		}
		else {
			class_binding(k);
			while (la == AND) {
				match(la, k);
				class_binding(k);
			}
		}
		break;
	case MODULE:
		match(la, k);
		if (la == TYPE) {
			match(la, k);
			ident(k);
			if (lexbuf == L"=") {
				match(la, k);
				module_type(k);
			}
			else {
				error();
			}

		}
		else if (la == REC) {
			match(la, k);
			match(CAPITALIZED_IDENT, k);
			match(COLON, k);
			module_type(k);
			if (lexbuf == L"=") {
				match(la, k);
				module_expr(k);
				while (la == AND) {
					match(la, k);
					match(CAPITALIZED_IDENT, k);
					match(COLON, k);
					module_type(k);
					if (lexbuf == L"=") {
						match(la, k);
						module_expr(k);
					}
					else {
						error();
					}
				}
			}
			else {
				error();
			}
		}
		else {
			match(CAPITALIZED_IDENT, k);
			while (la == LPAR) {
				match(la, k);
				match(CAPITALIZED_IDENT, k);
				match(COLON, k);
				module_type(k);
				match(RPAR, k);
			}
			if (la == COLON) {
				match(la, k);
				module_type(k);
			}
			if (lexbuf == L"=") {
				match(la, k);
				module_expr(k);
			}
			else {
				error();
			}
		}
		break;
	case OPEN:
		match(la, k);
		module_path(k);
		break;
	case INCLUDE:
		match(la, k);
		module_expr(k);
		break;
	default:
		break;
	}
}	

void let_binding(int k)
{
	if (la == MODULE) {
		match(la, k);
		match(CAPITALIZED_IDENT, k + 1);
		if (lexbuf == L"=") {
			match(la, k);
		}
		else {
			error();
		}
		module_expr(k + 1);
	}
	else {
		CIndentLexerContext context = saveContext();
		try {
			pattern(k);
			if (lexbuf == L"=") {
				match(la, k);
			}
			else {
				error();
			}
			expr(k);
		}
		catch (WinCamlException) {
			restoreContext(context);
			value_name(k);
			context = saveContext();
			try {
				match(COLON, k);
				if (la == TYPE) {
					match(la, k);
					while (la != DOT && la != YYEOF) {
						typeconstr(k + 1);
					}
					match(DOT, k + 1);
					typexpr(k + 1);
					if (lexbuf == L"=") {
						match(la, k);
					}
					else {
						error();
					}
					expr(k + 1);
				}
				else {
					poly_typexpr(k + 1);
					if (lexbuf == L"=") {
						match(la, k);
					}
					else {
						error();
					}
					expr(k);
				}
			}
			catch (WinCamlException)
			{
				restoreContext(context);
				while (la != COLON && lexbuf != L"=" && la != YYEOF) {
					parameter(k);
				}
				if (la == COLON) {
					match(la, k);
					typexpr(k);
				}
				if (lexbuf == L"=") {
					match(la, k);
				}
				else {
					error();
				}
				expr(k);
			}
		}
	}
}

void class_binding_or_spec(int k)
{
	if (la == VIRTUAL) {
		match(la, k);
	}
	if (la == LBRACKET) {
		match(la, k);
		type_parameters(k + 1);
		match(RBRACKET, k);
	}
	match(LOWERCASE_IDENT, k);
	if (lexbuf == L"=") {
		match(la, k + 1);
		class_expr(k + 1);
	}
	while (la != COLON && lexbuf != L"=" && la != ENDMARK && la != YYEOF) {
		parameter(k + 1);
	}
	if (la == COLON) {
		match(la, k + 1);
		class_type(k + 1);
	}
	if (lexbuf == L"=") {
		match(la,k + 1);
		class_expr(k + 1);
	}
}

void class_spec(int k)
{
	if (la == VIRTUAL) {
		match(la, k);
	}
	if (la == LBRACKET) {
		match(la, k);
		type_parameters(k + 1);
		match(RBRACKET, k);
	}
	match(LOWERCASE_IDENT, k + 1);
	match(COLON, k + 1);
	class_type(k + 1);
}

void value_name(int k)
{
	if (la == LOWERCASE_IDENT) {
		match(la, k);
	}
	else {
		trailing = 1;
		match(LPAR, k);
		operator_name(k + 1);
		match(RPAR, k);
	}
}

void directive_argument(int k)
{
	if (la == COMMENT) {
		match(la, k);
	}
	switch (la) {
	case STRING: case INTEGER:
		match(la, k);
		break;
	case LOWERCASE_IDENT: case CAPITALIZED_IDENT:
		value_path(k);
		break;
	default:
		break;
	}
}

void external_declaration(int k)
{
	match(STRING, k);
}

void class_binding(int k)
{
	if (la == VIRTUAL) {
		match(la, k);
	}
	if (la == LBRACKET) {
		match(la, k);
		type_parameters(k + 1);
		match(RBRACKET, k);
	}
	match(LOWERCASE_IDENT, k);
	if (lexbuf == L"=") {
		match(la, k);
		class_expr(k + 1);
	}
	else if (la == COLON) {
		match(la, k);
		class_type(k + 1);
		if (lexbuf == L"=") {
			match(la, k);
		}
		else {
			error();
		}
		class_expr(k + 1);
	}
	else {
		while (la != COLON && lexbuf != L"=" && la != YYEOF) {
			parameter(k + 1);
		}
		if (la == COLON) {
			match(la, k);
			class_type(k + 1);
			if (lexbuf == L"=") {
				match(la, k);
			}
			else {
				error();
			}
			class_expr(k + 1);
		}
		else if (lexbuf == L"=") {
			match(la, k);
			class_expr(k + 1);
		}
		else {
			error();
		}
	}
}

void classtype_def(int k)
{
	if (la == VIRTUAL){
		match(la, k);
	}
	if (la == LBRACKET) {
		match(LBRACKET, k);
		type_parameters(k + 1);
		match(RBRACKET, k);
	}
	match(LOWERCASE_IDENT, k);
	if (lexbuf == L"=") {
		match(la, k);
		class_body_type(k + 1);
	}
	else {
		error();
	}
}

void module_type(int k)
{
	begin_module_type(k);
	end_module_type(k);
}

void begin_module_type(int k)
{
	if (la == SIG) {
		match(la, k);
		while (la != END && la != YYEOF) {
			specification(k + 1);
			if (la == ENDMARK) {
				match(la, k);
			}
		}
		if (la == END) {
			match(la, k);
		}
	}
	else if (la == FUNCTOR) {
		match(la, k);
		match(LPAR, k);
		match(CAPITALIZED_IDENT, k + 1);
		match(COLON, k + 1);
		module_type(k + 1);
		match(RPAR, k);
		match(RIGHTARROW, k);
		module_type(k + 1);
	}
	else if (la == LPAR) {
		match(la, k);
		module_type(k + 1);
		match(RPAR, k);
	}
	else if (la == MODULE) {
		match(la, k);
		match(TYPE, k);
		match(OF, k);
		module_expr(k + 1);
	}
	else {
		modtype_path(k);
	}
}

void end_module_type(int k)
{
	while (la == WITH) {
		match(la, k);
		mod_constraint(k + 1);
		while (la == AND) {
			match(la, k);
			mod_constraint(k + 1);
		}
	}
}

void module_expr(int k)
{
	begin_module_expr(k);
	end_module_expr(k);
}

void begin_module_expr(int k)
{
	if (la == STRUCT) {
		match(la, k);
		while (la != END && la != YYEOF) {
			definition(k + 1);
			if (la == ENDMARK) {
				match(la, k);
			}
		}
		match(END, k);
	}
	else if (la == FUNCTOR) {
		match(la, k);
		match(LPAR, k);
		match(CAPITALIZED_IDENT, k + 1);
		match(COLON, k);
		module_type(k + 1);
		match(RPAR, k);
		match(RIGHTARROW, k + 1);
		module_expr(k + 2);
	}
	else if (la == LPAR) {
		match(la, k);
		if (la == VAL) {
			match(la, k + 1);
			expr(k + 2);
			if (la == COLON) {
				match(la, k + 1);
				package_type(k + 2);
			}
		}
		else {
			module_expr(k + 1);
			if (la == COLON) {
				match(la, k);
				module_type(k + 1);
			}
		}
		match(RPAR, k);
	}
	else {
		match(CAPITALIZED_IDENT, k);
		while (la == DOT) {
			match(la, k);
			match(CAPITALIZED_IDENT, k + 1);
		}
	}
}

void end_module_expr(int k)
{
	while (la == LPAR) {
		match(la, k);
		module_expr(k + 1);
		match(RPAR, k);
	}
}

void ident(int k)
{
	if (la == LOWERCASE_IDENT || la == CAPITALIZED_IDENT) {
		match(la, k);
	}
}

void module_path(int k)
{
	match(CAPITALIZED_IDENT, k);
	while (la == DOT) {
		match(la, k);
		if (la == CAPITALIZED_IDENT) {
			match(la, k + 1);
		}
		else {
			break;
		}

	}
}

void value_path(int k)
{
	if (la == LOWERCASE_IDENT) {
		value_name(k);
	}
	else {
		match(CAPITALIZED_IDENT, k);
		while (la == DOT) {
			match(la, k);
			if (la == CAPITALIZED_IDENT) {
				match(la, k + 1);
			}
		}
		value_name(k);
	}
}

void operator_name(int k)
{
	if (la == PREFIX_SYMBOL || la == INFIX_SYMBOL) {
		match(la, k);
	}
	else {
		if (lexbuf == L":=" || lexbuf == L"mod" || lexbuf == L"land" || lexbuf == L"lor"
			|| lexbuf == L"lxor" || lexbuf == L"lsl" || lexbuf == L"lsr" || lexbuf == L"asr") {
				match(la, k);
		}
		else {
			error();
		}
	}
}

void type_parameters(int k)
{
	match(QUOTE, k);
	ident(k);
	while (la == COMMA) {
		match(la, k);
		match(QUOTE, k);
		ident(k);
	}
}

void class_body_type(int k)
{
	if (la == OBJECT) {
		match(la, k);
		if (la == LPAR) {
			match(la, k);
			typexpr(k + 1);
			match(RPAR, k);
		}
		while (la == INHERIT || la == VAL || la == METHOD1 || la == CONSTRAINT) {
			class_field_spec(k + 1);
		}
		match(END, k);
	}
	else if (la == LBRACKET) {
		match(la, k);
		typexpr(k);
		while (la == COMMA) {
			match(la, k);
			typexpr(k + 1);
		}
		match(RBRACKET, k);
		class_path(k + 1);
	}
	else {
		class_path(k + 1);
	}
}

void parameter(int k)
{
	if (lexbuf == L"~") {
		match(la, k);
		if (la == LPAR) {
			match(la, k);
			match(LOWERCASE_IDENT, k + 1);
			if (la == COLON) {
				match(COLON, k);
				typexpr(k + 2);
			}
			match(RPAR, k);
		}
		else {
			match(LOWERCASE_IDENT, k);
			if (la == COLON) {
				match(la, k);
				pattern(k + 1);
			}
		}
	}
	else if (lexbuf == L"?") {
		match(la, k);
		if (la == LPAR) {
			match(la, k);
			match(LOWERCASE_IDENT, k + 1);
			if (la == COLON) {
				match(COLON, k + 1);
				typexpr(k + 2);
			}
			if (lexbuf == L"=") {
				match(la, k + 1);
				expr(k + 1);
			}
			match(RPAR, k);
		}
		else {
			match(LOWERCASE_IDENT, k);
			if (la == COLON) {
				match(la, k);
				if (la == LPAR) {
					match(la, k);
					pattern(k + 1);
					if (la == COLON) {
						match(la, k + 1);
						typexpr(k + 2);
					}
					if (lexbuf == L"=") {
						expr(k + 1);
					}
					match(RPAR, k);
				}
				else {
					pattern(k + 1);
				}
			}
		}
	}
	else {
		CIndentLexerContext context = saveContext();
		try {
			match(LPAR, k);
			match(TYPE, k + 1);
			match(LOWERCASE_IDENT, k + 2);
			match(RPAR, k);
		}
		catch (WinCamlException) {
			restoreContext(context);
			pattern(k);
		}
	}
}


void class_type(int k)
{
	CIndentLexerContext context = saveContext();
	try {
		if (lexbuf == L"?") {
			match(la, k);
		}
		match(LOWERCASE_IDENT, k);
		match(COLON, k);
		typexpr(k + 1);
	}
	catch (WinCamlException) {
		restoreContext(context);
		try {
			typexpr(k);
		}
		catch (WinCamlException) {
			restoreContext(context);
			class_body_type(k);
			if (la != RPAR && la != AND && la != END && lexbuf != L"=") {
				throw;
			}
		}
	}
}

void class_expr(int k)
{
	begin_class_expr(k);
	end_class_expr(k);
}

void begin_class_expr(int k)
{
	if (la == OBJECT) {
		match(la, k);
		if (la == LPAR) {
			match(la, k);
			pattern(k + 1);
			if (la == COLON) {
				match(COLON, k);
				typexpr(k + 2);
			}
			match(RPAR, k);
		}
		while (la  == INHERIT || la == VAL || la == METHOD1 || la == CONSTRAINT || la == INITIALIZER) {
			class_field(k + 1);
		}
		match(END, k);
	}
	else if (la == LET) {
		match(la, k);
		if (la == REC) {
			match(la, k);
		}
		let_binding(k);
		while (la == AND) {
			match(la, k);
			let_binding(k + 1);
		}
		match(IN1, k);
		class_expr(k + indentAfterIn);
	}
	else if (la == FUN) {
		match(la, k);
		parameter(k + 1);
		while (la != RIGHTARROW && la != YYEOF) {
			parameter(k + 1);
		}
		match(RIGHTARROW, k + 1);
		class_expr(k + 2);
	}
	else if (la == LPAR) {
		match(la, k);
		class_expr(k + 1);
		if (la == COLON) {
			match(la, k + 1);
			class_type(k + 2);
		}
		match(RPAR, k);
	}
	else if (la == LBRACKET) {
		match(la, k);
		typexpr(k + 1);
		while (la == COMMA) {
			match(la, k + 1);
			typexpr(k + 1);
		}
		match(RBRACKET, k);
		class_path(k);
	}
	else if (la == CAPITALIZED_IDENT) {
		match(la, k);
		while (la == DOT) {
			match(la, k + 1);
			if (la == CAPITALIZED_IDENT) {
				match(la, k + 1);
			}
			else {
				match(LOWERCASE_IDENT, k + 1);
				break;
			}
		}
	}
	else {
		match(LOWERCASE_IDENT, k);
	}
}

void end_class_expr(int k)
{
	if (la != AND && la != END) {
		arguments(k + 1);
	}
}

void type_def(int k)
{
	type_params(k);
	match(LOWERCASE_IDENT, k);
	type_information(k);
}

void type_params(int k)
{
	if (la == LPAR) {
		match(la, k);
		type_param(k + 1);
		while (la == COMMA) {
			match(la, k + 1);
			type_param(k + 1);
		}
		match(RPAR, k);
	}
	else {
		type_param(k);
	}
}

void type_param(int k)
{
	if (la == QUOTE) {
		match(la,k);
		ident(k + 1);
	}
	else if (lexbuf == L"+" || lexbuf == L"-") {
		match(la, k);
		match(QUOTE, k);
		ident(k + 2);
	}
}

void type_information(int k)
{
	CIndentLexerContext context = saveContext();
	try {
		type_equation(k);
		type_representation(k);
	}
	catch (WinCamlException) {
		restoreContext(context);
		try {
			type_representation(k);
		}
		catch (WinCamlException) {
			restoreContext(context);
		}
	}
	context = saveContext();
	try {
		while (la == CONSTRAINT) {
			match(la, k);
			match(QUOTE, k + 1);
			ident(k + 2);
			if (lexbuf == L"=") {
				match(la, k + 1);
				typexpr(k + 1);
			}
			else {
				error();
			}
		}
	}
	catch (WinCamlException) {
		restoreContext(context);
	}
}

void type_equation(int k)
{
	if (lexbuf == L"=") {
		match(la, k);
		if (la == PRIVATE) {
			match(la, k + 1);
			k++;
		}
		typexpr(k + 1);
	}
}

void type_representation(int k)
{
	if (lexbuf == L"=") {
		match(la, k);
		if (la == PRIVATE) {
			match(la, k);
		}
		if (la == LBRACE) {
			match(la, k);
			field_decl(k + 1);
			while (la == SEMICOLON) {
				match(la, k + 1);
				if (la != RBRACE) {
					field_decl(k + 1);
				}
			}
			match(RBRACE, k);
		}
		else {
			if (lexbuf == L"|") {
				match(la, k + indentFilter);
			}
			constr_decl(k + indentFilter + 1);
			while (lexbuf == L"|") {
				match(la, k + indentFilter);
				constr_decl(k + indentFilter + 1);
			}
		}
	}
}

void field_decl(int k)
{
	if (la == MUTABLE) {
		match(la, k++);
	}
	match(LOWERCASE_IDENT, k);
	match(COLON, k);
	poly_typexpr(k + 1);
}

void constr_decl(int k)
{
	match(CAPITALIZED_IDENT, k);
	if (la == OF) {
		match(la, k);
		typexpr(k + 1);
		while (lexbuf == L"*") {
			match(la, k + 1);
			typexpr(k + 1);
		}
	}
}

void class_field_spec(int k)
{
	if (la == INHERIT) {
		match(la, k);
		class_type(k + 1);
	}
	else if (la == VAL) {
		match(la, k++);
		if (la == MUTABLE) {
			match(la, k++);
		}
		if (la == VIRTUAL) {
			match(la, k++);
		}
		match(LOWERCASE_IDENT, k);
		match(COLON, k);
		typexpr(k);
	}
	else if (la == METHOD1) {
		match(la, k++);
		if (la == PRIVATE) {
			match(la, k++);
		}
		if (la == VIRTUAL) {
			match(la, k++);
		}
		match(LOWERCASE_IDENT, k);
		match(COLON, k);
		poly_typexpr(k + 1);
	}
	else {
		match(CONSTRAINT, k);
		typexpr(k + 1);
		if (lexbuf == L"=") {
			match(la, k + 1);
		}
		else {
			error();
		}
		typexpr(k + 2);
	}
}

void class_path(int k)
{
	if (la == CAPITALIZED_IDENT) {
		module_path(k);
	}
	match(LOWERCASE_IDENT, k);
}

void modtype_path(int k)
{
	if (la == LOWERCASE_IDENT) {
		match(la, k);
	}
	else {
		extended_module_path(k);
		if (la == DOT) {
			match(la, k + 1);
			ident(k + 1);
		}
	}
}

void extended_module_path(int k)
{
	match(CAPITALIZED_IDENT, k);
	while (la == DOT || la == LPAR) {
		if (la == DOT) {
			match(la, k + 1);
			if (la == CAPITALIZED_IDENT) {
				match(la, k + 1);
			}
			else {
				break;
			}

		}
		else {
			match(la, k + 1);
			extended_module_path(k + 2);
			match(RPAR, k + 1);
		}
	}
}

void poly_typexpr(int k)
{
	begin_poly_typexpr(k);
	end_poly_typexpr(k);
}

void begin_poly_typexpr(int k)
{
	if (la == QUOTE) {
		match(la, k);
		ident(k + 1);
		while (la == QUOTE) {
			match(la, k);
			ident(k + 1);
		}
		if (la == DOT) {
			match(la, k);
			typexpr(k + 1);
		}
	}
	else if (lexbuf == L"_") {
		match(la, k);
	}
	else if (la == LOWERCASE_IDENT) {
		match(la, k);
		if (la == COLON) {
			match(la, k);
			typexpr(k + 1);
			match(RIGHTARROW, k + 1);
			typexpr(k + 2);
		}
	}
	else if (lexbuf == L"?") {
		match(la, k);
		match(LOWERCASE_IDENT, k);
		match(COLON, k);
		typexpr(k + 1);
	}
	else if (la == CAPITALIZED_IDENT){
		extended_module_path(k);
		if (la == DOT) {
			match(la, k + 1);
		}
		match(LOWERCASE_IDENT, k + 1);
	}
	else if (la == LPAR){
		match(la, k);
		typexpr(k + 1);
		while (la == COMMA) {
			match(la, k + 1);
			typexpr(k + 1);
		}
		match(RPAR, k);
		if (la == SHARP) {
			match(la, k);
			class_path(k + 1);
		}
		else {
			typeconstr(k);
		}
	}
	else if (la == LBRACKET || la == LBRACKETSUP) {
		match(la, k);
		if (lexbuf == L"|") {
			match(la, k + 1);
		}
		tag_spec(k + 2);
		while (lexbuf == L"|") {
			match(la, k + 1);
			tag_spec(k + 2);
		}
		match(RBRACKET, k);
	}
	else if (la == LSTREAM) {
		match(la, k);
		if (lexbuf == L"|") {
			match(la, k + 1);
		}
		tag_spec_full(k + 2);
		while (lexbuf == L"|") {
			match(la, k + 1);
			tag_spec_full(k + 2);
		}
		if (lexbuf == L">") {
			match(la, k + 1);
			match(BACKQUOTE, k + 1);
			match(CAPITALIZED_IDENT, k + 2);
			while (la == BACKQUOTE) {
				match(la, k + 1);
				match(CAPITALIZED_IDENT, k + 2);
			}
		}
		match(RBRACKET, k);
	}
	else if (lexbuf == L"<") {
		match(la, k);
		if (la == LOWERCASE_IDENT) {
			method_type(k + 1);
			while (la == SEMICOLON) {
				match(la, k + 1);
				if (la == LOWERCASE_IDENT) {
					method_type(k + 1);
				}
				else if (lexbuf == L"..") {
					match(la, k + 1);
				}
				else {
					error();
				}
			}
			if (lexbuf == L">") {
				match(la, k);
			}
			else {
				error();
			}
		}
		else {
			if (lexbuf == L"..") {
				match(la, k + 1);
			}
			if (lexbuf == L">") {
				match(la, k);
			}
			else {
				error();
			}
		}
	}
	else if (la == SHARP) {
		match(la, k);
		class_path(k + 1);
	}
	else {
		error();
	}
}

void end_poly_typexpr(int k)
{
	while (la == SHARP || la == RIGHTARROW || la == AS || la == LOWERCASE_IDENT || la == CAPITALIZED_IDENT || lexbuf == L"*") {
		if (la == SHARP) {
			match(la, k);
			class_path(k + 1);
		}
		else if (la == RIGHTARROW) {
			match(la, k);
			CIndentLexerContext context = saveContext();
			try {
				typexpr(k + 1);
			}
			catch (WinCamlException) {
				restoreContext(context);
				class_type(k + 1);
			}
		}
		else if (la == AS) {
			match(la, k);
			match(QUOTE, k + 1);
			ident(k + 2);
		}
		else if (la == LOWERCASE_IDENT || la == CAPITALIZED_IDENT) {
			typeconstr(k);
		}
		else if (lexbuf == L"*") {
			match(la, k);
			typexpr(k + 1);
		}
	}
}

void class_field(int k)
{
	if (la == INHERIT) {
		match(la, k);
		class_expr(k + 1);
		if (la == AS) {
			match(la, k);
			value_name(k + 1);
		}
	}
	else if (la == VAL) {
		match(la, k++);
		if (la == MUTABLE) {
			match(la, k++);
		}
		if (la == VIRTUAL) {
			match(la, k++);
			match(LOWERCASE_IDENT, k);
			match(COLON, k);
			typexpr(k + 1);
		}
		else {
			match(LOWERCASE_IDENT, k);
			if (la == COLON) {
				match(la, k);
				typexpr(k + 1);
			}
			if (lexbuf == L"=") {
				match(la, k);
				expr(k + 1);
			}
			else {
				error();
			}
		}
	}
	else if (la == METHOD1) {
		match(la, k++);
		if (la == PRIVATE) {
			match(la, k++);
		}
		if (la == VIRTUAL) {
			match(la, k++);
			match(LOWERCASE_IDENT, k);
			match(COLON, k);
			poly_typexpr(k + 1);
		}
		else {
			match(LOWERCASE_IDENT, k);
			while (la != COLON && lexbuf != L"=" && la != YYEOF) {
				parameter(k + 1);
			}
			if (la == COLON) {
				match(la, k);
				poly_typexpr(k + 1 );
			}
			if (lexbuf == L"=") {
				match(la, k);
				expr(k + 1);
			}
			else {
				error();
			}
		}
	}
	else if (la == CONSTRAINT) {
		match(CONSTRAINT, k);
		typexpr(k);
		if (lexbuf == L"=") {
			match(la, k);
			typexpr(k);
		}
	}
	else {
		match(INITIALIZER, k);
		expr(k + 1);
	}
}

void typexpr(int k)
{
	begin_typexpr(k);
	end_poly_typexpr(k);
}

void begin_typexpr(int k)
{
	if (la == QUOTE) {
		match(la, k);
		ident(k + 1);
	}
	else if (lexbuf == L"_") {
		match(la, k);
	}
	else if (la == LOWERCASE_IDENT) {
		match(la, k);
		if (la == COLON) {
			match(la, k);
			typexpr(k + 1);
		}
	}
	else if (lexbuf == L"?") {
		match(la, k);
		match(LOWERCASE_IDENT, k);
		match(COLON, k);
		typexpr(k + 1);
	}
	else if (la == CAPITALIZED_IDENT){
		extended_module_path(k);
		if (la == DOT) {
			match(la, k + 1);
		}
		match(LOWERCASE_IDENT, k + 1);
	}
	else if (la == LPAR){
		match(la, k);
		if (la == MODULE) {
			match(la, k + 1);
			package_type(k + 1);
			match(RPAR, k);
		}
		else {
			typexpr(k + 1);
			while (la == COMMA) {
				match(la, k + 1);
				typexpr(k + 1);
			}
			match(RPAR, k);
			if (la == SHARP) {
				match(la, k);
				class_path(k + 1);
			}
			else {
				typeconstr(k);
			}
		}
	}
	else if (la == LBRACKET || la == LBRACKETSUP) {
		match(la, k++);
		if (lexbuf == L"|") {
			match(la, k);
		}
		tag_spec(k + 1);
		while (lexbuf == L"|") {
			match(la, k);
			tag_spec(k + 1);
		}
		match(RBRACKET, --k);
	}
	else if (la == LSTREAM) {
		match(la, k++);
		if (lexbuf == L"|") {
			match(la, k);
		}
		tag_spec_full(k + 1);
		while (lexbuf == L"|") {
			match(la, k);
			tag_spec_full(k + 1);
		}
		if (lexbuf == L">") {
			match(la, k);
			match(BACKQUOTE, k);
			match(CAPITALIZED_IDENT, k + 1);
			while (la == BACKQUOTE) {
				match(la, k);
				match(CAPITALIZED_IDENT, k + 1);
			}
		}
		match(RBRACKET, --k);
	}
	else if(lexbuf == L"<") {
		match(la, k++);
		if (la == LOWERCASE_IDENT) {
			method_type(k);
			while (la == SEMICOLON) {
				match(la, k);
				if (la == LOWERCASE_IDENT) {
					method_type(k);
				}
				else if (lexbuf == L"..") {
					match(la, k);
				}
				else {
					error();
				}
			}
			if (lexbuf == L">") {
				match(la, --k);
			}
			else {
				error();
			}
		}
		else {
			if (lexbuf == L"..") {
				match(la, k);
			}
			if (lexbuf == L">") {
				match(la, --k);
			}
			else {
				error();
			}
		}
	}
	else if (la == SHARP) {
		match(la, k);
		class_path(k + 1);
	}
	else {
		error();
	}
}

void mod_constraint(int k)
{
	CIndentLexerContext context = saveContext();
	try {
		if (la == TYPE) {
			match(la, k++);
			if (la == QUOTE) {
				type_parameters(k);
			}
			typeconstr(k);
			if (lexbuf == L"=") {
				match(la, k);
				typexpr(++k);
			}
			else {
				error();
			}
		}
		else {
			match(MODULE, k++);
			module_path(k);
			if (lexbuf == L"=") {
				match(la, k);
				extended_module_path(++k);
			}
			else {
				error();
			}
		}
	}
	catch (WinCamlException) {
		restoreContext(context);
		if (la == TYPE) {
			match(la, k++);
			if (la == QUOTE) {
				type_parameters(k);
			}
			match(LOWERCASE_IDENT, k);
			if (lexbuf == L":=") {
				match(la, k++);
				if (la == QUOTE) {
					type_parameters(k);
				}
				typeconstr(k);
			}
			else {
				error();
			}
		}
		else {
			match(MODULE, k++);
			match(CAPITALIZED_IDENT, k);
			if (lexbuf == L":=") {
				match(la, k++);
				extended_module_path(k);
			}
			else {
				error();
			}
		}
	}
}

void typeconstr(int k)
{
	if (la == CAPITALIZED_IDENT) {
		extended_module_path(k);
		if (la == DOT) {
			match(la, k++);
		}
		match(LOWERCASE_IDENT, k);
	}
	else if (la == LOWERCASE_IDENT) {
		match(la, k);
	}
}

void tag_spec(int k)
{
	if (la == BACKQUOTE) {
		match(la, k++);
		match(CAPITALIZED_IDENT, k);
		if (la == OF) {
			match(la, k++);
			typexpr(k);
		}
	}
	else {
		typexpr(k);
	}
}

void tag_spec_full(int k)
{
	if (la == BACKQUOTE) {
		match(la, k++);
		match(CAPITALIZED_IDENT, k);
		if (la == OF) {
			match(la, k++);
			typexpr(k);
		}
		while (lexbuf == L"&") {
			match(la, k);
			typexpr(++k);
		}
	}
	else {
		typexpr(k);
	}
}

void method_type(int k)
{
	match(LOWERCASE_IDENT, k);
	match(COLON, k++);
	poly_typexpr(k);
}

void stream_component(int k)
{
	if (la == QUOTE) {
		match(la, k++);
	}
	simple_expr(k);
}

void stream_matching(int k)
{
	if (lexbuf == L"|") {
		match(la, k);
	}
	stream_pattern(k + 1);
	if (la != RIGHTARROW) {
		pattern(k);
	}
	match(RIGHTARROW, k);
	expr(k + 1);
	while (lexbuf == L"|") {
		match(la, k);
		stream_pattern(k + 1);
		if (la != RIGHTARROW) {
			pattern(k);
		}
		match(RIGHTARROW, k);
		expr(k + 1);
	}
}

void stream_pattern(int k)
{
	match(LSTREAM, k);
	if (la != RSTREAM) {
		stream_pat_comp(k + 1);
		while (la == SEMICOLON) {
			match(la, k + 1);
			stream_pat_comp(k + 1);
			if (lexbuf == L"??") {
				match(la, k + 1);
				expr(k + 2);
			}
		}
	}
	if (la == SEMICOLON) {
		match(la, k + 1);
	}
	match(RSTREAM, k);
}

void stream_pat_comp(int k)
{
	if (la == QUOTE) {
		match(la, --k);
		pattern(k + 1);
		if (la == WHEN) {
			match(la, k + 1);
			expr(k + 2);
		}
	}
	else {
		CIndentLexerContext context = saveContext();
		try {
			pattern(k + 1);
			if (lexbuf == L"=") {
				match(la, k + 1);
			}
			else {
				error();
			}
			simple_expr(k + 2);
		}
		catch (WinCamlException) {
			restoreContext(context);
			ident(k + 1);
		}
	}
}

bool infix_op(int la)
{
	return ((la == INFIX_SYMBOL && lexbuf != L"|") || lexbuf == L"*" || lexbuf == L"="
		|| lexbuf == L"&" || lexbuf == L"or" || lexbuf == L":="
		|| lexbuf == L"mod" || lexbuf == L"land" || lexbuf == L"lor"
		|| lexbuf == L"lxor" || lexbuf == L"lsl" || lexbuf == L"lsr"
		|| lexbuf == L"asr");
}

void error()
{
	throw wce;
}

void pattern(int k)
{
	if (lexbuf == L"|") {
		match(la, k);
	}
	simple_pattern(k);
	while (lexbuf == L"|") {
		match(la, k);
		simple_pattern(k);
	};
}

void simple_pattern(int k)
{
	begin_simple_pattern(k);
	end_simple_pattern(k);
}

void begin_simple_pattern(int k)
{
	if (la == LOWERCASE_IDENT) {
		match(la, k);
	}
	else if (la == CAPITALIZED_IDENT) {
		match(la, k);
		while (la == DOT) {
			match(DOT, k + 1);
			match(CAPITALIZED_IDENT, k + 1);
		}
		CIndentLexerContext context = saveContext();
		try {
			simple_pattern(k + 1);
		}
		catch (WinCamlException) {
			restoreContext(context);
		}
	}
	else if (la == LPAR) {
		match(la, k++);
		if (la == MODULE) {
			match(la, k);
			match(CAPITALIZED_IDENT, k + 1);
			if (la == COLON) {
				match(la, k + 1);
				package_type(k + 1);
			}
		}
		else {
			CIndentLexerContext context = saveContext();
			try {
				pattern(k);
				if (la == COLON) {
					match(la, k);
					typexpr(k + 1);
				}
			}
			catch (WinCamlException) {
				restoreContext(context);
				operator_name(k + 1);
			}
		}
		match(RPAR, --k);
	}
	else if (lexbuf == L"_") {
		match(la, k);
	}
	else if (la == INTEGER || la == FLOAT || la == STRING || la == BOOLVALUE || la == EMPTYLIST || la == UNIT) {
		match(la, k);
	}
	else if (la == CHAR) {
		match(la, k);
		if (la == DOTDOT) {
			match(la, k + 1);
			match(CHAR, k);
		}
	}
	else if (la == BACKQUOTE) {
		match(la, k);
		match(CAPITALIZED_IDENT, k + 1);
		CIndentLexerContext context = saveContext();
		try {
			simple_pattern(k + 1);
		}
		catch (WinCamlException) {
			restoreContext(context);
		}
	}
	else if (la == SHARP) {
		match(la, k);
		match(LOWERCASE_IDENT, k + 1);
	}
	else if (la == LBRACE) {
		match(la, k++);
		field(k);
		if (lexbuf == L"=") {
			match(la, k);
			pattern(k + 1);
		}
		while (la == SEMICOLON) {
			match(la, k);
			if (lexbuf == L"_") {
				match(la, k + 1);
				break;
			}
			field(k);
			if (lexbuf == L"=") {
				match(la, k);
				pattern(k + 1);
			}
		}
		match(RBRACE, --k);
	}
	else if (la == LBRACKET) {
		match(la,k++);
		pattern(k);
		while (la == SEMICOLON) {
			match(la, k);
			pattern(k);
		}
		match(RBRACKET, --k);
	}
	else if (la == LLIST) {
		match(la,k++);
		pattern(k);
		while (la == SEMICOLON) {
			match(la, k);
			pattern(k);
		}
		match(RLIST, --k);
	}
	else {
		error();
	}
}

void end_simple_pattern(int k)
{
	while (la == AS || la == COMMA || la == COLONCOLON) {
		match(la, k);
		simple_pattern(k);
	}
}

void field(int k)
{
	class_path(k);
}

void pattern_matching(int k)
{
	k += indentFilter;

	if (lexbuf == L"|") {
		match(la, k);
	}
	pattern(k + 1);
	if (la == WHEN) {
		match(la, k + 1);
		expr(k + 2);
	}
	match(RIGHTARROW, k + 1);
	expr(k + 2);
	while (lexbuf == L"|") {
		match(la, k);
		pattern(k + 1);
		if (la == WHEN) {
			match(la, k + 1);
			expr(k + 1);
		}
		match(RIGHTARROW, k + 1);
		expr(k + 2);
	}
}

void multiple_matching(int k)
{
	k += indentFilter;
	parameter(k);
	while (la != WHEN && la != RIGHTARROW && la != 0) {
		parameter(k);
	}
	if (la == WHEN) {
		match(la, k);
		expr(k + 1);
	}
	match(RIGHTARROW, k + 1);
	expr(k + 2);
}

void class_body(int k)
{
	if (la == LPAR) {
		match(la, k++);
		pattern(k);
		if (la == COLON) {
			match(la, COLON);
			typexpr(k);
		}
		match(RPAR, --k);
	}
	while (la == INHERIT || la == VAL || la == METHOD1 || la == CONSTRAINT || la == INITIALIZER) {
		class_field(k);
	}
}

void expr(int k)
{
	simple_expr(k);
	while (la == SEMICOLON) {
		match(la, k);
		CIndentLexerContext context = saveContext();
		try {
			simple_expr(k);
		}
		catch (WinCamlException) {
			restoreContext(context);
		}
	}
}

void simple_expr(int k)
{
	if (infix_op(la)) {
		match(la, k);
	}
	very_simple_expr(k);
	arguments(k + 1);
	CIndentLexerContext context = saveContext();
	try {
		while (infix_op(la)) {
			match(la, k);
			context = saveContext();
			very_simple_expr(k);
			arguments(k + 1);
		}
	}
	catch (WinCamlException) {
		restoreContext(context);
	}
}

void very_simple_expr(int k)
{
	if (infix_op(la) && lexbuf == L"-") {
		match(la, k);
	}
	if (la == LOWERCASE_IDENT) {
		match(la, k);
		if (lexbuf == L"<-") {
			match(la, k + 1);
			expr(k + 2);
		}
	}
	else if (la == CAPITALIZED_IDENT) {
		match(la, k++);
		while (la == DOT) {
			match(la, k);
			if (la == CAPITALIZED_IDENT) {
				match(la, k);
			}
			else {
				if (la == LOWERCASE_IDENT) {
					match(la, k);
				}
				else {
					trailing = 1;
					match(LPAR, k);
					CIndentLexerContext context = saveContext();
					try {
						operator_name(k + 1);
					}
					catch (WinCamlException) {
						restoreContext(context);
						expr(k + 1);
					}
					match(RPAR, k);
				}
				break;
			}
		}
	}
	else if (la == INTEGER || la == FLOAT || la == CHAR || la == STRING || la == BOOLVALUE || la == EMPTYLIST || la == UNIT) {
		match(la, k);
	}
	else if (la == LPAR) {
		match(la, k++);
		if (la == MODULE)
		{
			match(la, k);
			module_expr(k + 1);
			if (la == COLON) {
				match(la, k + 1);
				package_type(k + 1);
			}
		}
		else
		{
			CIndentLexerContext context = saveContext();
			try {
				expr(k);
				if (la == COLON) {
					match(la, k);
					typexpr(k + 1);
				}
				if (la == COLONSUP) {
					match(la, k);
					typexpr(k + 1);
				}
			}
			catch (WinCamlException) {
				restoreContext(context);
				operator_name(k + 1);
			}
		}
		match(RPAR, --k);
	}
	else if (la == BACKQUOTE) {
		match(la, k++);
		match(CAPITALIZED_IDENT, k);
		CIndentLexerContext context = saveContext();
		try {
			expr(k + 1);
		}
		catch (WinCamlException) {
			restoreContext(context);
		}
	}
	else if (la == BEGIN1) {
		match(la, k);
		expr(k + 1);
		match(END, k);
	}
	else if (la == LBRACKET) {
		match(la, k++);
		expr(k);
		while (la == SEMICOLON) {
			match(la, k);
			if (la != RBRACKET) {
				expr(k);
			}
		}
		match(RBRACKET, --k);
	}
	else if (la == LSTREAM) {
		match(la, k++);
		if (la != RSTREAM) {
			stream_component(k);
			while (la == SEMICOLON) {
				match(la, k);
				if (la != RSTREAM) {
					stream_component(k);
				}
				else {
					break;
				}
			}
		}
		match(RSTREAM, --k);
	}
	else if (la == PARSER) {
		match(la, k);
		if (la != LSTREAM && lexbuf != L"|") {
			pattern(k + 1);
		}
		stream_matching(k + indentFilter);
	}
	else if (la == LLIST) {
		match(la, k++);
		expr(k);
		while (la == SEMICOLON) {
			match(la, k);
			if (la != RLIST) {
				expr(k);
			}
		}
		match(RLIST, --k);
	}
	else if (la == LBRACE) {
		match(la, k++);
		CIndentLexerContext context = saveContext();
		try {
			expr(k);
			match(WITH, k);
			field(k + 1);
			if (lexbuf == L"=") {
				match(la, k + 1);
				expr(k + 1);
			}
			while (la == SEMICOLON) {
				match(la, k + 1);
				if (la != RBRACE) {
					field(k + 1);
					if (lexbuf == L"=") {
						match(la, k + 1);
						expr(k + 1);
					}
				}
			}
		}
		catch(WinCamlException) {
			restoreContext(context);
			field(k);
			if (lexbuf == L"=") {
				match(la, k);
				simple_expr(k + 1);
			}
			while (la == SEMICOLON) {
				match(la, k);
				if (la != RBRACE) {
					field(k);
					if (lexbuf == L"=") {
						match(la, k);
						simple_expr(k + 1);
					}
				}
			}
		}
		match(RBRACE, --k);
	}
	else if (la == PREFIX_SYMBOL) {
		match(la, k);
		very_simple_expr(k);
	}
	else if (la == IF) {
		match(la, k);
		expr(k + 1);
		match(THEN, k);
		if (la == BEGIN1 || la == LPAR) {
			simple_expr	(k);
		}
		else {
			simple_expr(k + 1);
		}
		if (la == ELSE) {
			match(la, k);
			if (la == IF || la == BEGIN1 || la == LPAR) {
				simple_expr(k);
			}
			else {
				simple_expr(k + 1);
			}
		}
	}
	else if (la == WHILE) {
		match(la, k);
		expr(k + 1);
		match(DO, k);
		expr(k + 1);
		match(DONE, k);
	}
	else if (la == FOR) {
		match(la, k++);
		ident(k);
		if (lexbuf == L"=") {
			match(la, k);
		}
		else {
			error();
		}
		expr(k + 1);
		if (la == TO) {
			match(la, --k);
		}
		else {
			match(DOWNTO, --k);
		}
		expr(k + 1);
		match(DO, k);
		expr(k + 1);
		match(DONE, k);
	}
	else if (la == MATCH) {
		match(la, k);
		expr(k + 1);
		match(WITH, k);
		if (la != PARSER) {
			pattern_matching(k);
		}
		else {
			match(la, k + 1);
			if (la != LSTREAM && lexbuf != L"|") {
				pattern(k + 1);
			}
			stream_matching(k + 1 + indentFilter);
		}
	}
	else if (la == FUNCTION) {
		match(la, k);
		pattern_matching(k);
	}
	else if (la == FUN) {
		match(la, k);
		multiple_matching(k);
	}
	else if (la == TRY1) {
		match(la, k);
		expr(k + 1);
		match(WITH, k);
		pattern_matching(k);
	}
	else if (la == LET) {
		match(la, k);
		if (la == OPEN) {
			match(la, k);
			module_path(k + 1);
			match(IN1, k);
			expr(k + indentAfterIn);
		}
		else
		{
			if (la == REC) {
				match(la, k);
			}
			let_binding(k + 1);
			while (la == AND) {
				match(la, k);
				let_binding(k + 1);
			}
			match(IN1, k);
			expr(k + indentAfterIn);
		}
	}
	else if (la == NEW) {
		match(la, k);
		class_path(k + 1);
	}
	else if (la == OBJECT) {
		match(la, k);
		class_body(k + 1);
		match(END, k);
	}
	else if (la == LOBJECT) {
		match(la, k++);
		if (la == LOWERCASE_IDENT) {
			match(LOWERCASE_IDENT, k);
			if (lexbuf == L"=") {
				match(la, k);
			}
			else {
				error();
			}
			expr(k + 1);
			while (la == SEMICOLON) {
				match(la, k + 1);
				if (la != ROBJECT) {
					match(LOWERCASE_IDENT, k + 1);
					if (lexbuf == L"=") {
						match(la, k + 1);
					}
					else {
						error();
					}
					expr(k + 1);
				}
			}
		}
		match(ROBJECT, --k);
	}

	else if (la == ASSERT1) {
		match(la, k);
		expr(k + 1);
	}
	else {
		match(LAZY, k);
	}
	end_very_simple_expr(k);
}

void end_very_simple_expr(int k)
{
	while (true) {
		if (la == DOT) {
			match(la, k);
			if (la == LPAR) {
				match(la, k);
				expr(k + 1);
				match(RPAR, k);
				if (lexbuf == L"<-") {
					match(la, k + 1);
					simple_expr(k + 2);
				}
			}
			else if (la == LBRACKET) {
				match(la, k);
				expr(k + 1);
				match(RBRACKET, k);
				if (lexbuf == L"<-") {
					match(la, k + 1);
					simple_expr(k + 2);
				}
			}
			else {
				field(k);
				if (lexbuf == L"<-") {
					match(la, k + 1);
					simple_expr(k + 2);
				}
			}
		}
		else if (la == SHARP) {
			match(la, k);
			match(LOWERCASE_IDENT, k + 1);
		}
		else if (la == COLONCOLON) {
			match(la, k);
			simple_expr(k);
		}
		else if (la == COMMA) {
			match(la, k);
			simple_expr(k);
		}
		else {
			break;
		}
	}
}	

void arguments(int k)
{
	while (true) {
		CIndentLexerContext context = saveContext();
		try {
			argument(k);
		}
		catch (WinCamlException) {
			restoreContext(context);
			break;
		}
	}
}

void argument(int k)
{
	if (lexbuf == L"~" || lexbuf == L"?") {
		match(la, k);
		match(LOWERCASE_IDENT, k);
		if (la == COLON) {
			match(la, k);
			very_simple_expr(k);
		}
	}
	else {
		very_simple_expr(k);
	}
}

void package_type(int k)
{
	modtype_path(k);
	if (la == WITH) {
		match(la, k);
		package_constraint(k + 1);
		while (la == AND) {
			match(la, k);
			package_constraint(k + 1);
		}
	}
}

void package_constraint(int k)
{
	match(TYPE, k);
	typeconstr(k + 1);
	if (lexbuf == L"=") {
		match(la, k + 1);
	}
	else {
		error();
	}
	typexpr(k + 1);
}	
