/*

CLASS {Parser|_of_source},
UNTESTED, RECURSIVE : 

IMPORT: {Compiler}, {Type}, {Variable}, {Function},
		{Expression}, {Localize_str}, {E_op} ;
		
IMPORT: {Text_parser}, {String_functions}, {File_path}, {Text_file} ;

BASE CLASS {Text_parser}


*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AL4Compiler
{
    public class Parser
    {
        public Parser()
        {
        }

        /*
        
        {Type}      Class_|where_from_
        
        METHOD Compiler|_object ==> {Compiler} :
			RESULT = Compiler_.{Compiler} .
			
		OVERRIDE Err(STR M|essage_text)
			:
			CASE Compiler.{Compiler}.Current_fun.Dummy_compile ? ==> ;
			BASE(M) .
		
		OVERRIDE Warn(STR M|essage_text)
			:
			CASE Compiler.{Compiler}.Current_fun.Dummy_compile ? ==> ;
			BASE(M) .
			
		METHOD Style_warn|ing(STR M|essage_text)
			:
			{Compiler} comp|iler = Compiler
			CASE comp.Current_fun.Dummy_compile ? ==> ;
			PUSH comp.Is_style_warn = TRUE : Warn(M) ; .
			
		METHOD Index_warn|ing(STR M|essage_text)
			:
			{Compiler} comp|iler = Compiler
			CASE comp.Current_fun.Dummy_compile ? ==> ;
			PUSH comp.Is_indexing_warn = TRUE : Warn(M) ; .
        */
        public Compiler Compiler_ = null;
        public Type Class_ = null;
        public List<string> Source = new List<string>();
        public int Line;
        public int Pos;

        public string Next_char()
        {
            string Result = "";
            if (Pos <= Last() && Source.Count > Line)
                Result = Source[Line][Pos] + "";
            else Result = (Char)0 + "";
            return Result;
        }

        public string Rest_from_pos
        {
            get
            {
                return Source[Line].Substring(Pos, Source[Line].Length - Pos);
            }
        }
        
        public int Last()
        {
            if (Line >= Source.Count) return 0;
            return Source[Line].Length - 1;
        }

        public void Skip_char()
        {
            Pos++;
        }
        
        public void Skip_sp()
        {
            for (int i = Pos; i <= Last() + 1; i++)
            {
                Pos = i;
                if (" \t\r\n".Contains(Next_char())) { }
                else break;
            }
        }
        
        public string Skip_to(string Sym)
        {
            int from = Pos;
            for (int i = Pos; i <= Source[Line].Length - Sym.Length; i++)
            {
                Pos = i;
                if (Source[Line].Substring(Pos, Sym.Length) == Sym)
                    return Source[Line].Substring(from, Pos - from);
            }
            return Source[Line].Substring(from, Source[Line].Length - from);
        }

        public bool Ending_incl = false;

        public string String_const(bool Single)
        {
            if (Symbol("@")) Ending_incl = true;
            string Result = "";
            bool ok = false;
            try
            {
                for (int i = Pos; i <= Last(); i++)
                {
                    for (int y = Pos; y <= Last(); y++)
                    {
                        if (End_of_line()) break;
                        if (Ending_incl && "\r\n".Contains(Next_char()))
                            Result += Next_char();
                        if ((Next_char() + (char)0)[0] <= ' ') Pos++;
                        else break;
                    }
                    switch (End_of_line())
                    {
                        case true: goto label_end_i;
                    }
                    switch (Next_char()[0])
                    {
                        case '\"': Result = Result + string_from_Pos();
                            ok = true; break;
                        case '#':
                            ok = true;
                            Pos++;
                            switch ("0123456789".Contains(Next_char()))
                            {
                                case true: Result = Result + (Char)Number(); break;
                                default: Result = Result + Encoded_char(); break;
                            }
                            break;
                    }
                    switch (Single)
                    {
                        case true: return Result;
                    }
                }
            }
            finally
            {
                if (!ok) Err("Waiting for string constant but: " + Next20());
            }
        label_end_i: return Result;
        }

        private string string_from_Pos()
        {
            string Result = "";
            int from = Pos + 1;
            for (int j = from; j <= Source[Line].Length; j++)
            {
                Pos = j;
                switch (Next_char()[0])
                {
                    case '\"': Result = Source[Line].Substring(from, Pos++ - from);
                        return Result;
                }
            }
            Err("String constant is not finished");
            return Result;
        }

        public string Encoded_char()
        {
            string Result = "";
            string id = Ident();
            bool sw_ok = false;
            if (id != "")
                switch (id[0])
                {
                    case 'N': if (id == "NL")
                        {
                            Result = "" + (Char)13 + (Char)10;
                            sw_ok = true;
                        }
                        break;
                    case 'L': if (id == "LF")
                        {
                            Result = "" + (Char)10;
                            sw_ok = true;
                        }
                        break;
                    case 'C': if (id == "CR")
                        {
                            Result = "" + (Char)13;
                            sw_ok = true;
                        } break;
                    case 'T': if (id == "TAB")
                        {
                            Result = "" + (Char)9;
                            sw_ok = true;
                        }
                        break;
                    case 'B': if (id == "BK")
                        {
                            Result = "" + (Char)8;
                            sw_ok = true;
                        }
                        else if (id == "BELL")
                        {
                            Result = "" + (Char)7;
                            sw_ok = true;
                        }
                        break;
                    case 'S': if (id == "SP")
                        {
                            Result = " ";
                            sw_ok = true;
                        }
                        break;
                    case 'Q': if (id == "QU")
                        {
                            Result = "\"";
                            sw_ok = true;
                        }
                        break;
                }
            if (!sw_ok)
                Err("Unknown char code: " + id);
            return Result;
        }

        static int CNT_Ident;

        public string Ident()
        {
            if (++CNT_Ident == 13147)
            { }

            string Result = "";
            Skip_sp();
            int from = Pos;
            switch (Is_letter() || Next_char() == "_")
            {
                case false: Err("Not an identifier: " + Next20()); break;
            }
            for (int i = Pos; i <= Last()+1; i++)
            {
                Pos = i;
                switch (Is_letter() || Pos < Last()+1 && Source.Count > Line && "01234567890_".Contains(Source[Line][Pos]))
                {
                    case false: goto end_for_i;
                }
            }
            end_for_i: Result = Source.Count > Line && from < Source[Line].Length ? Source[Line].Substring(from, Pos - from) : "";
            return Result;
        }

        public void Scan_names(List<string> Names)
        {
            Names.Add(Ident());
            if (Symbol("|"))
            {
                string s = Names[Names.Count - 1];
                Names.Add(s + Ident());
            }
        }

        public void Check_names(List<string> Names)
        {
            if (Names[Names.Count - 1].Length < 8) {
            	if (next_is_indexing()) return;
                Err("Full name length must exceed 7 characters: " +
                    Names[Names.Count - 1]);
            }
            if (Names.Count > 1)
            {
                string s1 = Names[Names.Count - 2];
                string s2 = Names[Names.Count - 1];
                if (s1 != "" && s2 != "" && (s1[s1.Length - 1] == '_') != (s2[s2.Length - 1] == '_'))
                    Err("Both short and long names must be ended with or without trailing '_'");
            }
        }
        
        public bool next_is_indexing() 
        {
        	bool result = Next_symbol("INDEXING");
        	if (result) return true;
        	int save_pos = Pos;
        		result = Symbol("[");
        		if (result) {
        			if (Symbol("*")) {}
        			result = Symbol("]");
        			result = Symbol("INDEXING");
        		}
        	Pos = save_pos;
        	return result;
        }

        public void Check_reserved(List<string> Names)
        {
            foreach (string n in Names)
            {
                bool yes = false;
                switch ((n + (char)0)[0])
                {
                    case 'T':
                        if (n == "THIS" || n == "TRUE" || n == "TO") yes = true; break;
                    case 'R':
                        if (n == "RESULT" || n == "REAL") yes = true; break;
                    case 'F':
                        if (n == "FOR" || n == "FOREVER" || n == "FUN" || n == "FUNCTION" || n == "FALSE")
                            yes = true;
                        break;
                    case 'S':
                        if (n == "CASE" || n == "STR" || n == "STRING" || n == "STOP") yes = true;
                        break;
                    case 'P':
                        if (n == "PUSH") yes = true;
                        break;
                    case 'C':
                        if (n == "CONTINUE" || n == "CLASS") yes = true;
                        break;
                    case 'B':
                        if (n == "BREAK" || n == "BOOL" || n == "BOOLEAN") yes = true;
                        break;
                    case 'I':
                        if (n == "INT" || n == "INTEGER" || n == "INDEX") yes = true;
                        break;
                    case 'D':
                        if (n == "DOWNTO" || n == "DATA") yes = true;
                        break;
                }
                if (yes)
                    Err("Re-using reserved keyword: " + n);
            }
        }

        public void Check_local_names(List<string> Names)
        {
            foreach (string n in Names)
            {
                if (((n + (char)0)[0] + "").ToUpper() == (n + (char)0)[0] + "")
                    Warn("It is not recommended to use " +
                        "names starting from uppercase letter for local variables");
                if (n.Length > 0 && n[n.Length - 1] == '_')
                    Err("Declaring local variable as a weak reference to an object " +
                        "using trailing '_' is not allowed");
            }
        }

        public string Scan_type()
        {
            const string obr = "{";
            const string cbr = "}";
            string Result = "";
            if (Symbol(obr))
            {
                Result = obr + Ident() + cbr;
                Wait(cbr);
                int before_try = Pos;
                if (Symbol(".") && Symbol(obr))
                {
                    Result += "." + obr + Ident() + cbr;
                    Wait(cbr);
                }
                else
                    Pos = before_try;
            }
            else Result = Ident();
            return Result;
        } 

        const char obr = '{';
        const char cbr = '}';

        public string Class_name()
        {
            Wait("" + obr);
            string Result = "" + obr + Ident() + cbr;
            Wait("" + cbr);
            return Result;
        }

        public int Number()
        {
            try
            {
                if (!"0123456789".Contains(Next_char()))
                    Err("Not an integer number: " + Next20());
                string nx = Line < Source.Count && Source[Line].Length > Pos + 1 ?
                      Source[Line].Substring(Pos, 2).ToLower() : "";
                if (nx == "0x")
                {
                    Pos += 2;
                    return Radix_number(16);
                }
                if (nx == "0b")
                {
                    Pos += 2;
                    return Radix_number(2);
                }
                if (nx == "0o")
                {
                    Pos += 2;
                    return Radix_number(8);
                }
                return Radix_number(10);
            }
            catch
            { return 0; }
        }
        
        public int Radix_number(int Base)
        {
            int Result = 0;
            Skip_sp();
            bool bad_digit = false;
            bool told = false;
            bool fin = false;
            for (int i = Pos; i <= Last() + 1 && !fin; i++)
            {
                Pos = i;
                char c = (Next_char() + (char)0)[0];
                switch (c)
                {
                    case '_': break;
                    case '0': case '1': case '2': case '3': case '4': case '5': case '6':
                    case '7': case '8': case '9':
                        Result = Result * Base + (int)(Next_char() + " ")[0] - (int)'0';
                        bad_digit = (int)c - (int)'0' >= Base;
                        break;
                    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
                        Result = Result * Base + (int)(Next_char() + " ")[0] + 10 - (int)'A';
                        char nx = (Next_char() + (char)0)[0];
                        bad_digit = (int)nx - (int)'A' + 10 >= Base;
                        break;
                    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
                        Result = Result * Base + (int)(Next_char() + " ")[0] + 10 - (int)'A';
                        nx = (Next_char() + (char)0)[0];
                        bad_digit = (int)nx - (int)'a' + 10 >= Base;
                        break;
                    default: fin = true; break;
                }
                if (bad_digit && !told)
                {
                    Err("Bad digit for base " + Base.ToString() + ": " + Next20());
                    told = true;
                }
            }
            return Result;
        }

        public double Real_number()
        {
            Skip_sp();
            bool neg = false;
            if (Next_char() == "-")
            {
                neg = true;
                Skip_char();

                Skip_sp();
            }
            double Result = Number();
            if (Next_char() == ".")
            {
                Skip_char();
                if ("0123456789".Contains(Next_char())) { }
                else Err("Digits must follow '.' in real number");
                double divider = 1;
                for (int xdummy = Pos; xdummy <= Last(); xdummy++)
                {
                    if (Next_char() == "_") { Skip_char(); continue; }
                    if ("0123456789".Contains(Next_char()))
                    {
                        divider /= 10;
                        Result += ((int)Next_char()[0] - (int)'0') * divider;
                        Skip_char();
                    }
                    else break;
                }
            }
            if ("eE".Contains(Next_char()))
            {
                Skip_char();
                int e = Integer();
                Result *= Math.Pow(10, e);
            }
            if (neg)
                Result = -Result;
            return Result;
        }

        public int Integer()
        {
            Skip_sp();
            bool neg = false;
            if (Symbol("-")) neg = true;
            int Result = Number();
            if (neg) Result = -Result;
            return Result;
        }
        
        public bool End_of_line()
        {
            return Line >= Source.Count || Pos >= Source[Line].Length;
        }

        public bool End_of_source()
        {
            return Line >= Source.Count || Line == Source.Count-1 && End_of_line();
        }

        public void Fin()
        {
            Skip_sp();
            if (!End_of_line()) Err("Ambiguous characters: " + Next20());
            for (int i = Line + 1; i <= Source.Count; i++)
            {
                Line = i;
                Pos = 0;
                Skip_sp();
                if (!End_of_line()) return;
            }
        }

        public bool Is_letter()
        {
            string U = Next_char().ToUpper();
            string L = Next_char().ToLower();
            return U != L;
        }

        public string Next20()
        {
            int L = 20;
            if (Last() < Pos + L)
                L = Last() - Pos + 1;
            if (L <= 0) return "";
            return Source.Count > Line ? Source[Line].Substring(Pos, L) : "";
        }

        public bool Symbol(string S)
        {
            Skip_sp();
            if (End_of_line()) return false;
            int L = S.Length;
            if (L + Pos > Source[Line].Length)
                return false;
            if (Source[Line].Substring(Pos, L) == S)
            {
                bool was_letter = Is_letter();
                Pos += S.Length;
                if (was_letter && Is_letter())
                {
                    Pos -= S.Length;
                    return false;
                }
                Skip_sp();
                return true;
            }
            return false;
        }
        
        public bool Next_symbol(string S)
        {
            int temp_pos = Pos;
            try
            {
                if (Symbol(S)) return true;
            }
            finally { Pos = temp_pos; }
            return false;
        }

        public void Wait(string S)
        {
            if (!Symbol(S)) Err("Waiting for " + S + " but: " + Next20());
        }

        public bool Ending(string S)
        {
            if (Line >= Source.Count) return false;
            string s = Source[Line].TrimEnd();
            int from = s.Length - S.Length;
            if (from < 0) return false;
            return s.Substring(s.Length - S.Length, S.Length) == S;
        }

        public int Remove_comments()
        {
            int in_multi = -1;
            bool old = true;
            if (Source.Count > 0 && Source[0] == "/" + "*" + ")")
                Source[0] = "";
            else
            {
                old = false;
                in_multi = 1;
            }
            for (int i = 0; i <= Source.Count - 1; i++)
            {
                Line = i;
                Pos = 0;
                string s = Source[i].TrimEnd();
                string t = Source[i].Trim();
                
				if (!old)
				{
					if (in_multi < 0 && s.Length > 1 && 
							s[s.Length-2] == '*' && s[s.Length-1] == '/')
					{
						in_multi = i + 1;
					}
					else if (in_multi > 0 && t.Length > 1 && 
							t[0] == '/' && t[1] == '*')
					{
                        s = "";
						in_multi = -1;
					}
				}
	
                if (in_multi < 0 && s.Trim() == "END") return i - 1;
                Source[i] = s;
                if (old && in_multi < 0 && Symbol("(" + "*"))
                {
                    if (!Ending("*" + ")")) in_multi = i+1;
                    Source[i] = "";
                }
                if (in_multi >= 0)
                {
                    if (old && Ending("*" + ")")) in_multi = -1;
                    Source[i] = "";
                }
                else Remove_single_line_comment();
            }
            return Source.Count - 1;
        }

        private void Remove_single_line_comment()
        {
            bool In_str = false;
            string s = Source[Line];
            for (int j = 0; j < s.Length; j++)
            {
                switch (s[j])
                {
                    case '\"': In_str = !In_str; break;
                    case '/': if (!In_str && s[j + 1] == '/')
                        {
                            Source[Line] = s.Substring(0, j);
                            return;
                        }
                        break;
                }
            }
        }

        public void Check_lines_len(int To)
        {
            int i = 0;
            foreach (string s in Source)
            {
                Line = i++;
                if (Line > To) return;
                if (s.Length > 80)
                    Err("Line length exceeds 80 characters");
            }
        }

        public static string Sp(int N)
        {
            string Result = "";
            for (int i = 1; i <= N; i++)
                Result += " ";
            return Result;
        }

        public void Merge_continued(int Last)
        {
            for (int i = Last; i >= 0; i--)
            {
                Line = i;
                string e = Source[i].TrimEnd();
                if (e == "") continue;
                e = e.Substring(e.Length - 1);
                if (".;".Contains(e)) continue;
                if (",([".Contains(e)) // || (Source[i+1].TrimStart() + " ")[0] == '\"')
                {
                    Source[i] += "\r\n" + Source[i + 1];
                    Source[i + 1] = "";
                }
                
                string next = Source[i+1].TrimStart();
                if (next == "" || next[0] == '{' || 
                    next[0] == '<' || next[0] == '[' || next[0] == '(' ||
                    next[0] == '.' || next[0] == ';' ||
                    next[0] == '-' && next[1] == '-') continue;
                    
                if ((next[0] + "").ToLower() == (next[0] + "").ToUpper())
                {
                    Source[i] += "\r\n" + Source[i + 1];
                    Source[i + 1] = "";
                }
            }
            for (int j = 0; j < Source.Count; j++)
            {
                if (j > Last) return;
                string st = Source[j].TrimEnd();
                if (st == "") continue;
                if (!";.".Contains(st[st.Length-1])) continue;
                for (int i = st.Length-2; i >= 0; i--)
                {
                    if (st[i] == st[st.Length-1])
                    {
                        Line = j;
                        Err("Two sequential symbols '" + st[i] + 
                            "' at end of line are not allowed");
                    }
                    if (!" \t".Contains(st[i])) break;
                }
            }
        }

        public string Scan_expr()
        {
            int from = Pos;
            int level = 0;
            for (int xdummy = Pos; xdummy <= Last() + 1; xdummy++)
            {
                Skip_sp();
                if (End_of_line()) break;
                switch (Next_char()[0])
                {
                    case '\"':
                    case '#': string sdummy = String_const(false); break;
                    case '(':
                    case '[':
                    case '{':
                        level++; Skip_char(); break;
                    case ')':
                    case ']':
                    case '}':
                        if (level-- <= 0) goto for_end_label;
                        Skip_char(); break;
                    case '!':
                    case '<':
                    case '>':
                    case '=':
                    case '+':
                    case '-':
                    case '*':
                    case '/':
                    case '%':
                    case '&':
                    case '|':
                    case '^':
                        Skip_char(); break;
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        double Dummy = Real_number(); break;
                    case ':':
                    case ';':
                    case '.':
                    case ',': if (level <= 0) goto for_end_label;
                        Skip_char();
                        break;
                    case '\'':
                        Skip_char();
                        Ident();
                        Wait("'");
                        break;
                    default: sdummy = Ident();
                        break;
                }
            }
        for_end_label:
            string Result = Source[Line].Substring(from, Pos - from).Trim();
            if (Result == "") Err("Expression is empty");
            return Result;
        }

        public string Scan_array()
        {
            Wait("[");
            string Result = "[" + Scan_expr();
            for (int zdummy = Pos; zdummy <= Last(); zdummy++)
            {
                if (!Symbol(",")) break;
                Result += ", " + Scan_expr();
            }
            Wait("]");
            Result += "]";
            return Result;
        }

        public string Skip_block_comment(List<string> All_id)
        {
            for (int i = Pos; i <= Last(); i++)
            {
                if (Next_char() == "-") Skip_char();
                else break;
            }
            Wait("'");
            int from = Pos;
            Skip_to("'");
            string label = "'" + Source[Line].Substring(from, Pos - from).Trim() + "'";
            Wait("'");
            if (All_id.Contains(label))
                Err("Duplicating label: " + label);
            All_id.Add(label);
            Fin();
            return label;
        }

        /*
        
        METHOD Binary_op|eration ==> {Expr}.{bin} :
            ---- 'binary operation: arithmetic, logic, bitwise or concatenation'
            Skip_sp
            BOOL unk|nown_binary_operation
            RESULT = 'CONCAT'
            CASE Next_char ?
                ["&"]: 
                       CASE Symbol("&&") ? RESULT = 'AND' ==>
                       ELSE RESULT = 'AND_B' ;
                ["|"]: 
                       CASE Symbol("||") ? RESULT = 'OR' ==>
                       ELSE RESULT = 'OR_B' ;
                ["+"]: RESULT = 'PLUS'
                ["-"]: RESULT = 'MINUS'
                ["*"]: RESULT = 'MULT'
                ["/"]: RESULT = 'DIV'
                ["%"]: RESULT = 'REST'
                ["^"]: RESULT = 'XOR_B'
                ["!"]: 
                       CASE Symbol("!=") ? RESULT = 'NOT_EQ' ==>
                       ELSE 
                            CASE ?
                            [Symbol("!IN")] ? RESULT = 'NOT_IN' ==>
                            [Symbol("!LIKE")] ? RESULT = 'UNLIKE' ==>
                            ELSE RESULT = 'NOT_EQ'
                                 unk = TRUE ;
                       ;
                ["="]: RESULT = 'EQUAL'
                       CASE Symbol("==") ? ==>
                       ELSE unk = TRUE ;
                ["<"]: 
                       CASE ?
                       [Symbol("<=")] ? RESULT = 'LESS_OR_EQ' ==>
                       [Symbol("<>")] ? RESULT = 'NOT_EQ' ==>
                       ELSE RESULT = 'LESS' ;
                [">"]: 
                       CASE Symbol(">=") ? RESULT = 'GREATER_OR_EQ' ==>
                       ELSE RESULT = 'GREATER' ;
                ["I"]: 
                       CASE Symbol("IN") ? RESULT = 'IN' ==> ;
                ["L"]:
                       CASE Symbol("LIKE") ? RESULT = 'LIKE' ==> ;
                ["."]: NONE
            ;
            CASE unk ? Err("Unknown operation"._Eunk2 ": " Next20) ;
            CASE RESULT != 'CONCAT' ? Skip_char ; .
        */
        public Expression.Bin Binary_op()
        {
            Expression.Bin Result = Expression.Bin.CONCAT;
            Skip_sp();
            bool unk = false;
            switch ((Next_char() + " ")[0])
            {
                case '&':
                    if (Symbol("&&")) return Expression.Bin.AND;
                    else Result = Expression.Bin.AND_B;
                    break;
                case '|':
                    if (Symbol("||")) return Expression.Bin.OR;
                    else Result = Expression.Bin.OR_B;
                    break;
                case '+': Result = Expression.Bin.PLUS;
                    break;
                case '-': Result = Expression.Bin.MINUS;
                    break;
                case '*': Result = Expression.Bin.MULT;
                    break;
                case '/': Result = Expression.Bin.DIV;
                    break;
                case '%': Result = Expression.Bin.REST;
                    break;
                case '^': Result = Expression.Bin.XOR_B;
                    break;
                case '!':
                    if (Symbol("!=")) return Expression.Bin.NOT_EQ;
                    else if (Symbol("!IN")) return Expression.Bin.NOT_IN;
                    else
                    {
                        Result = Expression.Bin.NOT_EQ;
                        unk = true;
                    }
                    break;
                case '=': Result = Expression.Bin.EQUAL;
                    if (Symbol("==")) return Result;
                    unk = true;
                    break;
                case '<': 
                    if (Symbol("<=")) return Expression.Bin.LESS_OR_EQ;
                else Result = Expression.Bin.LESS;
                    break;
                case '>':
                    if (Symbol(">=")) return Expression.Bin.GREATER_OR_EQ;
                    else Result = Expression.Bin.GREATER;
                    break;
                case 'I':
                    if (Symbol("IN")) return Expression.Bin.IN;
                    break;
            }
            if (unk) Err("Unknown operation: " + Next20());
            if (Result != Expression.Bin.CONCAT) Skip_char();
            return Result;
        }

        public bool End_of_expr()
        {
            Skip_sp();
            if (End_of_line()) return true;
            switch ((Next_char() + " ")[0])
            {
                case ':': case ';': case '.': case ',': case ')': case ']': case '?':
                    return true;
                case '<': return Next_symbol("<<");
                case '>': return Next_symbol(">>");
                case '=': return Next_symbol("==>") || !Next_symbol("==");
                case 'T': return Next_symbol("TO");
                case 'D': return Next_symbol("DOWNTO");
            }
            return false;
        }

        public void Err(string M) { Compiler_.Error(M); }
        public void Warn(string M) { Compiler_.Warning(M); }

        public string Rest_of_line()
        {
            string Result = "";
            if (Line < Source.Count)
            {
                Result = Source[Line];
                if (Pos >= Result.Length) Result = "";
                else Result = Result.Substring(Pos);
            }
            return Result;
        }

        public string Current_line()
        {
            string Result = "";
            if (Line < Source.Count) Result = Source[Line];
            return Result;
        }

        /*
        
        METHOD check_operation({Expr} E|xpression_binary) :
			CASE Ignore_errors || E.Operation != 'BINARY' ? ==> ;
            -------------------------------------- 'Check by type kinds first'
            {Type}.{kind_type} k1|_argument1 = E.Arg1_.Type_expr.Kind
            {kind_type} k2|_argument2 = E.Arg2_.Type_expr.Kind
            BOOL a1|_array_arg1 = E.Arg1_.Is_array
            BOOL a2|_array_arg2 = E.Arg2_.Is_array
            CASE E.Sign IN ['PLUS', 'MINUS', 'MULT', 'DIV',
            	 'LESS', 'LESS_OR_EQ', 'GREATER', 'GREATER_OR_EQ',
            	 'EQUAL', 'NOT_EQ']
            	 ?
				 CASE ?
                 [k1 == 'INT' && k2 == 'REAL'] ?
                      {Expr} conv|ert_operation = {Expr}(Arg1_ = E.Arg1_,
                     		 Operation = 'INT2REAL',
                     		 Type_obj_ = Compiler_.{Compiler}.TYPE_REAL),
                     		 OWNED BY E //Compiler_.{Compiler}.Current_fun
                     		 
                      E.Arg1_ = conv
                      k1 = 'REAL'
                 [k1 == 'REAL' && k2 == 'INT'] ?
                      conv = {Expr}(Arg1_ = E.Arg2_,
                     		 Operation = 'INT2REAL',
                     		 Type_obj_ = Compiler_.{Compiler}.TYPE_REAL),
                     		 OWNED BY E //Compiler_.{Compiler}.Current_fun
                     		 
                      E.Arg2_ = conv
                      k2 = 'REAL' ;
            ;
            STR add|itionally = ""
            CASE E.Sign ?
            ['UNDEFINED']: Err("Undefined operation"._EUndefinedOp)
            ['PLUS', 'MINUS', 'MULT', 'DIV']:
                ------------------------------- '+, -, *, /'
                BOOL ok|_operation = !a1 && !a2
                CASE ?
                [k1 IN ['CLASS', 'STRUCTURE']
                 || k2 IN ['CLASS', 'STRUCTURE']] ? 
	                 {Fun} operator 
	                 	   = Compiler_.{Compiler}.Current_fun.Search_operator(
	                 	   E.Sign, E.Arg1_, E.Arg2_)
	                 		
	                 ok = operator != NONE
	                 CASE ok ?
	                      operator.Count_ref += 1
	                      operator.Usage_count += 1
	                      Compiler_.{Compiler}.Current_fun.Calls_fun(operator)
	                      E.Operation = 'FUN'
	                      E.Fun_ = operator
	                      E.Items_[] << E.Arg1_ << E.Arg2_
	                 	  E.Type_obj_ = operator.Result.Type_ ;
	                 	  
                [k1 == 'STR' && k2 == 'STR' && E.Sign == 'PLUS'
                 && Concatenate_by_plus] ?
                     ok = TRUE
                     E.Sign = 'CONCAT'
                     E.Type_obj_ = Compiler_.{Compiler}.TYPE_STR
                
                ELSE
	                 ok &&= k1 IN ['INT', 'REAL']
	                 ok &&= k2 IN ['INT', 'REAL']
                	 E.Type_obj_ = E.Arg1_.Type_expr
	                 CASE E.Type_obj_.Kind == 'INT' ?
	                     E.Type_obj_ = E.Arg2_.Type_expr ;
                ;
            ['REST', 'AND_B', 'OR_B', 'XOR_B']:
                -------------------------------- '%, &, |, ^'
                ok = k1 == 'INT' && k2 == 'INT' && (!a1 && !a2)
                     
                E.Type_obj_ = Compiler_.{Compiler}.TYPE_INT
            ['LESS', 'LESS_OR_EQ', 'GREATER', 'GREATER_OR_EQ']:
                -------------------------------- '<, <=, >, >='
				ok = k1 IN ['INT', 'REAL'] && k2 IN ['INT', 'REAL']
                            
                ok ||= k1 == k2 && k1 == 'STR'
                ok &&= !a1 && !a2
                E.Type_obj_ = Compiler_.{Compiler}.TYPE_BOOL
            ['EQUAL', 'NOT_EQ']:
                -------------------------------- '==, !='
                ok = k1 == 'INT' && k2 == 'INT'
                ok ||= k1 == k2 && k1 != 'STRUCTURE'
                CASE Can_compare_reals ? 
                     ok ||= k1 IN ['INT', 'REAL'] && k2 IN ['INT', 'REAL']
                ;
                CASE !ok && k1 == 'CLASS' //, 'STRUCTURE'] 
                	 && k2 == 'CLASS'
                	 && E.Arg2_.Operation == 'CONST'
                	 && E.Arg2_.Var_arg.Constant_value == "NONE"
                	 ?
                     ok = TRUE ;
                ok &&= !a1 && !a2
                CASE !Can_compare_reals ?
                	 ok &&= k1 != 'REAL' && k2 != 'REAL' ;
                CASE k1 == 'ENUM' ?
                     {Type} t1|_type_of_arg1 = E.Arg1_.Type_expr 
                     {Type} t2|_type_of_arg2 = E.Arg2_.Type_expr 
                     ok = t1.Names[0] == t2.Names[0] 
                     ok &&= t1.Names[1] == t2.Names[1]
                     ok &&= t1.Base_ == t2.Base_
                ;
                E.Type_obj_ = Compiler_.{Compiler}.TYPE_BOOL
                CASE !ok && k1 == 'REAL' || k2 == 'REAL' ? 
                     add = "comparing real number for equality / inequality"
                     	""._real_for_eq
                ;
            ['IN', 'NOT_IN']:
                --------------------------------- 'IN - check now'
                ok = k1 == 'STR' && k1 == k2 && !a1 && !a2
                ok ||= !a1 && a2 && k1 == k2 && k2 != 'REAL'
                CASE k1 == 'STR' && a2 && E.Arg2_.Operation == 'ARRAY' && (
                     E.Arg2_.Items_[].Count == 1)
                     ?
                     Err("Don't use IN/!IN to check a string for a certain "
                     "value, use ==/!= in such case"._Edont)
                ;
                CASE k1 == 'ENUM' ?
                     STR name1|_type = E.Arg1_.Type_expr.Whole_name
                     STR name2|_type = E.Arg2_.Type_expr.Whole_name  
                     ok &&= name1 == name2
                     CASE ok ?
                          {Type} base1|_type = E.Arg1_.Type_expr.Base_
                          {Type} base2|_type = E.Arg2_.Type_expr.Base_  
                          ok &&= base1 == base2 ; 
                ;
                E.Type_obj_ = Compiler_.{Compiler}.TYPE_BOOL
            ['LIKE', 'UNLIKE']:
                ok = k1 == 'STR' && k1 == k2 && !a1 && !a2
                E.Type_obj_ = Compiler_.{Compiler}.TYPE_BOOL
            ['OR', 'AND']:
                ---------------------------------- '||, &&'
                ok = k1 == k2 && k1 == 'BOOL' && !a1 && !a2
                E.Type_obj_ = Compiler_.{Compiler}.TYPE_BOOL
            ['CONCAT']:
                ---------------------------------- 'strings concatenation'
                ok = k1 == k2 && k1 == 'STR' && !a1 && !a2
                E.Type_obj_ = Compiler_.{Compiler}.TYPE_STR
                CASE !ok && k1 != 'STR' ?
                     add = ", concatenation of not string "
                     "argument"._concat_not_str
                ;
                CASE !ok && add == "" && k2 != 'STR' ?
                     add = ", concatenation with not string argument"
                     	""._concat_not_str2 ;
            ;
            CASE !ok ?
                PUSH Pos = E.Arg1_.At_pos :
                     CASE add != "" ?
                          Err("Inapplicable operation"._Einapp 
                          "(" add "): " Next20)
                     ELSE Err("Inapplicable operation"._Einapp2 ": " Next20) ;
                ;
            ; .
            
FUN bool2str( BOOL B|ool_value) ==> STR :
    CASE B ? RESULT = "TRUE" ==> ;
    RESULT = "FALSE" .
            
            
        TEST checking_operation :
             ------------------------------------------ 'Let`s begin'
             ASSERT bool2str(FALSE) == "FALSE"
             ASSERT bool2str(TRUE) == "TRUE"
             << "TEST Parser.checking_operation ----------------------------"#NL
             ASSERT TRUE
             ------------------------------------------ 'prepare'
             c|lass_temp = {Type}(Names[] << "{Test:Parser}")
             com|piler_temp = {Compiler}(Current_class = c, Max_err=20)
             
             p|arser_temp = {Parser}(Compiler_ = com, Class_ = c,
             	Warning_80= FALSE)
             	
             c.Parse = p
             Init_system_types(com)
             ASSERT TRUE
             ---------------------------------------- 'prepare system constants'
             vtemp|_variable = {Var}(Line = 0, In_type_ = c)
             com.Init_const(p)
             ASSERT vtemp != NONE
             ------------------------------------------ 'start testing'
             ASSERT com.Errors_count == 0
             p.Test_line("")
             ------------------------------- 'check_operation'
             a1|_argument = {Expr}(
             		Operation = 'VAR',
                    Var_arg = {Var}(
                    		Line = 0,
                    		Type_ = com.TYPE_INT)
             ), OWNED BY c
                    
             a2|_argument = {Expr}(
             		Operation = 'VAR',
                    Var_arg = {Var}(
                    		Line = 0,
                    		Type_ = com.TYPE_INT)
             ), OWNED BY c
                    
             ex|pression_temp = {Expr}(
             		Operation = 'BINARY',
             		Sign = 'PLUS',
                	Arg1_ = a1,
                	Arg2_ = a2)
                
             ASSERT com.Errors_count == 0
             ------------------------------- '[ + ]'
             com.Errors_count = 0
             p.check_operation(ex)
             ASSERT com.Errors_count == 0
             PUSH a1.Var_arg.Type_ = com.TYPE_STR :
                  p.check_operation(ex)
                  ASSERT com.Errors_count == 1 ;
             ------------------------------ '[ < ]'
             ex.Sign = 'REST'
             p.check_operation(ex)
             ASSERT com.Errors_count == 1
             ------------------------------ '[ % ]'
             ex.Sign = 'LESS'
             p.check_operation(ex)
             ASSERT com.Errors_count == 1
             ------------------------------ '[ == ]'
             ex.Sign = 'EQUAL'
             p.check_operation(ex)
             ASSERT com.Errors_count == 1
             ------------------------------ '[ IN ]'
             ex.Sign = 'IN'
             p.check_operation(ex)
             ASSERT com.Errors_count == 1
             ------------------------------ '[ IN ["xxx"] ]'
             << "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" #NL
             
             temp_const = {Expr}(Operation = 'CONST',
                    Var_arg = {Var}(Type_ = com.TYPE_STR, Line = 0))
                    
             temp_a2|_IN_only = {Expr}(Operation = 'ARRAY',
                Items_[] << temp_const), OWNED BY c
                
             temp_a1|_IN_only = {Expr}(Operation = 'VAR',
                    Var_arg = {Var}(Line = 0, Type_ = com.TYPE_STR)),
                    OWNED BY c
                    
             PUSH ex.Arg2_ = temp_a2 :
             PUSH ex.Arg1_ = temp_a1 :
                  p.check_operation(ex) ;
             ;
             ASSERT com.Errors_count >= 1
             com.Errors_count = 1
             ------------------------------ '[ OR ]'
             p.Test_line("")
             ex.Sign = 'OR'
             p.check_operation(ex)
             ASSERT com.Errors_count == 2
             ------------------------------ '[ CONCAT ]'
             p.Test_line("")
             ex.Sign = 'CONCAT'
             p.check_operation(ex)
             ASSERT com.Errors_count == 3
             PUSH a1.Var_arg.Type_ = com.TYPE_STR :
                  p.check_operation(ex)
                  ASSERT com.Errors_count == 3, com.Errors_count.Str ;
             ----------------------------- 'NAN =='
             com.Errors_count = 0
             ex.Sign = 'EQUAL'
             PUSH ex.Arg1_.Var_arg = com.NAN_c :
                  p.check_operation(ex)
                  ASSERT com.Errors_count >= 0 ;
             ------------------------------ 'compare enums'
             com.Errors_count = 0
             {Type} en|um_temp = {Type}(Kind = 'ENUM')
             
             a1 = {Expr}(Operation = 'VAR', Var_arg = {Var}(Line = 0, 
                Type_ = en)), OWNED BY c
                
             a2 = {Expr}(Operation = 'VAR', Var_arg = {Var}(Line = 0, 
                Type_ = en)), OWNED BY c
                
             ex = {Expr}(Operation = 'BINARY', Sign = 'EQUAL', Arg1_ = a1,
                Arg2_ = a2), OWNED BY c
                
             p.check_operation(ex)
             ASSERT com.Errors_count == 0
             ------------------------------ 'check enum IN [enums]'
             ex.Sign = 'IN'
             a2.Var_arg.Is_array = TRUE
             p.check_operation(ex)
             ASSERT com.Errors_count == 0
             //------------------------------ 'test priorities'
             //{Expr} operands|_temp[] << a1 << a2
             //{bin} signs|_temp[] << 'OR'
             //p.Do_priorities( [" "], operands[], signs[] )
             //p.Do_priorities( ["*"], operands[], signs[] )
             //p.Do_priorities( ["+"], operands[], signs[] )
             //p.Do_priorities( ["^"], operands[], signs[] )
             //ASSERT TRUE
             //-------------------------------- '... + comparisons & logic'
             //p.Do_priorities( ["|"], operands[], signs[] )
             //p.Do_priorities( ["I"], operands[], signs[] )
             //p.Do_priorities( ["<"], operands[], signs[] )
             //p.Do_priorities( ["&&"], operands[], signs[] )
             //p.Do_priorities( ["||"], operands[], signs[] )
             
             //ASSERT operands[].Count == 1, operands[].Count.Str
             //   ", " operands[0].Operation.Name
             //   ", " operands[1].Operation.Name
             //--------------------------------- 'should stay one operand on top'
             //ASSERT signs[].Count == 0, signs[].Count.Str ", " signs[0].Name
             //<< "end of TEST Parser.checking_operation ---------------------"#NL
             .
        */
        static int CNT_check_operation;

        private void check_operation(Expression E)
        {
            if (CNT_check_operation++ == 7)
            { }

            Type.Kind_type k1 = E.Arg1_.Type_expr() != null ? E.Arg1_.Type_expr().Kind : Type.Kind_type.BOOL;
            Type.Kind_type k2 = E.Arg2_ != null && E.Arg2_.Type_expr() != null ? E.Arg2_.Type_expr().Kind : Type.Kind_type.BOOL;
            bool a1 = E.Arg1_.Is_array();
            bool a2 = E.Arg2_ != null ? E.Arg2_.Is_array() : false;
            bool ok = false;
            switch (E.Sign)
            {
                case Expression.Bin.PLUS:
                case Expression.Bin.MINUS:
                case Expression.Bin.MULT:
                case Expression.Bin.DIV:
                    ok = k1 == Type.Kind_type.INT || k1 == Type.Kind_type.REAL;
                    ok = ok && (k2 == Type.Kind_type.INT || k2 == Type.Kind_type.REAL);
                    ok = ok && !a1 && !a2;
                    E.Type_obj_ = E.Arg1_.Type_expr();
                    if (E.Type_obj_.Kind == Type.Kind_type.INT)
                        E.Type_obj_ = E.Arg2_.Type_expr();
                    break;
                case Expression.Bin.REST:
                case Expression.Bin.AND_B:
                case Expression.Bin.OR_B:
                case Expression.Bin.XOR_B:
                    ok = k1 == k2 && k1 == Type.Kind_type.INT && !a1 && !a2;
                    E.Type_obj_ = Compiler_.TYPE_INT;
                    break;
                case Expression.Bin.LESS:
                case Expression.Bin.LESS_OR_EQ:
                case Expression.Bin.GREATER:
                case Expression.Bin.GREATER_OR_EQ:
                    ok = (k1 == Type.Kind_type.INT || k1 == Type.Kind_type.REAL) &&
                        (k2 == Type.Kind_type.INT || k2 == Type.Kind_type.REAL) ||
                        k1 == k2 && k1 == Type.Kind_type.STR;
                    ok = ok && !a1 && !a2;
                    E.Type_obj_ = Compiler_.TYPE_BOOL;
                    break;
                case Expression.Bin.EQUAL:
                case Expression.Bin.NOT_EQ:
                    ok = (k1 == Type.Kind_type.INT) &&
                        (k2 == Type.Kind_type.INT) ||
                        k1 == k2;
                    ok = ok && (k1 != Type.Kind_type.REAL) && (
                         k2 != Type.Kind_type.REAL) && !a1 && !a2;
                    E.Type_obj_ = Compiler_.TYPE_BOOL;
                    break;
                case Expression.Bin.IN:
                case Expression.Bin.NOT_IN:
                    ok = k1 == Type.Kind_type.STR && k1 == k2 && !a1 && !a2 ||
                        !a1 && a2 && k1 == k2;
                    E.Type_obj_ = Compiler_.TYPE_BOOL;
                    break;
                case Expression.Bin.OR:
                case Expression.Bin.AND:
                    ok = k1 == k2 && k1 == Type.Kind_type.BOOL && !a1 && !a2;
                    E.Type_obj_ = Compiler_.TYPE_BOOL;
                    break;
                case Expression.Bin.CONCAT:
                    ok = k1 == k2 && k1 == Type.Kind_type.STR && !a1 && !a2;
                    E.Type_obj_ = Compiler_.TYPE_STR;
                    break;
            }
            if (!ok)
            {
                int save_pos = Pos;
                Pos = E.Arg1_.At_pos;
                Err("Unapplicable operation: " + Next20());
                Pos = save_pos;
            }
        }

        /*
        
        BOOL Ignore_errors
        
        METHOD Do_priorities(
            {Expr} Operands|_array[], {E_op} Operations[])
            :
			FOR i_op1 IN [0 TO Operations[].Count-2] :
				CASE Operations[i_op1].Fun_ != NONE ? CONTINUE i_op1 ;
			    {bin} op1|_previous_operation = Operations[i_op1].Sign
			    FOR j_op2 IN [i_op1 + 1 TO Operations[].Count-1] :
					CASE Operations[j_op2].Fun_ != NONE ? CONTINUE j_op2 ;
					{bin} op2|_next_operation = Operations[j_op2].Sign
					CASE op1 IN ['AND_B', 'OR_B', 'XOR_B'] 
						 && op2 IN ['PLUS', 'MINUS', 'MULT', 'DIV', 'REST']
						 || op1 IN ['PLUS', 'MINUS', 'MULT', 'DIV', 'REST'] 
						 && op2 IN ['AND_B', 'OR_B', 'XOR_B']
						 ?
					     Err("It is not allowed to use bitwise operations (&, |, ^)"
					     " on the same level with arithmetic operations"
					     ""._SameLevelBitwiseOps)
					     
						BREAK j_op2, BREAK i_op1
					;
			    ;
			;
            ---------- 'Build tree downing all operations with certain priority'
            FOR i|dummy IN [0 TO Operations[].Count-1] :
                CASE Operations[].Count==0 ? BREAK i ;
                INT max_pr|irity = 0
                FOR j IN [0 TO Operations[].Count-1] :
                    CASE max_pr < Operations[j].Priority ? 
                         max_pr = Operations[j].Priority ;
                ;
                CASE !priority(max_pr, Operands[], Operations[]) ? BREAK i ;
            ; .
        */
        public void Do_priorities(string[] Sets, List<Expression> Operands, List<Expression.Bin> Signs)
        {
            foreach (string s in Sets)
            {
                int Nmax = Operands.Count;
                for (int x = 0; x <= Nmax; x++)
                    if (!priority(s, Operands, Signs)) break;
            }
        }

        /*
        
        METHOD priority( INT Min_pr|iority_to_down,
            {Expr}  Operands|_array[],
            {E_op}  Operations[]) ==> BOOL
            :
            ---------------- 'Build tree downing operations of certain priority'
            //FOR op IN Operations[] :
	        INT at|_position INDEXING ({Expr},{E_op}) = 0
            FOR i|dummy IN [0 TO Operations[].Count-1] :
                CASE at >= Operations[].Count ? ==> ;
				{E_op} op|eration_at_i_op = Operations[at]
                BOOL want|_move = op.Priority >= Min_pr
                CASE !want ?
                     at += 1
                     CONTINUE i ;
				---------------------- 'want move down as having more priority'
				{Expr} o1|_operand = Operands[at]
				
				{Expr} down|ed_operation 
            	CASE Operations[at].Fun_ != NONE ? 
            	     down = {Expr}(Operation= 'FUN', Fun_= Operations[at].Fun_,
            	     	This_= Operations[at].This_pass_,
            	     	Items_[] << o1 << Operands[at+1],
            	     	At_line= o1.At_line, At_pos= o1.At_pos), OWNED BY THIS
            	ELSE
					 down = {Expr}(Operation = 'BINARY',
							 Arg1_ = o1, Arg2_ = Operands[at+1],
							 Sign = Operations[at].Sign, At_line = o1.At_line,
							 At_pos = o1.At_pos), OWNED BY THIS
            	;
						
				check_operation(down)
				----------------------- 'optimize compare sub-strings'
				{Expr} a1|_argument_of_down = down.Arg1_
				{Expr} a2|_argument_of_down = down.Arg2_
				BOOL optimized = FALSE
				CASE a1.Operation == 'INDEX' ?
				     BOOL is_substr|ing = a1.Type_expr.Kind == 'STR' 
				     	  is_substr &&= ! a1.Is_array
				     	  is_substr &&= a1.Arg3_ != NONE
                     CASE is_substr ?
					 	  optimize_comparing_substring(down) 
					 	  optimized = TRUE ;
				;
				CASE !optimized ?
				     is_substr = !a2.Is_array 
				     is_substr &&= a2.Operation == 'INDEX'
				     is_substr &&= a2.Type_expr.Kind == 'STR'
				     is_substr &&= a2.Arg3_ != NONE
				     CASE is_substr ?
					 	  optimize_comparing_substring(down) ;
				;
				----------------------- 'move down'
				Operands[at] = down
				Operands[].Delete(at+1)
				Operations[].Delete(at)
				RESULT = TRUE
				----------------------- 'long concatenations'
				CASE down.Sign != 'CONCAT' ? CONTINUE i ;
				down.Items_[] << down.Arg1_ << down.Arg2_
				down.Arg1_ = NONE
				down.Arg2_ = NONE
				FOR j|dummy IN [at+1 TO Operands[].Count-1] :
					CASE Operations[at].Sign != 'CONCAT' ? ==> ;
					BOOL is_strings = Operands[at+1].Type_expr.Kind == 'STR'
						 is_strings &&= !Operands[at+1].Is_array
                    CASE !is_strings && !Ignore_errors ?
                         PUSH Pos = Operands[at+1].At_pos :
                              Err("Concatenation is not applicable for a not "
                              "string operand"._concat ": " Next20) ;
                    ;
                    CASE Operands[at+1].Operation=='CONST' 
                         && Operands[at+1].Var_arg.Str_c == "" 
                         ? 
                         NONE
                    ELSE down.Items_[] << Operands[at+1] ;
                    Operands[].Delete(at+1)
                    Operations[].Delete(at)
				;
				CASE down.Items_[].Count==0 ? 
				     down.Operation = 'CONST'
				     down.Var_arg = {Var}(Is_const= TRUE, Type_n= "STR",
				     	Type_= Compiler_.{Compiler}.TYPE_STR, Str_c= "",
				     	Line= -1) ;
            ; .
			
		FUN Test_parser|_with_temp_compiler ==> {Parser},
			NEW
            :
			--------------------------------- 'compiler -> class -> parser'
			c|ompiler_temp = {Compiler}
            t|ype_class_temp = {Type}
            RESULT = {Parser}(Compiler_ = c, Class_ = t, Warning_80= FALSE)
            c = {Compiler}, OWNED BY RESULT
            RESULT.Compiler_ = c
            t.Parse = RESULT
            --------------------------------- 'initialize constants'
            v|ariable_temp = {Var}(Line = -1, In_type_ = t)
            c.Init_system_types
            c.Init_const(t.Parse)
			--------------------------------- 'current positions'
			c.Current_class = t
			
			f|unction_test = {Fun}(Names[] << "Dummy", A_Name_1 = "Dummy",
				Parse = RESULT), >> t.Method[]
				
			c.Current_fun = f
			.

            
        METHOD optimize_comparing_substring(
            {Expr} C|ompare_operation_optimize)
            :
	        CASE C.Sign !IN ['LESS', 'LESS_OR_EQ', 'GREATER', 'GREATER_OR_EQ',
	        	 'EQUAL', 'NOT_EQ'] 
	        	 ? ==> ;
            {Expr} a1|_argument = C.Arg1_
            {Expr} a2|_argument = C.Arg2_
			BOOL index1|_is_a1 = a1.Operation == 'INDEX' && !a1.Arg1_.Is_array            
			BOOL index2|_is_a2 = a2.Operation == 'INDEX' && !a2.Arg1_.Is_array            
            CASE index1 && index2 ?
                 f|unction_special = {Expr}(Operation = 'SYSTEM_FUN', 
                    At_line = a1.At_line, At_pos = a1.At_pos,
                    Fun_ = Compiler_.{Compiler}.SYSFUN_Compare_substrings,
                    Items_[] << a1.Arg1_ << a2.Arg1_,
                    Items_[] << a1.Arg2_ << ensure_2nd(a1.Arg3_, a1.Arg2_),
                    Items_[] << a2.Arg2_ << ensure_2nd(a2.Arg3_, a2.Arg2_)), 
                    OWNED BY C
                 C.Arg1_ = f
                 
                 C.Arg2_ = {Expr}(Operation = 'CONST',
                    Var_arg = Compiler_.{Compiler}.N0_c ), OWNED BY C ==>
            ;
            CASE index1 ?
                 f = {Expr}(Operation = 'SYSTEM_FUN',
                    At_line = a1.At_line, At_pos = a1.At_pos,
                    Fun_ = Compiler_.{Compiler}.SYSFUN_Compare_substr1,
                    Items_[] << a1.Arg1_ << a1.Arg2_ << a1.Arg3_ << a2),
                    OWNED BY C
                 C.Arg1_ = f
                 
                 C.Arg2_ =  {Expr}(Operation = 'CONST',
                    Var_arg = Compiler_.{Compiler}.N0_c ), OWNED BY C ==>
            ;
            
            f = {Expr}(Operation = 'SYSTEM_FUN',
               At_line = a1.At_line, At_pos = a1.At_pos,
               Fun_ = Compiler_.{Compiler}.SYSFUN_Compare_substr1,
               Items_[] << a2.Arg1_ << a2.Arg2_ << a2.Arg3_ << a1),
               OWNED BY C
               
            CASE C.Sign.Name ?
            ["'LESS'"]: C.Sign = 'GREATER'
            ["'GREATER'"]: C.Sign = 'LESS'
            ["'LESS_OR_EQ'"]: C.Sign = 'GREATER_OR_EQ'
            ["'GREATER_OR_EQ'"]: C.Sign = 'LESS_OR_EQ' ;
            C.Arg1_ = f
            
            C.Arg2_ =  {Expr}(Operation = 'CONST',
               Var_arg = Compiler_.{Compiler}.N0_c ), OWNED BY C .
            
            
             
        FUN ensure_2nd|_operand_get_first_if_NONE(
            {Expr} A2|_argument_shuold_be_returned_if_OK,
            {Expr} A3|_argument_return_if_A2_is_NONE ) ==> {Expr}
            :
            --------------------------------------- 'either A2 or A3'
            CASE A2 != NONE ? RESULT = A2 ==> ;
            RESULT = A3 .
            
        TEST of_ensure_2nd :
             e2|_temporary = {Expr}
             e3|_temporary = {Expr}
             ASSERT ensure_2nd(NONE, e3) == e3
             ASSERT ensure_2nd(e2, e3) == e2 .
             
        */
        private bool priority(string Set, List<Expression> Operands, List<Expression.Bin> Signs)
        {
            for (int i = 0; i < Signs.Count; i++)
            {
                bool want = false;
                Expression.Bin sgn = Signs[i];
                switch (Set[0])
                {
                    case '*': want = sgn == Expression.Bin.MULT ||
                        sgn == Expression.Bin.DIV || sgn == Expression.Bin.REST;
                        break;
                    case '+': want = sgn == Expression.Bin.PLUS || sgn == Expression.Bin.MINUS;
                        break;
                    case '^': want = sgn == Expression.Bin.AND_B || sgn == Expression.Bin.XOR_B;
                        break;
                    case '|':
                        if (Set == "|") want = sgn == Expression.Bin.OR_B;
                        else want = sgn == Expression.Bin.OR;
                        break;
                    case '<': want = sgn == Expression.Bin.LESS || sgn == Expression.Bin.LESS_OR_EQ ||
                        sgn == Expression.Bin.GREATER || sgn == Expression.Bin.GREATER_OR_EQ ||
                        sgn == Expression.Bin.EQUAL || sgn == Expression.Bin.NOT_EQ;
                        break;
                    case 'I': want = sgn == Expression.Bin.IN || sgn == Expression.Bin.NOT_IN;
                        break;
                    case '&': want = sgn == Expression.Bin.AND;
                        break;
                    default: want = sgn == Expression.Bin.CONCAT;
                        break;
                }
                if (want)
                {
                    Expression down = new Expression();
                    down.Operation = Expression.Operation_kind.BINARY;
                    down.Sign = Signs[i];
                    down.Arg1_ = Operands[i];
                    down.Arg2_ = i < Operands.Count - 1 ? Operands[i + 1] : null;
                    down.Copy_location(down.Arg1_);
                    check_operation(down);
                    Operands[i] = down;
                    if (i < Operands.Count - 1) { Operands.RemoveAt(i + 1); }
                    Signs.RemoveAt(i);
                    return true;
                }
            }
            return false;
        }

    }
}
/*




























////////////         ////    ///////    /////////          //// //          
//         //       // //  //          //                 // // //          
//         //      //  // //          //                 //  // //          
//         //     //   //  //         //                //   // //          
////////////     //    //   ///////   //               //    // //          
//              //     //         //  //              //     // //          
//             //////////          // //             ////////// //          
//            //       //         //   //           //       // //          
//           //        //  ///////       ////////  //        // /////////// 






STR		Syntax_name|_PASCAL_SEAL_etc
--------------------- 'flags meaking rules ease'
BOOL Case_insensitive

OVERRIDE Any_case ==> BOOL := Case_insensitive .

BOOL Allow_assign_to_fields|_and_methods_via_setters
BOOL Concatenate_by_plus|_for_strings
BOOL Allow_omit_array_brackets
BOOL Not_restrict_nesting|_levels_of_CASE_and_FOR
BOOL Ignore_function_result|_not_assigned_EXCEPT_OBJECTS
BOOL Clone_by_default|_structures
BOOL Can_compare_reals|_for_equal

----------------------------- 'PASCAL'

METHOD token|_check_as_identifier(STR Token|_to_check) ==> BOOL :
	Skip_sp
	CASE !Next_symbol(Token) ? ==> ;
	INT p|osition_before_id = Pos
	CASE Ident == Token ? RESULT = TRUE ==> ;
	Pos = p .
	
METHOD Next_token(STR Token|_to_check) ==> BOOL :
	Skip_sp
	PUSH Pos :
	     RESULT = token(Token) ; .

STR r|esulting_source[]
STR m|ask_comments_strings[]
INT a|rray_al4_code_flags[]

BOOL from_kol|_project_migrating

METHOD Convert_from_pascal|_like_syntax :
	Compiler_.{Compiler}.Error_prefix = "[Pascal]"
	--------------------- 'options to ease rules while compiling migrated code'
	Case_insensitive = TRUE
	Allow_assign_to_fields = TRUE
	Concatenate_by_plus = TRUE
	Allow_omit_array_brackets = TRUE
	--------------------- 'part II ease rules'   
	Not_restrict_nesting = TRUE
	Ignore_function_result = TRUE
	Clone_by_default = TRUE
	Can_compare_reals = TRUE
	--------------------- 'kol ?'
	FOR src IN Compiler.Source_paths[] :
	    CASE "kol2al4" IN src.Lower ?
	         from_kol = TRUE
	         BREAK src ;
	;

	--------------------- 'let us begin'              
	Skip_sp
	CASE End_of_line ? Fin ;
	PUSH Line :
	PUSH Pos :
		 FOR s IN Source[] : 
		     r[] << "" 
		     m[] << s ;
		 Wait("*" "/")
		 CASE End_of_line ? Fin ;
		 Wait("*" ")")
		 CASE !End_of_line ?
		      Err("*) must be the last symbol in a line"._EEndPasCommentNotLast)
		 ;
		 Fin
		 ------------------ 'mask comments & strings'
		 FOR s IN Source[Line TO *] :
		 	 FOR i|dummy IN [0 TO s.Len] :
			     INT i1|_position_of_open_comment1 = s.Find("(*")
			     INT i2|_position_of_open_comment2 = s.Find("{")
			     CASE i2 >= 0 && s.[i2+1] IN "@|" ? i2 = -1 ;
			     INT i3|_position_of_open_string = s.Find("'")
			     CASE i1 < 0 && i2 < 0 && i3 < 0 ? BREAK i, CONTINUE s ;
			     CASE i3 < 0 ? i3 = i1.Max(i2) ;
			     CASE i2 < 0 ? i2 = i1.Max(i3) ;
			     CASE i1 < 0 ? i1 = i2.Max(i3) ;
			     i1 = i1.Min(i2).Min(i3)
			     mask_1_comment(s.Index, i1, m[])
			     s = s.clear_commented_chars(m[s.Index]) 
		 	 ;
		 ;
		 ----------------- 'lowercase all but string contants'
		 INT in_al4|_code = 0
		 FOR s IN Source[0 TO Line-1] : a[] << 0 ; 
		 FOR s IN Source[Line TO *] :
		     a[] << 0
		     STR st|rimmed_lowered_s = s.Trim.Lower
		     CASE ?
		     [in_al4 == 0 && st == "(*$ifdef al4*)"] ?
		          in_al4 = 1
		     [in_al4 == 0 && st == "(*$ifdef al4code*)"] ?
		          in_al4 = 2
		          a[s.Index] = 2
		          Source[s.Index] = s.Lower
		          CONTINUE s
		     [in_al4 > 0 && st == "(*$else*)"] ?
		          in_al4 = -1
		     [in_al4 != 0 && st == "(*$endif*)"] ?
		          in_al4 = 0
		     ;
		     CASE ?
		     [in_al4 == 2] ?
		          a[s.Index] = in_al4
		          CONTINUE s
		     [in_al4 < 0] ? 
		          Source[s.Index] = "" 
		          CONTINUE s ;
		     CASE End_of_line ? Fin ;
		     ---------------- 'only in pascal code'     
		     CASE m[s.Index] == s && s.TrimR == s ?
		          Source[s.Index] = s.Lower
		          CONTINUE s ;
		     STR was|_before_lower = s
		     s = s.Lower
		     FOR i IN [0 TO s.Len-1] :
		         CASE m[s.Index].[i] ?
		         [#QU]: s.[i] = was.[i]
		         [" "]: s.[i] = " " ;
		     ;
		     Source[s.Index] = s.TrimR ;
		     
		 -----------------------'.pas0al4'
		 //DEBUG:
		 // STR s000|_pas0al4[]
		 // FOR s IN Source[] :
		 //     s << " ".Repeat(72-s.Len) "// " a[s.Index].S
		 //     s000[] << s ;
		 // Unicode_save(Full_path ".pas0al4", s000[])
		 //; 
		 ----------------- 'additional merge continued lines'
		 FOR s IN Source[* DOWNTO Line+1] :
		 	 CASE a[s.Index] > 0 || a[s.Index-1] > 0 ?
		 	      CASE #CR IN s || #LF IN s ?
		 	           Source[s.Index] = s.Replace_all(#CR, " ").Replace_all(
		 	           		#LF, " ") 
		 	      ;
		 	      CONTINUE s ;
		 	 st = s.Trim
		     CASE st.Starting("end") || st.Starting("(*") 
			      || st.Starting("procedure") || st.Starting("function")
			      || st.Starting("else")
			      ?
		          CONTINUE s ;
		     STR prev|ious_line_trimmed = Source[s.Index-1].Trim
		     
		     CASE Is_letter(prev.[*]) ?
		          STR prev_last|_ident = get_last_ident(prev)
		     ELSE prev_last = "" ;
		          
		     CASE Is_letter(s.[*]) ?
		          STR s_last|_ident = get_last_ident(s)
		     ELSE s_last = "" ;
		          
		     CASE st.Ending(";") && prev != "" && prev.[*] !IN ";}" 
		     	  && !prev.Ending("*)")
		     	  && prev_last !IN ["then", "else", "of", "do", "begin", "end",
		     	  "repeat", "try", "finally", "except", "interface",
		     	  "implementation", "type", "private", "public", 
		     	  "protected"]
                  || s_last != "" && s_last IN ["then", "do"]
                  && prev.[*] !IN ";}" && !prev.Ending("*)")
                  && prev_last !IN ["then", "else", "of", "do", "begin", "end",
                  "repeat", "try", "finally", "except"]
		     	  ?
		          Source[s.Index-1] = Source[s.Index-1] " " s.TrimL
		          Source[s.Index] = "" ;
		 ;
		 ----------------- 'remove (*$ifdef al4*) / (*$endif*)'
		 FOR s IN Source[] :
		     CASE s.Trim ?
		     ["(*$ifdef al4*)"]: BOOL clear_endif = TRUE
		    	 Source[s.Index] = ""
		     ["(*$ifdef al4code*)"]: clear_endif = FALSE
		     ["(*$endif*)"]: 
		    	 CASE clear_endif ? Source[s.Index] = "" ;
		     ;
		 ;
		 FOR s IN Source[] :
		     CASE #CR !IN s && #LF !IN s || a[s.Index] > 0 ? CONTINUE s ;
		     Source[s.Index] = s.Replace_all(#CR, " ").Replace_all(#LF, " ")
		 ;
		 
		 
		 
		 -----------------------'.pas1al4'
		 Unicode_save(Full_path ".pas1al4", Source[]) 
		 ----------------- 'analyse pseudo Pascal code and convert it'
		 INT y|_line_current INDEXING STR = Line
		 CASE token("program") ?
		      CASE Compiler_.{Compiler}.All_classes[0].Parse != THIS ?
		           Err("Only the first unit of the application can start by "
		           "the keyword PROGRAM"._EOnly1Program)
		      ;
		 ELSE Wait("unit") ;
		 -------------------- 'unit name'
		 STR names|_of_unit[]
		 NONE = scan_pascal_names(names[])
		 CASE names[0] != Full_path.Extract_file_name.Change_ext("").Lower ?
		      Err("Name of unit must match its file name"._ENameUnitNotMatch)
		 ;
		 Wait(";")
		 Fin
		 
		 r[y] = "CLASS {" uppercase_first_letter(names[].Whole_name_of) 
		 		"}, UNTESTED"
		 --------------------- 'modifiers'
		 FOR line IN Source[Line TO *] :
		     CASE line == "" ? CONTINUE line ;
		     Line = line.Index
		     CASE !Symbol("{@") ? BREAK line ;
		     FOR xdummy IN [0 TO Last] :
		         STR id|entifier = Ident
		         CASE ?
		         [id IN ["abstract", "bad", "bitwise", "bytes",
		         	  "destructors", "infinite", "operators", "operations",
		         	  "recursive", "stopping"]] ?
		              r[y] = r[y] ", " id.Upper
		         ;
		         CASE !Symbol(",") ? BREAK xdummy ;
		     ;
		     Wait("}")
		     Fin
		 ;
		 r[y] = r[y] " :"
		 ---------------------- 'interface uses ...'
		 Wait("interface")
		 Fin
		 CASE token("uses") ?
		      y = Line
		      r[y] = "IMPORT: "
		      STR spl_im|port_splitted[]
		      --------------------------- 'uses list', REUSED
		      FOR xdummy IN [0 TO Last] :
		          id = Ident
		          id = delphi_unit_to_al4_class(id)
		          Split(spl_im[], id, ",")
		          FOR imp IN spl_im[] : import_class(y, imp.Trim) ;
		          CASE !Symbol(",") ? BREAK xdummy ;
		      ;
		      FOR dummy IN [Line TO Source[].Count-1] :
		          CASE End_of_line ? Fin ;
		      	  CASE !Symbol("{@") ? BREAK dummy ;
		      	  id = Skip_to("}")
		      	  Wait("}")
		      	  Split(spl_im[], id, ",")
		      	  FOR imp IN spl_im[] : import_class(y, imp.Trim) ;
		      ;
		      r[y] = r[y].TrimR.Remove_ending(",") " ."
		      Wait(";")
		      --------------------------- 'end of uses'
		      Fin
		      CASE from_kol && token("mirror") ?
		           NONE = Skip_to(";")
		           Wait(";")
		           Fin ;
		 ;
		 PUSH Line :
		 PUSH Pos :
		      FOR yy IN [Line TO Source[].Count-1] :
		          CASE Source[yy].Trim == "implementation" ?
		               Line = yy
		               Pos = Source[yy].Len+1
		               Fin
		               CASE token("uses") ?
		                    r[y] = r[y].Remove_ending(".") ", "
		                    LIKE .................. 'uses list' ;
		          ;
		      ;
		 ;
		 ;
		 --------------------- 'types (N x STRUCTURE + 1 x CLASS)'
		 in_al4 = 0
		 STR base_class = ""
		 BOOL first_after_uses = TRUE
		 FOR i IN [Line TO Source[].Count] :
		 	 CASE End_of_line ? Fin ;
		 	 CASE ?
		     [in_al4 == 0 && Symbol("(*$ifdef al4code*)")] ? in_al4 = 2
                Pos = Last+1
                CONTINUE i
		     [in_al4 != 0 && Symbol("(*$endif*)")] ? in_al4 = 0
		     	Pos = Last+1
		     	CONTINUE i 
		     ; 
		     CASE ?
             [in_al4 == 2] ?
                  INT iLine|_index INDEXING (STR,INT) = Line
                  a[iLine] = in_al4
                  r[Line] = Source[Line]
                  Pos = Last+1
                  CONTINUE i
		     [End_of_line] ? CONTINUE i ;
		     CASE ?
		     [base_class == "" && first_after_uses && Symbol("{@")] ?
		     	Wait("base")
		     	Wait("class")
		     	base_class = Ident
		     	base_class= type_convert(base_class)
		     	Wait("}")
				--------------------- 'BASE CLASS ...'
				CASE r[Line] != "" ? r[Line] = r[Line] #NL ;
				     
				r[Line] = r[Line] 
					"BASE CLASS " uppercase_first_letter(base_class)
					 
		      	base_line = Line
		      	                     
		     [token("type")] ?
		          CASE base_line <= 0 ? base_line = Line ; 
		          pascal_type_definitions
		     [token("function")] ?
		          pascal_function_procedure("function", FALSE, TRUE)
		     [token("procedure")] ?
		          pascal_function_procedure("procedure", FALSE, TRUE)
		     [token("const")] ? pascal_constants
		     [token("implementation")] ?
		          CASE End_of_line ? Fin ;
		          CASE token("uses") ? Pos = Last + 1 ;
		          BREAK i
		     [token("var") || token("threadvar")] ?
		          Err("Global variables are not supported"._EGlobalNotSupport)
		     ELSE 
		     	Err("Unknown/unsupported Pascal declaration in interface type"
		     	""._EUnknownPascal ": " Source[Line])
		     ;
		     first_after_uses = FALSE
		 ;
		 --------------------- 'implementation part'
		 in_al4 = 0
		 FOR dummy IN [Line TO Source[].Count] :
		     CASE End_of_line ? Fin ;
		     CASE ?
		     [in_al4 == 0 && Symbol("(*$ifdef al4code*)")] ? in_al4 = 2
		     	iLine = Line
		     	a[iLine] = in_al4
		     	Pos = Last+1
		     	CONTINUE dummy
		     [in_al4 != 0 && Symbol("(*$endif*)")] ?
		          Pos = Last+1
		          in_al4 = 0
		          CONTINUE dummy
		     ;
		     CASE  ?
		     [in_al4 == 2] ?
		          r[Line] = Source[Line]
		          Pos = Last+1
		          CONTINUE dummy
		     [in_al4 < 0] ?
		          Pos = Last+1
		          CONTINUE dummy
		     [End_of_line] ? CONTINUE dummy ;
		     CASE ?
		     [token("const")] ? pascal_constants
		     [token("function")] ? pascal_implementation_fun("function")
		     [token("procedure")] ? pascal_implementation_fun("procedure")
		     [token("constructor")] ?
		          PUSH in_construct = TRUE :
		          	   pascal_implementation_fun("constructor") ;
		     [token("destructor")] ?
		          PUSH in_destruct = TRUE :
		          	   pascal_implementation_fun("destructor") ;
		     [token("end")] ?
		          FOR i IN [r[].Count TO Line+1] : r[] << "" ;
		          r[Line] = r[Line] "END"
		          r[Line+1] = r[Line+1] "*" "/"
		          Wait(".")
		          BREAK dummy
		     
		     ELSE Err("Unknown/unsupported Pascal declaration in "
		     		  "implementation part"._EUnknownImplem
		     		  ": " Rest_of_line)
		     ;
		 ;
	; 
	;
	FOR s IN r[Line TO *] : Source[s.Index] = s.TrimR ;
	------------------------ 'final split by #NL'
	Line_numbers[].Clear
	FOR i IN [0 TO Source[].Count-1] : Line_numbers[] << (i+1) ;
	FOR s IN Source[* DOWNTO 0] :
	    CASE #NL !IN s ? CONTINUE s ;
	    STR ss|plitted[]
	    Split(ss[], s, #NL)
	    Source[].Delete(s.Index)
	    ------------------------------ 'line numbers correction also'
	    INT line_n|umber_to_duplicate = Line_numbers[s.Index]
	    Line_numbers[].Delete(s.Index)
	    a[].Delete(s.Index)
	    FOR sss IN ss[* DOWNTO 0] :
	        Source[].Insert(s.Index, sss)
	        a[].Insert(s.Index, 0)
	        Line_numbers[].Insert(s.Index, line_n) ;
	;
	----------------- 'final merging by comma'
	FOR s IN Source[*-1 DOWNTO 0] :
	    CASE a[s.Index] > 0 || a[s.Index-1] > 0 ? CONTINUE s ;
		CASE s.TrimR.Ending(",") ?
		     Source[s.Index] = s " " Source[s.Index+1]
		     Source[s.Index+1] = "" ;
	;
	r[].Clear
	----------------- 'save modified source'
	FOR s IN Source[] :
	    FOR i IN [s.Len TO 80] : s = s " " ;
	    s = s "//# " Line_numbers[s.Index].Str
	    CASE a[s.Index] ?
	    [1]: s = s " [AL-IV]" 
	    [2]: s = s " [AL-IV CODE]" ;
	    r[] << s ;
	Unicode_save(Full_path ".pas2al4", r[]) .
	
//METHOD Convert_SEAL :
//    ---------------------------- 'remove directive [SYNTAX=SEAL]'
//    FOR s IN Source[0 TO *] :
//        s = s.Trim
//        CASE s.[0]=="[" && s.[*]=="]" ? 
//             Source[s.INDEX] = "" 
//             BREAK s ;
//    ;
//    ----------- '1: merge lines accounting ; or { or } as the line end only'
//    FOR s IN Source[* DOWNTO 0] :
//        CASE s.Trim == "" ? 
//             FOR ss IN Source[s.INDEX-1 DOWNTO 0] :
//                 ss = ss.Remove_ending_comment.Trim
//                 CASE ss == "" ? CONTINUE ss ;
//                 DONE     
//                 BOOL is_start|_of_statement = ss.[*] IN "{};:?." 
//                 BREAK ss ;
//             CASE is_start ? CONTINUE s ;
//        ;
//        s = s.Remove_ending_comment.Trim
//        //DEBUG: 
//        //      CASE s.INDEX <= 92 ?
//        //           ---------------- 'stoj!' 
//        //      ;
//        //;
//        CASE s.[*] !IN "{};:?." && !s.Starting("--") ? 
//             Source[s.INDEX] = Source[s.INDEX].Remove_ending_comment
//                #NL Source[s.INDEX+1]
//                
//             Source[s.INDEX+1] = "" ;
//    ;
//    //DEBUG:
//    //      FOR sss IN Source[] :
//    //          STR stamp|ed_strings[] << sss.Replace_all(#NL, "$$") ;
//    //      Unicode_save(Full_path ".seal2al4", stamp[])
//    //;
//    ------------------------- '2: remove ; ending lines'
//    FOR s IN Source[* DOWNTO 0] :
//        s = s.Remove_ending_comment.TrimR
//        CASE s.[*] == ";" ? 
//             Source[s.INDEX].[s.Len-1] = #SP ; 
//    ;
//    ------------------------- '3: uppercase some keywords, { } --> : ; or ? ; '
//    FOR s IN Source[* DOWNTO 0] :
//        s = s.Remove_ending_comment.TrimR
//        CASE s == "" ? CONTINUE s ;
//        STR id|entifier_1st = s.TrimL.Starting_ident
//        CASE ?
//        [id==""] ? NONE
//        [id.[0] IN "bcdefimoprs"
//         && id IN ["case", "for", "push", "debug", "silent", 
//         "end", "else", "limit", "bool", "int", "str", "real", "fun", 
//         "function", "method", "override", "operator", "import"]] ? 
//             Source[s.INDEX] = Source[s.INDEX].Replace(id, id.Upper)
//             s = Source[s.INDEX].Remove_ending_comment.TrimR
//        [id == "if"] ?
//             Source[s.INDEX] = Source[s.INDEX].Replace("if", "CASE")
//             s = Source[s.INDEX].Remove_ending_comment.TrimR
//             FOR j IN [0 TO s.Len-1] :
//                 CASE s.[j-1] IN 
//                      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
//                      "0123456789_"     
//                      ?
//                      CONTINUE j ;
//                 CASE s.[j] !IN "Tt" ? CONTINUE j ;
//                 CASE s.[j TO j+3].Upper != "THEN" ? CONTINUE j ;
//                 CASE s.[j+4] IN
//                      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
//                      "0123456789_"     
//                      ?
//                      CONTINUE j ;
//                 s = s.[0 TO j-1] " " s.[j+4 TO *] //" ;"
//                 Source[s.INDEX] = s
//             ;
//        ;
//        CASE s.[*] == "{" ?
//            INT p|osition_bracket = s.Len-1 // s.Find_last("{")
//            CASE s.TrimL.Starting_ident == "CASE" ? 
//                 STR sym|bol_starting_block = "?" 
//            ELSE sym = ":" ;
//            s.[*] = sym
//            Source[s.INDEX] = s //.[p] = sym 
//            CONTINUE s ;
//        CASE s.TrimL.Starting("{") && !s.TrimL.Starting("{{") ? 
//             s.[s.Find("{")] = ":" ;
//        BOOL dot|ending = s.[*] == "."
//        STR wo_dot|_s_line_without_dot = if(dot, s.[0 TO *-1].TrimR)
//        CASE (dot && wo_dot.Ending("}") && !wo_dot.Ending("}}") 
//             || s.[*] == "}") 
//             && "{" !IN s 
//             ?
//             p = s.Find_last("}")
//             CASE dot ? Source[s.INDEX].[p] = #SP 
//             ELSE Source[s.INDEX].[p] = ";" ;
//        ;
//    ;
//    Unicode_save(Full_path ".canonical", Source[])
//    Line = 0
//    Pos = 0
//    .
    
FUN Starting_ident|ifier(
STR S|tring_to_extract_id) ==> STR
    :
    FOR i IN [0 TO S.Len-1] :
        CASE S.[i] IN "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ? 
             CONTINUE i 
        ELSE RESULT = S.[0 TO i-1] ==> ;
    ; .
	
METHOD uppercase_first_letter(STR S|tring_name) ==> STR :
	RESULT = S
	FOR i IN [0 TO S.Len-1] :
	    CASE Is_letter(RESULT.[i]) ? RESULT.[i] = RESULT.[i].Upper ==> ;
	; .
	
FUN get_last_ident(
STR S|tring_with_identifier_at_end) ==> STR
	:
	FOR i IN [S.Len-1 DOWNTO 0] :
	    CASE S.[i] IN "abcdefghijklmnopqrstuvwxyz" ?
	         RESULT = S.[i] RESULT
	    ELSE ==> ;
	; .
	
STR imported_units[]
BOOL delphi2al4|_unit_imported
STR imported_al4|_classes[]

METHOD import_class(
INT y|_line INDEXING STR, STR Class|_name)
    :
	CASE Class == "" || Class.Lower IN imported_units[] 
		 || "{" Class.Lower "}" IN imported_units[]
		 ?
	     ==> ;
	imported_units[] << Class.Lower
	CASE Class == "delphi2al4" ? delphi2al4 = TRUE ;
	CASE Class.[0] == "{" ?
	     r[y] = r[y] Class ", "
	     append_import(Class)
	ELSE r[y] = r[y] "{" Class.[0].Upper Class.[1 TO *] "}, "
		 append_import("{" Class.[0].Upper Class.[1 TO *] "}")
	; .

METHOD append_import(STR List|_of_classes) :
	STR spl|itted_list[]
	Split(spl[], List, ",")
	FOR s IN spl[] : imported_al4[] << s.Lower ; .
	
FUN mask_1_comment|_or_string(
INT L|ine_index_starting_comment INDEXING STR,
INT P|osition_starting_comment,
STR M|ask_strings_source[])
	:
	CASE M[L].[P] ?
	["'"]: M[L].[P] = #QU
		   INT e|nding_position = M[L].Find("'")
		   FOR i INDEXING STR IN [P+1 TO e] : M[L].[i] = #QU ;
		   
	["{"]: STR ending|_symbol = "}"
		   INT x|_fill_from
		   BOOL closed|_comment
		   ------------------ 'mask until ending symbol', REUSED 
		   e = M[L].Find(ending)
		   CASE e > 0 ?
		        FOR i INDEXING STR IN [P TO e] : M[L].[i] = " " ;
		        ==> ;
		   x = P
		   FOR n IN [L TO M[].Count-1] :
		       e = M[n].Find(ending)
		       CASE e < 0 ? e = M[n].Len-1
		       ELSE closed = TRUE
		       		e += ending.Len-1 ;
		       FOR i IN [P TO e] : M[n].[i] = " " ;
		       x = 0
		       CASE closed ? ==> ;
		   ;
		   ---------------------- 'ok, masked'
		   
	["(*"]: ending = "*)"
			LIKE ............ 'mask until ending symbol'
	; .
	
FUN clear_commented_chars(
STR S|tring_original, STR M|ask_spaces_in_place_of_comments) ==> STR
	:
	RESULT = S
	FOR i IN [0 TO S.Len-1] :
	    CASE M.[i] == " " ? RESULT.[i] = " " ;
	; .
		           
		   
FUN Min|imum_of_two_integers(INT A|_value_1, INT B|_value_2) ==> INT :
	CASE A < B ? RESULT = A
	ELSE RESULT = B ; .
		     
FUN Max|imum_of_two_integers(INT A|_value_1, INT B|_value_2) ==> INT :
	CASE A > B ? RESULT = A ;
	RESULT = B .
		     
INT base_line INDEXING STR = -1

METHOD pascal_type_definitions :
	CASE !End_of_line ?
	     Err("The keyword TYPE must be placed on a separate line"._EType1Line)
	;
	Fin
	INT n|umber_of_definition = -1
	STR base_t|ype_name
	FOR dummy IN [Line TO Source[].Count-1] :
		CASE End_of_line ? Fin ;
	    CASE Next_symbol("(*") ? ==> ;
	    n += 1
	    INT p|osition_before_identifier = Pos
	    STR id|entifier_or_keyword = Ident
	    CASE id IN ["function", "procedure", "implementation", "type",
	    	 "const", "var", "threadvar"]
	    	 ?
	         Pos = p ==> ;
	    -------------------------- 'name'
	    Pos = p
	    STR names|_of_type[]
	    STR original_name = scan_pascal_names(names[])
	    CASE ?
	    [from_kol && names[0].[0] == "p"] ?
	         Wait("=")
	         Wait("^")
	         CASE base_t == "" ? base_t = Ident
	         	  BOOL assigned_base = TRUE
	         ELSE NONE = Ident 
	         	  assigned_base = FALSE ;
	         CASE assigned_base && base_t.[1 TO *] != names[0].[1 TO *] ?
	              Err("Waiting for Pname = ^Tname but"._EPanmeTname 
	              ": " names[].Whole_name_of " = ^" base_t)
	         ;
	         Wait(";")
	         CONTINUE dummy
	    [names[0].[0] != "t"] ?
	         Err("Type must start from symbol 'T'") ;
	    names[0] = names[0].[1 TO *]
	    -------------------------- '='
	    Wait("=")
	    CASE End_of_line ? Fin ;
	    CASE Next_symbol("(*") ? NONE = Skip_to("*)")
	    	 Wait("*)")
	    	 CASE End_of_line ? Fin ;
	    ;
	    CASE Symbol("(") ?
	         enumeration(names[])
	         CONTINUE dummy ;
	    id = Ident
	    CASE id ?
	    ["class"]: class_or_structure_declaration(original_name, names[], TRUE)
	    ["record"]: class_or_structure_declaration("", names[], FALSE)
	    ["object"]:
	    	CASE !from_kol ?
	    	     Err("Objects are supported only for KOL original projects"
	    	     ""._EObjectsNotFromKOL)
	    	;
	    	class_or_structure_declaration(original_name, names[], TRUE)
	    ["packed"]:
	    	CASE token("record") ?
	    	     class_or_structure_declaration("", names[], FALSE)
	    	ELSE Err("Only packed records can be in input"._EPackedRecordsOnly)
	    	;
	    
	    ELSE Err("Only class, record or (enumeration) can be defined with "
	    	 "TYPE declaration but"._EOnlyClassRecordEnum " " id 
	    	 " " ("was found"._EWasFound) )
	    ;
	    CASE End_of_line ? Fin ;
	; .
	
METHOD enumeration(STR Names|_of_enum_type[]) :
	r[Line] = r[Line] "ENUM {" Names[].Whole_name_of "}: "
				
	FOR idummy IN [Pos TO Last] :
	    STR item_n|ames[]
	    NONE = scan_pascal_names(item_n[])
	    r[Line] = r[Line] "'" item_n[].Whole_name_of.Upper "', "
	    CASE !Symbol(",") ? BREAK idummy ;
	;
	r[Line] = r[Line].TrimR.Remove_ending(",") " ."
	Wait(")")
	Wait(";")
	Fin .
	
STR original_class|_unit_name
	
METHOD class_or_structure_declaration(
STR Original|_name,
STR Names|_of_class_or_structure[],
BOOL Can_be_class|_itself)
	:
	CASE Symbol(";") ? ==> ;
	CASE Symbol("(") ?
		 STR base_class = Ident
		 CASE ?
		 [base_class == "tform"] ? base_class = "{Form_delphi}"
		 [base_class.[0] == "t"] ?
			  base_class = type_convert(base_class)
			  CASE base_class == "{Object}" ? base_class = "" ;
		 ELSE Err("Base class name must start from the letter 'T'"._EBaseT)
		 ;
		 CASE !Can_be_class || base_line < 0 ?
		      Err("Only unit class can be inherited from another class"
		      ""._EOnlyUnitClassInherit)
		 ;
		 Wait(")")
		 CASE End_of_line ? Fin ;
	;
	CASE base_line > 0 && Can_be_class 
		 //&& Names[0] == Full_path.Extract_file_name.Change_ext("").Lower
		 ?
	     CASE base_class != "" && r[base_line] == "" ?
	          r[base_line] = "BASE CLASS: " base_class " ."
	          base_line = 0 
	     ;
	     BOOL is_class_unit = TRUE
	     original_class = Original
	ELSE BOOL is_structure = TRUE ;
	CASE is_structure ?
	     r[Line] = r[Line] "STRUCTURE {" Names[].Whole_name_of "} :"
	;
	BOOL is_public|_section_by_default = TRUE
	BOOL can_be_field = TRUE
	INT index_f|ield = 0
	FOR dummy IN [Line TO Source[].Count-1] :
	    CASE End_of_line ? Fin ;
	    index_f += 1
	    CASE ?
	    [from_kol && index_f == 1] ?
	    	INT bpos|ition_before = Pos
	    	STR field|_name = Ident
	    	CASE field == "form" ?
	    	     Wait(":")
	    	     Wait("pcontrol")
	    	     Wait(";")
	    	     CONTINUE dummy
	    	ELSE Pos = bpos
	    		 index_f += 1000 // still this is not MCK form
	    	;
        [from_kol && index_f == 2] ?
            STR dummy_f|orm_field = Ident
            Wait("=")
            NONE = Ident
            Wait("(")
            ------------------- 'redefine base class as {Form_delphi}'
            base_class = Ident
            base_class = type_convert(base_class)
            CASE base_class == "{Form}" ? base_class = "{Form_KOL}" ;
            CASE base_line > 0 ?
            	 r[base_line] = "BASE CLASS: " base_class " ." ;
            Wait(")")
            CONTINUE dummy
        [from_kol && index_f == 3] ?
        	CASE token("kolproject1") ? 
	             Wait(":")
	             Wait("tkolproject")
	             Wait(";")
        	ELSE NONE = Ident
	             Wait(":")
	             Wait("tkolform")
	             Wait(";")
	             index_f += 1000 ;
            CONTINUE dummy
        [from_kol && index_f == 4] ?
            NONE = Ident
            Wait(":")
            Wait("tkolform")
            Wait(";")
            CONTINUE dummy
	    ;
	    INT p|os_before_id = Pos
	    STR id|entifier = Ident
	    CASE id ?
	    ["public", "published"]: is_public = TRUE
	    	can_be_field = TRUE
	    ["protected", "private"]: is_public = FALSE
	    	can_be_field = TRUE
	    	CASE !is_class_unit ?
	    	     Err("Only unit class can have protected fields"._EStrucProtec)
	    	;
	    //["private", "published"]: Err("Not supported"._ENotSupport ": " id)
	    ["function", "procedure", "constructor", "destructor"]:
	    	can_be_field = FALSE
	    	CASE !is_class_unit ?
	    		 Err("Only class unit can contain methods"._EOnlyClassUnitMethods)
	    	;
	    	pascal_function_procedure(id, TRUE, is_public)
	    	CASE ?
	    	[id IN ["function", "procedure"] && token("virtual")] ?
	    	     Wait(";")
	    	[id IN ["function", "procedure"] && token("override")] ?
	    	     Wait(";")
	    	;    
	    	     
	    ["property"]:
	    	pascal_property
	    ["end"]:
	    		CASE is_structure ? r[Line] = r[Line] " ." ;
	    		Wait(";") 
	    		Fin ==>
	    ELSE
	    	CASE can_be_field ?
		    	 Pos = p
		    	 declare_field(is_class_unit, is_public) ;
	    ;
	    CASE End_of_line ? Fin ;
	; .
	
STRUCTURE {function_decl|aration} :
	STR Name|_of_function_unique_in_unit
	INT Line|_where_declared INDEXING STR
	BOOL Is_method
	BOOL Is_public .
	
{function_decl} all_fun|ctions_declared_in_interface_part[]
	

METHOD pascal_function_procedure(
STR Keyw|ord_function_procedure_etc|DUMMY,
BOOL Is_method|_of_unit_class,
BOOL Is_public|_function_or_method)
	:
	STR names|_of_function[]
	NONE = scan_pascal_names(names[])
	FOR i IN [0 TO all_fun[].Count-1] :
	    CASE names[0] == all_fun[i].Name ?
	         Err("Duplicating function/procedure declaration ("
	         "already was declared in line"._EDupFun " " all_fun[i].Line.Str 
	         ")")
	    ;
	;
    
    f_decl|aration = {function_decl}(Name= names[0], Line= Line,
    	Is_method= Is_method, Is_public= Is_public)
    	
    all_fun[] << f_decl.Clone
	CASE Symbol("(") ?
	     CASE ")" !IN Rest_of_line ?
	          go_to_line_containing(")") ;
		 NONE = Skip_to(")")
		 NONE = Symbol(")") ;
	CASE Symbol(":") ? 
	     NONE = Ident
	     CASE Symbol("{@") ?
	          NONE = Skip_to("}")
	          Wait("}") ;
	;
	Wait(";")
	CASE ?
	[token("virtual")]? Wait(";") 
	[token("override")] ? Wait(";") ;
	Fin .
	
METHOD pascal_property :
	STR names|_of_property[]
	NONE = scan_pascal_names(names[])
	CASE Symbol("[") ?
	     STR param|eter_of_property = Ident
	     Wait(":")
	     Wait("integer")
	     Wait("]") ;
	Wait(":")
	STR type_n|ame = scan_pascal_type
	STR t|ype_of_property = type_convert(type_n)
	-------------------------------- 'read & write'
	Wait("read")
	STR read|_field_or_method = Ident
	CASE Symbol("write") ?
	     STR wr|ite_field_or_method = Ident ;
	Wait(";")
	-------------------------------- 'read method'
	r[Line] = "METHOD " names[].Whole_name_of
	CASE param != "" ? r[Line] = r[Line] "(INT " param ")" ; 
	r[Line] = r[Line] " ==> " t " := " read " ."
	CASE wr != "" ?
	     --------------------------- 'write method'
	     r[Line] = r[Line] #NL "METHOD __write_" names[0] "("
	     CASE param != "" ? r[Line] = r[Line] "INT " param "," ;
	     
	     r[Line] = r[Line] t " Value|_to_set)" 
	     ", SETTER FOR " names[0] " : " wr " = Value ." ; .

METHOD go_to_line_containing(STR S|ubstring) :
	FOR y IN [Line+1 TO Source[].Count-1] :
		Line = y
		Pos = 0
		CASE S IN Source[y] ? ==> ;
	; . 

	
METHOD declare_field(
BOOL Is_class_unit|_field_otherwise_structure|DUMMY,
BOOL Is_public)
	:
	STR names|_of_var[]
	NONE = scan_pascal_names(names[])
	CASE Symbol(",") ?
	     PUSH Pos :
	     PUSH Line :
		      NONE = Skip_to(":")
		      declare_1_field(Is_public, names[]) ;
	     ;
	     CASE !in_param ? r[Line] = r[Line] #NL ;
	     ==>
	;
	declare_1_field(Is_public, names[]) .
	
METHOD declare_1_field(
BOOL Is_public, STR names|_of_field[])
	:
	Wait(":")
	CASE from_kol && names[*].Len < 8 ?
	     CASE names[*].[*] == "_" ?
	          names[] << names[*] "fields_var_"
	     ELSE names[] << names[*] "_field_var" ;
	;
    CASE Is_public ? 
	     STR name_f|ield = names[0].[0].Upper names[0].[1 TO *] 
  			   "" names[].Whole_name_of.Remove_starting(names[0])
    ELSE name_f = names[].Whole_name_of ;

	CASE token("array") ?
	     CASE token("of") ?
	          STR type_n|ame = scan_pascal_type
	          r[Line] = r[Line] type_convert(type_n) " " name_f "[]"
	          Wait(";")
	          Fin ==> ;
	     ----------------- 'fixed array bounds'
	     Wait("[")
	     Wait("0")
	     Wait("..")
	     Skip_sp
	     CASE Next_char IN "0123456789" ?
	          INT last|_index = Number
	     ELSE Err("Only numeric array bounds are supported"._ENumericArray) ;
	     Wait("]")
	     -------------------------- 'type of fixed array'
	     Wait("of")
	     type_n = scan_pascal_type
	     r[Line] = r[Line] type_convert(type_n) " " name_f "[" (last+1).Str "]"
	     Wait(";")
	     Fin ==> ;
	-------------------------- 'scalar field'
	type_n = scan_pascal_type
	STR t|ype_in_al_iv = type_convert(type_n)
	CASE ?
	["[" IN t] ?
	     STR arr|ay_specifier = t.[t.Find("[") TO *]
	     t = t.Remove_ending(arr)
	[t == "STR"] ?
	     CASE Symbol("[") ?
	          NONE = Skip_to("]")
	          Wait("]") ;
	;
	r[Line] = r[Line] t " " name_f arr
	Wait(";")
	Fin .
	
METHOD scan_pascal_type ==> STR :
	RESULT = Ident
	CASE Symbol(".") ?
		 RESULT = RESULT "." Ident ;
	CASE RESULT IN ["tstringlist", "pstrlist"] ?
	     CASE Symbol("{@[*]}") ? RESULT = "tstringlist[*]"
	     ELSE RESULT = "tstringlist" ;
	     ==> ;	
	CASE RESULT == "tlist" && Symbol("{@") ?
	     RESULT = Ident
	     //RESULT[0] = RESULT[0].Upper
	     ---------------------------- 'either [*] or []'
	     Wait("[")
	     CASE Symbol("*") ? STR array|_sizes = "[*]"
	     ELSE array = "[]" ;
		 Wait("]")
		 Wait("}")
		 RESULT = RESULT array ==> ;
	CASE Symbol("{@") ?
	     RESULT = Ident
	     CASE Symbol("[") ?
	          Wait("]")
	          RESULT = RESULT "[]" ;
	     Wait("}") ; .
	
METHOD type_convert(STR Type|_pascal) ==> STR :
	STR t|ype_without_array = Type
	CASE "[" IN t ?
		STR ar|ray_specifier = t.[t.Find("[") TO *]
		t = t.[0 TO t.Find("[")-1] ;
	CASE t ?
	["boolean"]: RESULT = "BOOL" ==>
	["byte"]: RESULT = "BYTE" ==>
	["integer", "smallint", "shortint", "word", "dword", "cardinal"]:
		RESULT = "INT" ==>
	["single", "double"]: RESULT = "REAL" ==>
	["string"]: RESULT = "STR" ==>
	;

	CASE "." IN t ?
	     STR parts|_of_type[]
	     Split(parts[], t, ".")
	     
	     RESULT = "{" parts[0].[0].Upper parts[0].[1 TO *] 
	     	"}.{" parts[1].[1 TO *] "}" ==>
	;
	CASE t.[0] != "t" && !(from_kol && t.[0] == "p") ?
         Err("Type of field/variable must be either in [BOOLEAN, BYTE, "
         "INTEGER, DOUBLE, SINGLE, STRING] or start from the letter 'T'"
         ""._ETypePascalMust ": " t)
    ;
	t = t.[1 TO *]
	FOR im IN imported_units[] :
	    CASE im.[0] == "{" ? im = im.[1 TO *-1] ;
	    CASE t == im ?
	         RESULT = "{" t.[0].Upper t.[1 TO *] "}" ==> ;
	;
	-------------------- 'usual classes convert'
	t = "{" t "}"
	CASE Type_convert_from[].Count == 0 ?
	     FOR s IN Compiler.Source_paths[] :
	         STR path|_possible = s.Trailing_slash
	         	 "Delphi_type_to_al4.convert"
	         	 
	         CASE !path.Exists ? CONTINUE s ;
		     STR text|_loaded[]
		     Text_load(path, text[])
		     STR spl|itted_line[]
		     FOR line IN text[] :
		         Split(spl[], 
		          line.Replace_all(#TAB, " ").Replace_all("  ", " "), " ")
		          
		         //STR line_m|erged =  spl[].Merge("")
		         Type_convert_from[] << spl[0]
		         Type_convert_to[] << spl[1] ;
	     ;
	;
	FOR s IN Type_convert_from[] :
	    CASE s == t ?
	         t = Type_convert_to[s.Index]
	         BREAK s ;
	;
	RESULT = t ar .
	
STR Type_convert_from[]
STR Type_convert_to[]
	    	     
METHOD pascal_constants :
	STR names|_of_a_constant[]
	INT n|umber_constants = -1
	FOR dummy IN [Line TO Source[].Count-1] :
		CASE End_of_line ? Fin ;
	    CASE Next_symbol("(*") ? ==> ;
	    n += 1
	    INT p|osition_before_identifier = Pos
	    STR id|entifier_scanned = Ident
	    CASE n > 0 && id IN ["const", "var", "threadvar",
	    	 "function", "procedure", "constructor", "destructor",
	    	 "type", "implementation"]
	    	 ?
	         Pos = p ==> ;
	    Pos = p
	    NONE = scan_pascal_names(names[])
	    CASE names[*].Len < 8 ? names[] << names[*] "_CONSTANT" ;
	    ------------------------ ': type'
	    CASE Symbol(":") ?
	         id = scan_pascal_type
	         CASE ?
	         [id == "array"] ?
	         	pascal_const_array(names[])
	         	CONTINUE dummy
	              
	         [id !IN ["boolean", "byte", "integer", "double",
	          "single", "string"]] ?
	              Err("Constants only of basic Pascal types are allowed: "
	              "BOOLEAN, BYTE, INTEGER, DOUBLE, SINGLE or STRING"
	              ""._EConstOnlyBasicTypes)
	         ;
	         STR start|ing_part = "CONSTANT " type_convert(id) 
	         			": " names[].Whole_name_of.Upper 
	         			
	    ELSE start = "CONSTANT INT : " names[].Whole_name_of.Upper
	    	 BOOL automatic|_type = TRUE ;
	    ------------------------- '= value'
	    Wait("=")
	    CASE ?
	    [Symbol("$")] ?
	         r[Line] = r[Line] start " = 0x" Skip_to(";") " ."
	    [Next_char IN "'#"] ?
	         CASE ?
	         [!automatic && start.Starting("CONSTANT INT")] ?
	              Err("Integer constant can not be defined with a string value"
	              ""._EIntConstStrValue)
	              
	         [automatic && start.Starting("CONSTANT INT")] ?
	              start = start.Replace("CONSTANT INT ", "CONSTANT STR ")
	         ;
	         STR s|tring_value = scan_pascal_string
	         r[Line] = r[Line] start " = " as_string_const(s) " ."
	    ELSE s = Skip_to(";")  
	    	 r[Line] = r[Line] start " = " s " ." ;
	    Wait(";")
	    Fin ; .
	    
METHOD pascal_const_array(STR names|_of_constant[]) :
	Wait("[")
	Wait("0")
	Wait("..")
	INT n|umber_of_items_dummy = Number
	Wait("]")
	-------------------- 'type'
	Wait("of")
	STR type_n|ame_of_items = scan_pascal_type
	-------------------- 'values'	
	Wait("=")
	Wait("(")
	STR type|_final = type_convert(type_n) 
	r[Line] = r[Line] "CONST " type ": " names[].Whole_name_of.Upper " = ["
	FOR idummy IN [0 TO Source[].Count.Max(Last)] :
	    CASE type_n ?
	    ["boolean"]: STR v_add|ed_value = Ident
	    			 CASE v_add !IN ["true", "false"] ?
	    			      Err("Invalid boolean constant"._EEnvalidBoolean
	    			      ": " v_add)
	    			 ;
	    ["byte", "integer", "dword", "word"]: v_add = Integer.Str
	    ["double"]: v_add = Real_number.Str
	    ["string"]: v_add = scan_pascal_string
	    			v_add = as_string_const(v_add)
	    ELSE Err("Unknown Pascal type " type_n) ;
	    r[Line] = r[Line] v_add ", "
	    CASE !Symbol(",") ? BREAK idummy ;
	    CASE End_of_line ? Fin ;
	;
	---------------------- 'ending'
	r[Line] = r[Line].TrimR.Remove_ending(",") "] ."
	Wait(")")
	Wait(";")
	Fin .
	    
	    
METHOD scan_const ==> STR :
	Skip_sp
	CASE Next_char ?
	["#", "'"]: RESULT = scan_pascal_string
				RESULT = as_string_const(RESULT)
	["$"]: Skip_char
		   RESULT = "0x" scan_hex.Hex(2)
	["-"]: RESULT = scan_number
	["+"]: Skip_char 
		   RESULT = scan_number
	ELSE RESULT = scan_number ; .  
	
METHOD scan_number ==> STR :
	RESULT = Integer.Str
	CASE Next_char == "." ?
	     CASE Next_symbol("..") ? ==> ;
	     RESULT = RESULT "."
	     FOR i|dummy IN [Pos TO Last] :
	     	 Skip_char
	         CASE Next_char IN "0123456789" ?
	              RESULT = RESULT Next_char
	         ELSE BREAK i ;
	     ;
	     CASE RESULT.[*] == "." ? RESULT = RESULT "0" ;
	;
	CASE Next_char IN "Ee" ?
	     Skip_char
	     RESULT = RESULT "e" Integer.Str ; .

METHOD scan_pascal_string ==> STR :
	FOR idummy IN [Pos TO Last] :
	    CASE ?
	    [Symbol("#")] ?
	         CASE Symbol("$") ? INT code|_scanned = scan_hex
	         ELSE code = Number ;
	         RESULT = RESULT code.Char
	    [Symbol("'")] ?
	         RESULT = RESULT Skip_to("'")
	         Skip_char
	         CASE Next_char == "'" ? RESULT = RESULT "'" ;
	    ELSE ==> ;
	; .
	
METHOD scan_hex ==> INT :
	STR v|alue_hexadecimal = ""
	FOR i|dummy IN [Pos TO Last] :
	    CASE Next_char IN "0123456789ABCDEFabcdef" ?
	         v = v Next_char
	         Skip_char
	    ELSE BREAK i ;
	;
	v = v.Upper
	FOR i IN [0 TO v.Len-1] :
	    CASE v.[i] IN "ABCDEF" ?
	         RESULT = RESULT * 16 + v.[i].Ord - "A".Ord + 10
	    ELSE RESULT = RESULT * 16 + v.[i].Ord - "0".Ord ;
	; . 
	         
	
FUN as_string_const(STR S|tring_value) ==> STR :
	BOOL q|outed_const = FALSE
	FOR i IN [0 TO S.Len-1] :
	    CASE ?
	    [S.[i] == #QU] ?
	         CASE q ? RESULT = RESULT #QU
	         		  q = FALSE ;
	         RESULT = RESULT "#QU"
	    [S.[i].Ord < " ".Ord] ?
	         CASE q ? RESULT = RESULT #QU
	         		  q = FALSE ;
			 RESULT = RESULT "#" S.[i].Ord.Str	         
	    ELSE
	    	 CASE !q ? RESULT = RESULT #QU
	    	 		   q = TRUE ;
	    	 RESULT = RESULT S.[i] ;
	;
	CASE q ? RESULT = RESULT #QU ;
	CASE RESULT == "" ? RESULT = #QU#QU  ; .
	
--------------------------------- 'Pascal implementation'

METHOD compare_declarations(
STR Decl|aration, STR Header|_implementation, INT Line_decl|ared)
	:
	STR d|ecl_prepared = Decl.Replace_all(#TAB, " ").Replace_all("  ", " ")
	d = d.Replace_all(" ;", ";").Replace_all(" :", ":")
	d = d.Replace_all("( ", "(").Replace_all(" )", ")")
	d = d.Replace_all(" (", "(").Replace_all(") ", ")")
	d = d.Remove_ending("virtual;").Remove_ending("override;").TrimR
	d = d.Replace_all(", ", ",").Replace_all("; ", ";")
	d = d.Replace_all(" ;", ";")
	----------------------------------- 'compress header too'
	STR h|eader_prepared = Header.Replace_all(#TAB, " ").Replace_all("  ", " ")
	h = h.Replace_all("( ", "(").Replace_all(" )", ")")
	h = h.Replace_all(", ", ",").Replace_all("; ", ";")
	h = h.Replace_all("( ", "(").Replace_all(" )", ")")
	h = h.Replace_all(" (", "(").Replace_all(") ", ")")
	h = h.Replace_all(" ;", ";").Replace_all(" :", ":")
	CASE h != d ?
	     Err("Method/function implementation is not matching its declaration"
	     ""._EDeclImplNotMatch ", " ("found"._EFound) ":"#NL h #NL#NL
	     "" ("but declaration in line"._EDeclInLine) " " Line_decl.Str 
	     " " ("was"._EDeclWas) ":"#NL d #NL)
	; .
	
BOOL in_construct
BOOL in_destruct

METHOD pascal_implementation_fun|ction(
STR Keyw|ord_function_procedure_etc)
	:
	STR names|_of_function[]
	NONE = scan_pascal_names(names[])
	CASE Symbol(".") ?
	     CASE names[].Count > 1 ?
	          Err("When implement a method, use only a short name of the "
	          "class"._EUseOnlyShortClassName)
	     ;
	     CASE names[0].[0] != "t" ?
	          Err("Class name must start from the letter 'T'"._EClassNameT)
	     ;
	     CASE names[0] != original_class ?
	          Err("Class name must match the unit class"._EClassMustMatch
	          ": " original_class)
	     ;
	     STR header|_to_compare = Keyw " " Rest_of_line
	     NONE = scan_pascal_names(names[])
	     BOOL is_method|_implementation = TRUE
	ELSE
		 header = Keyw " " names[].Whole_name_of " " Rest_of_line ;
	CASE "(" IN header || ";" !IN header ?
		 FOR yy IN [Line+1 TO Source[].Count-1] :
		     CASE ")" IN header ? BREAK yy ;
		     Source[Line] = Source[Line] " " Source[yy].Trim
		     header = header " " Source[yy].Trim
		     Reset_cache_last_pos
		     Source[yy] = "" ;
	;
	CASE names[*].Len < 8 ? names[] << names[*] "_function" ;
	---------------------- 'search among declared functions / methods'
	INT found_i|ndex = -1
	FOR i IN [0 TO all_fun[].Count-1] :
	    CASE all_fun[i].Name == names[0] ?
	         STR decl|aration = Source[all_fun[i].Line].Trim
	         CASE "(" IN decl || ";" !IN decl ?
	              decl = fullfill_decl(decl, all_fun[i].Line+1) ;
	         compare_declarations(decl, header.Trim, all_fun[i].Line)
	         CASE ?
	         [all_fun[i].Is_method && !is_method] ?
	              Err("Method implementation must specify function/procedure "
	              "name in form T<class>.<method>"._EMethodImplNameForm)
	         [!all_fun[i].Is_method && is_method] ?
	              Err("Function/procedure was not declared as a class method"
	              ""._EWasNotMethod ": " names[0])
	         ;
	         found_i = i
	         BREAK i ;
	;
	CASE found_i < 0 && is_method ?
	     Err("Method was not declared in the unit class"._EMethodNotDeclared
	     ": " names[].Whole_name_of)
	;
	------------------------------------- '<< METHOD/FUNCTION name|s'
	CASE ?
	[Keyw == "constructor"] ? r[Line] = r[Line] "CONSTRUCT"
	[Keyw == "destructor"] ? r[Line] = r[Line] "DESTRUCT"
	[is_method] ? r[Line] = r[Line] "METHOD"
	ELSE r[Line] = r[Line] "FUNCTION" ;
	CASE Keyw !IN ["constructor", "destructor"] ?
		 r[Line] = r[Line] " " names[].Whole_name_of ; 
	------------------------------------- 'parameters ?'
	CASE Symbol("(") ?
	     PUSH in_param = TRUE :
	     PUSH is_event_handler :
			  CASE ?
			  [Keyw == "destructor"] ?
			       Err("Destructor should not have parameters"
			       ""._EDestructorNoParam)
			  [Keyw == "constructor"] ?
			       Warn("Constructor parameters will be ignored"
			       ""._WConstructorNoParam)
			       BOOL ignore_params = TRUE
			       NONE = Skip_to(")")
			  ;
	     	  CASE !ignore_params ? r[Line] = r[Line] "(" ;
	     	  BOOL is_const|_parameter = FALSE
	     	  BOOL after_comma = FALSE
		      FOR xdummy IN [Pos TO Last] :
		      	  CASE ignore_params ? BREAK xdummy ;
		      	  CASE !after_comma ? is_const = token("const") ;
		      	  -------------------------------- 'var parameter ?'
		          CASE !is_const && token("var") ?
		               BOOL var|_modifier = TRUE ;
		          CASE var && Symbol("{@ignore}") ? var = FALSE ;
		          CASE var ?
		               Err("The 'VAR' parameter specification is not allowed "
		               "(use e.g. array parameters to return several results)"
		               ""._EVarParam)
		          ;
		          --------------------------- 'do declaration'
		          declare_parameter(is_const)
		          CASE ?
		          [Symbol(",")] ? after_comma = TRUE
		          [Symbol(";")] ? after_comma = FALSE
		          ELSE BREAK xdummy ;
		          CASE End_of_line ? Fin ;
		      ;
		      CASE !ignore_params ?
		      	   r[Line] = r[Line].TrimR.Remove_ending(",") ")" ;
		      --------------- ') closing parameters list'
		      Wait(")") ;
	     ;
	;
	CASE Symbol(":") ?
		 STR type_n|ame = scan_pascal_type
		 CASE type_n == "array" || "[" IN type_n ?
		      Err("Returning an array as a result is not allowed"._ERetArray)
		 ;
		 STR d_res|ulting_def = " ==> " type_convert(type_n) 
	     r[Line] = r[Line] d_res 
	     result_needed = TRUE
	ELSE result_needed = FALSE ;
	INT y|_line_header_of_function INDEXING STR = Line
	CASE Keyw IN ["procedure", "function"] ? r[Line] = r[Line] ", PUBLIC" ;
	Wait(";")
	------------------------------- 'modifiers ?'
	FOR dummy IN [Line TO Source[].Count-1] :
		CASE End_of_line ? Fin ;
	    CASE !Symbol("{@") ? BREAK dummy ;
	    FOR xdummy IN [Pos TO Last] :
	        STR mod|ifier = Ident
	        CASE mod !IN ["recursive", "callback", "new"] ?
	             Err("Unknown/unsupported function modifier"._EUnknownFunMod
	             ": " mod)
	        ;
	        r[y] = r[y] ", " mod.Upper
	        CASE !Symbol(",") ? BREAK xdummy ;
	    ;
	    Wait("}")
	    Fin ;
	r[y] = r[y] " :"
	------------------ 'local variables'
	PUSH declaring_local_var = TRUE :
		 FOR dummy IN [Line TO Source[].Count-1] :
		     CASE End_of_line ? Fin ;
		     CASE ?		     
		     [token("const")] ?
		          Warn("Local CONSTants are implemented as variables"
		          ""._WLocalConst)
		          
		          declare_var(TRUE)
		          CONTINUE dummy
		     [token("label")] ?
		          Err("LABELs are not supported, together with GOTO statements"
		          ""._EGotoNotSupported)
		     [!token("var")] ? BREAK dummy ;
		     	  CASE End_of_line ? Fin ;
		     declare_var(FALSE)
		 ;
	;
	CASE End_of_line ? Fin ;
	Wait("begin")
	r[Line] = r[Line] "--------- 'BEGIN (" names[].Whole_name_of ")'"
	------------------- 'block of code'
	pascal_block_code("end")
	r[Line] = r[Line] " ."
	Wait(";")
	Fin .
	
METHOD fullfill_decl(
STR Decl|aration_start, INT From|_line) ==> STR
	:
	RESULT = Decl
	FOR y IN [From TO Source[].Count-1] :
	    CASE ")" IN RESULT ? ==> ;
	    RESULT = RESULT " " Source[y].Trim ; .
	
	
BOOL declaring_local_var|iables
	
METHOD declare_var|iable_local(BOOL Is_const|ant_declared) :
	FOR dummy IN [Line TO Source[].Count-1] :
		declare_parameter(Is_const)
		CASE Is_const ? 
		     Wait("=")
		     STR value|_const = scan_const
		     r[Line] = r[Line] " = " value ;
		CASE !Symbol(",") ? Wait(";") ;
		CASE End_of_line ? Fin ;
		CASE ?
		[Next_token("begin")] ? ==>
		[Next_token("var")] ? ==>
		[Next_token("label")] ? ==>
		[Next_token("const")] ? ==> ;
	; .
	    
BOOL in_param|eters_list
BOOL is_event_handler|_possible
	
METHOD declare_parameter|_or_variable(
BOOL Is_const|_parameter)
	:
	STR names|_of_parameter[]
	NONE = scan_pascal_names(names[])
	names[0].[0] = names[0].[0].Upper
	CASE names[*].Len < 8 && names[].Count == 1 ?
	     CASE ?
	     [declaring_local_var] ? names[] << names[0] "_local_var"
	     [!in_param] ? NONE
	     [names[0] == "Sender"]? names[] << "Sender_object"
	     			 			 is_event_handler = TRUE 
	     [is_event_handler && names[0] == "Shift"]? names[] << "Shift_state"
	     [is_event_handler && names[0] == "Button"]? names[] << "Button_pressed"
	     [is_event_handler && names[0] == "X"]? names[] << "X_coordinate"
	     [is_event_handler && names[0] == "Y"]? names[] << "Y_coordinate"
	     ELSE names[] << names[*] "_parameter" ;
	;
	INT p|osition_after_comma = -1
	CASE Next_symbol(",") ?
	     p = Pos
	     INT line|_was_before = Line
	     NONE = Skip_to(":") 
	;
	---------------------------------------------- 'type of parameter'
	CASE ?
	[Symbol(":")] ?
		 CASE token("array") ?
			  CASE Symbol("[") ?
			       Wait("0..")
			       INT high_v|alue_dummy = Number
			       STR array_size = "[*]"
			  ELSE array_size = "[]" ;
			  Wait("of") ;
		 STR type_n|ame_of_parameter = scan_pascal_type
		 CASE array_size == "" && "[" IN type_n ?
		      array_size = extract_array_def(type_n)
		      type_n = type_n.[0 TO type_n.Find("[")-1]
		      BOOL do_not_check_const = TRUE 
		 ;
	[!Is_const] ?
	     Err("The variable/parameter must have data type specified"._EMustType
	     ": " names[].Whole_name_of)
	ELSE type_n = "integer"
	;
	---------------------------------------------- 'r[Line] = r[Line] parameter'
	CASE !do_not_check_const && in_param ?
		 CASE ?
		 [array_size == "[]" && Is_const] ?
		      Err("The 'CONST' specifier is not applicable to a dynamic array"
		      ""._EConstDynamicArray)
		 
		 [array_size == "[*]" && !Is_const] ?
		      Err("Fixed array should have the 'CONST' specifier"
		      ""._EFixArrNotConst)
		      
		 [array_size == "" && !Is_const && type_n == "string"] ?
		      Err("String parameter must be passed as 'CONST' argument"
		      ""._EStrNotConst)
		 ;
	;
	//DEBUG:
	//      CASE type_n == "tlongsize" ? NONE ;
	//;
	type_n = type_convert(type_n)
	CASE "[" IN type_n ?
	     array_size = type_n.[type_n.Find("[") TO *]
	     type_n = type_n.Remove_ending(array_size) ;
	CASE r[Line] != "" && !in_param ? r[Line] = r[Line] #NL ;
	r[Line] = r[Line] type_n " " names[].Whole_name_of array_size
	CASE in_param ? r[Line] = r[Line] ", " ;
	CASE p > 0 ? Pos = p 
				 Line = line ; .
	
FUN extract_array_def|_from_type_scanned(
STR Type|_scanned) ==> STR 
	:
	CASE Type.Ending("[]") ? RESULT = "[]" ==> ;
	CASE Type.Ending("[*]") ? RESULT = "[*]" ; .
	
BOOL result_needed|_for_function_converted

METHOD scan_pascal_names(STR Names|_to_scan_to[]) ==> STR :
	Names[].Clear
	RESULT = Ident
	Names[] << RESULT
	CASE Symbol("{@") ?
	     CASE ?
	     [Symbol("||")] ?
	          Names[] << Ident
	     [Symbol("|")] ?
	          Names[] << Names[0] Ident
	     ELSE Names[0] = Ident ;
	     CASE Symbol("|") ?
	          Names[] << Names[0] Ident
	          Wait("}") ==> ;
	     Wait("}") ;
	CASE Symbol("{|") ?
	     Names[] << Names[0] Ident
	     Wait("}") ; .

--------------------------- 'statements convert from Pascal'

STR indent|_out

METHOD pascal_block_code(STR End|_statement), RECURSIVE :
	PUSH indent = indent "    " :
	     INT in_al4|_code = 0
		 FOR dummy IN [Line TO Source[].Count-1] :
	     	 CASE End_of_line ? Fin ;
	     	 ------------------ 'conditional compiling'
             CASE Next_symbol("(*") ?
                  CASE ?
                  [in_al4 == 0 && Symbol("(*$ifdef al4*)")] ? in_al4 = 1
                  [in_al4 == 0 && Symbol("(*$ifdef al4code*)")] ? in_al4 = 2
                  [in_al4 > 0 && Symbol("(*$else*)")] ? in_al4 = -1
                  [in_al4 != 0 && Symbol("(*$endif*)")] ? in_al4 = 0 ;
                  CASE End_of_line ? CONTINUE dummy ;
             ;
             CASE ?
             [in_al4 == 2] ?
                  r[Line] = Source[Line]
                  Pos = Last+1
                  CONTINUE dummy
             [Next_symbol("(*$")] ? CONTINUE dummy
             [in_al4 < 0] ?
                  Pos = Last+1
                  CONTINUE dummy ;
             CASE End_of_line ? Fin ;
		     ------------------------------ 'block comment || end'
		     CASE ?
		     [Next_symbol("{@--")] ?
		          pascal_statement
		          CONTINUE dummy
		     ;
		     CASE token(End) ? ==> ;
		     ----------------------------- 'usual statements PASCAL --> AL-IV'
		     pascal_statement
		     CASE Symbol(";") ? Fin
		     					CONTINUE dummy ;
		     CASE End_of_line ? Fin ;
		     CASE ?
		     [Next_token("end")] ? NONE
		     [Next_token("else")] ? NONE
		     
		     ELSE Err("Symbol ';' can be omitted only just before END/ELSE"
		          ""._ENoSemicolonBeforeEnd)
		     ;
		 ;
	; .
	
METHOD pascal_block_code_2(
STR End1|_statement, STR End2|_statement) ==> STR, RECURSIVE
	:
	PUSH indent = indent "    " :
	     INT in_al4|_code = 0
		 FOR dummy IN [Line TO Source[].Count-1] :
		     CASE End_of_line ? Fin ;
             ------------------ 'conditional compiling'
             CASE ?
             [in_al4 == 0 && Symbol("(*$ifdef al4*)")] ? in_al4 = 1
             [in_al4 == 0 && Symbol("(*$ifdef al4code*)")] ? in_al4 = 2
             [in_al4 > 0 && Symbol("(*$else*)")] ? in_al4 = -1
             [in_al4 != 0 && Symbol("(*$endif*)")] ? in_al4 = 0 ;
             CASE End_of_line ? CONTINUE dummy ;
		     CASE ?
             [in_al4 == 2] ?
                  r[Line] = Source[Line]
                  Pos = Last+1
                  CONTINUE dummy
             [Next_symbol("(*$")] ? CONTINUE dummy
             [in_al4 < 0] ?
                  Pos = Last+1
                  CONTINUE dummy
		     [token(End1)] ? RESULT = End1 ==>
		     [token(End2)] ? RESULT = End2 ==>
		     [Next_symbol("{@--")] ?
		          pascal_statement
		          CONTINUE dummy ;
		     pascal_statement
		     CASE Symbol(";") ? Fin
		     					CONTINUE dummy ;
		     CASE End_of_line ? Fin ;
		 ;
	; .
	
METHOD pascal_statement, RECURSIVE :
    CASE ?
    [token("begin")] ? pascal_block_code("end")
    [token("if")] ? pascal_if
    [token("case")] ? pascal_case
    [token("for")] ? pascal_for
    [token("while")] ? pascal_while
    [token("do")] ? pascal_do_while
    [token("repeat")] ? pascal_repeat_until
    [token("with")] ? Err("'WITH' is not supported"._EWithNotSupported)
    [token("try")] ? pascal_try
    [Symbol("{@--")] ?
         NONE = Skip_to("'")
         Wait("'")
         STR text|_of_block_comment = Skip_to("'")
         Wait("'")
         r[Line] = r[Line] indent "-- '" text "'"
         Wait("}")
    ELSE pascal_simple_statement ; .
    
BOOL in_left_side|_of_assignment
    
METHOD pascal_simple_statement :
	//DEBUG:
	//      CASE Line >= 199 && Line <= 201 ?
	//           << "pascal_simple_statement at Line " Line.Str #NL
	//           	  "" Rest_of_line  
	//           BOOL interest = TRUE ;
	//;
	CASE ?
	[Symbol("{@none=}")] ?
	     STR dest|ination_variable = "NONE"
	     
	[token("exit")] ?
	     r[Line] = r[Line] indent "==>" ==>

	[token("break")] ?
	     r[Line] = r[Line] indent "BREAK " for_nesting[*] ==>

	[token("continue")] ?
	     r[Line] = r[Line] indent "CONTINUE " for_nesting[*] ==>

	ELSE 
		 PUSH in_left_side = TRUE : 
		 	  dest = scan_expression ;
	;
	BOOL ended|_statement = Next_symbol(";")
	ended ||= Next_token("else")
	ended ||= Next_token("end")
	ended ||= End_of_line
	
	//DEBUG :
	//      CASE interest ?
	//           << "dest=[" dest "], ended=" ended.Str >>;
	//;
	
	CASE ?
	[ended] ?
	     CASE ?
	     [dest == "inherited"] ?
	          CASE !in_construct && !in_destruct ? 
	               r[Line] = r[Line] indent "BASE" ;
	     [dest.Starting("inc(")] ?
	         STR var|iable_pushing = dest.["inc(".Len TO *-1]
	         STR parts|_camma_separated[]
	         Split(parts[], var, ",")
	         CASE parts[1] == "" ?
	         	  r[Line] = r[Line] indent var " += 1"
	         ELSE r[Line] = r[Line] indent parts[0] " += " parts[1] ;

	     [dest.Starting("dec(")] ?
	         var = dest.["inc(".Len TO *-1]
	         Split(parts[], var, ",")
	         CASE parts[1] == "" ?
	         	  r[Line] = r[Line] indent var " -= 1"
	         ELSE r[Line] = r[Line] indent parts[0] " -= " parts[1] ;
	         
	     [dest.Starting("freeandnil(") || dest.Starting("free_and_nil(")] ?
	         var = dest.[dest.Find("(")+1 TO *-1]
	         CASE Symbol("{@clear}") ?
	              r[Line] = r[Line] indent var ".Clear();"
	         ELSE r[Line] = r[Line] indent var " = NONE" ;

	     [dest.Ending(".free")] ?
	         var = dest.Remove_ending(".free")
	         CASE Symbol("{@clear}") ?
	              r[Line] = r[Line] indent var ".Clear();"
	         ELSE r[Line] = r[Line] indent var " = NONE" ;
	         
	     [!delphi2al4 || !dest.Starting("push(")] ?
		     r[Line] = indent dest
		     
	     [dest.Starting("push(")] ?
	         var = dest.[dest.Find("(")+1 TO *]
	         CASE "," IN var ? var = var.[0 TO var.Find(",")-1]
	         	  STR temp_val|ue_assigning = dest.[dest.Find(",")+1 TO *-1]
	         ;
	         r[Line] = indent "PUSH " var
	         CASE temp_val != "" ? r[Line] = r[Line] " = " temp_val ;
	         r[Line] = r[Line] " :"
	         Wait(";")
	         Fin
	         --------------- 'try finally'
	         Wait("try")
	         pascal_block_code("finally")
	         Wait("pop")
	         Wait("(")
	         Wait(var)
	         Wait(")")
	         Wait(";")
	         --------------- 'end'
	         r[Line] = r[Line] indent ";"
	         Wait("end")
	     ;
	     ==>
	[End_of_line] ?
	     r[Line] = r[Line] indent dest ==>
	;
	Wait(":=")
	CASE End_of_line ? Fin ;
	STR source|_expression = scan_expression
	CASE ?
	[from_kol && source == "newlist"] ?
	     r[Line] = r[Line] indent dest ".Clear" 
	[from_kol && source == "newstrlist"] ?
	     r[Line] = r[Line] indent dest ".Clear" 
	[source == ".Clear"] ?
	     r[Line] = r[Line] indent dest source
	[source.Ending(".create")] ?
	     source = source.Remove_ending(".create")
	     CASE ?
	     [source == "tlist"] ?
	          r[Line] = r[Line] indent dest "[].Clear"
	     [source == "tstringlist"] ?
	          r[Line] = r[Line] indent dest "[].Clear"
	     ELSE source = type_convert(source)
		      r[Line] = r[Line] indent dest " = " source ; 
	ELSE r[Line] = r[Line] indent dest " = " source ; .
	     
    
METHOD pascal_if, RECURSIVE :
	INT y|_where_if_line_converted INDEXING STR = Line
    STR cond|ition = scan_expression
    STR cond1|_the_1st = cond
    r[y] = r[y] indent "CASE ? [" cond "] ? "
    Wait("then")
    CASE End_of_line ? Fin ;
    PUSH indent = indent "    " :
    	 pascal_statement ;
    -------------------- 'else ?'
    INT count_elses = 0
    FOR dummy IN [Line TO Source[].Count-1] :
        CASE End_of_line ? Fin ;
     	CASE !token("else") ? BREAK dummy ;
     	count_elses += 1
     	CASE r[Line].Trim != "" ? r[Line] = r[Line] #NL ;
     	r[Line] = r[Line] indent "ELSE "
     	CASE End_of_line ? Fin ;
     	PUSH indent = indent "    " :
     		 pascal_statement ;
    ;
    ----------------- 'ending ;', REUSED
    CASE ?
    [r[Line] == ""] ? r[Line] = indent ";"
    [r[Line].TrimR.Ending(";")] ? r[Line] = r[Line] #NL indent ";"
    ELSE r[Line] = r[Line] " ;" ;
    ----------------- 'convert CASE ?[]? .... ELSE'
    CASE count_elses <= 1 ?
     	 STR st|atement = r[y].[r[y].Find("] ?")+3 TO *]
         r[y] = indent "CASE " cond1 " ? " st ; .

METHOD pascal_case, RECURSIVE :
	STR cond|ition = scan_expression
	Wait("of")
	r[Line] = r[Line] indent "CASE " cond " ?"
	Fin
	FOR dummy IN [Line TO Source[].Count-1] :
	    CASE token("end") ? BREAK dummy ;
	    
	    CASE token("else") ?
	         ------------------------ 'else clause'
	         r[Line] = r[Line] indent "ELSE"
	         CASE End_of_line ? Fin ;
	         PUSH indent = indent "    " :
	              pascal_block_code("end") ;
	         BREAK dummy
	    ;
	         
	    cond = scan_expression
	    FOR xdummy IN [Pos TO Last] :
	        CASE !Symbol(",") ? BREAK xdummy ;
	        cond = cond ", " scan_expression ;
	    Wait(":")
	    CASE End_of_line ? Fin ;
	    r[Line] = r[Line] indent "[" cond "]:"
	    ----------------------------- 'single statement or block in begin - end'
	    PUSH indent = indent "    " : pascal_statement ;
	    CASE End_of_line ? Fin ;
	    CASE ?
	    [Next_token("else")] ? NONE
	    [Next_token("end")] ? NONE
	    ELSE Wait(";")
		     Fin ;
	; 
	LIKE pascal_if .................. 'ending ;'
	.
	    
STR for_nesting|_labels[]
	    
METHOD pascal_for, RECURSIVE :
	STR for_var|iable_in_pascal_code = Ident
	STR var|iable_for = "__for_" for_var
	Wait(":=")
	STR from|_expression = scan_expression
	CASE token("to") ? STR by|_direction = " TO "
	ELSE Wait("downto")
		 by = " DOWNTO " ;
	STR to|_expression = scan_expression
	Wait("do")
	------------------- 'create FOR statement'
	r[Line] = r[Line] indent "FOR " var " IN [" from by to "] : " for_var 
		" = " var
		
	CASE End_of_line ? Fin ;
	for_nesting[] << var
	PUSH indent = indent "    " : pascal_statement ;
	for_nesting[].Delete(for_nesting[].Count-1)
	LIKE pascal_if .................. 'ending ;' .

METHOD pascal_while, RECURSIVE :
	STR cond|ition = scan_expression
	Wait("do")
	STR var|iable_for = "__while_" (Line+1).Str
	CASE cond == "true" ?
	     r[Line] = r[Line] indent "FOR, INFINITE :"
	ELSE r[Line] = r[Line] indent "FOR " var " IN [1 TO MAX_INT-1] :" ;
	for_nesting[] << var
	Fin
	------------- 'body'
	PUSH indent = indent "    " :
	     r[Line] = r[Line] indent "CASE !(" cond ") ? BREAK " var ";"
		 Wait("begin")
		 Fin
		 pascal_block_code("end") ;
	for_nesting[].Delete(for_nesting[].Count-1)
	LIKE pascal_if .................. 'ending ;' .
	
METHOD pascal_do_while :
	Err("DO WHILE is not implemented") .
	
METHOD pascal_repeat_until :
	STR var|iable_for = "_repeat_" (Line+1).Str
	r[Line] = r[Line] indent "FOR " var " IN [1 TO MAX_INT-1] :"
	for_nesting[] << var
	PUSH indent = indent "   " :
	     pascal_block_code("until")
	     STR cond|ition = scan_expression
	     r[Line] = r[Line] indent "CASE " cond " ? BREAK var ;" ;
	LIKE pascal_if .................. 'ending ;'
	for_nesting[].Delete(for_nesting[].Count-1) .
	
BOOL try_warned
	
METHOD pascal_try :
	CASE !try_warned ?
		 Warn("TRY block is just converted, EXCEPT clause is ignored"._WTryJust)
		 try_warned = TRUE ;
	CASE End_of_line ? Fin ;
	STR what|_ending = pascal_block_code_2("finally", "except")
	CASE what == "finally" ?
	     CASE End_of_line ? Fin ;
	     pascal_block_code("end")
	ELSE
		 FOR i IN [Line TO Source[].Count-1] :
		     Line = i
		     Pos = 0
		     CASE token("end") ? BREAK i ;
		 ;
	; .
	
----------------------- 'pascal expression'

METHOD scan_expression ==> STR, RECURSIVE :
	STR items|_of_same_level[]
	STR op|erations[]
	FOR xdummy IN [Pos TO Last] :
		items[] << scan_operand_expression
		Skip_sp
		CASE End_of_line ? Fin ;
		CASE ?
		[Symbol("<>")] ? op[] << "!="
		[Symbol("<=")] ? op[] << "<="
		[Symbol(">=")] ? op[] << ">="
		[Symbol("=")] ? op[] << "=="
		[Next_char IN "+-*=<>"] ? op[] << Next_char
			Skip_char
		[Next_char == "/"] ? op[] << "*" << Next_char
			Skip_char
			items[] << "1.0"
		[token("mod")] ? op[] << "%"
		[token("div")] ? op[] << "/"
		[token("and")] ?
		     CASE Symbol("{@&}") ? op[] << "&"
		     ELSE op[] << "&&" ;
		[token("or")] ?
		     CASE Symbol("{@|}") ? op[] << "|"
		     ELSE op[] << "||" ;
		[token("xor")] ? op[] << "^"
		[token("shl")] ?
		     CASE items[].Count > 1 ?
		          Err("'SHL' is not applicable, close into brackets (x SHL y)"
		          ""._EShlTooManyOperands)
		     ;
		     RESULT = items[0] ".ShiftL(" scan_operand_expression ")" ==>
		[token("shr")] ?
		     CASE items[].Count > 1 ?
		          Err("'SHR' is not applicable, close into brackets (x SHR y)"
		          ""._EShrTooManyOperands)
		     ;
		     RESULT = items[0] ".ShiftL(" scan_operand_expression ")" ==>
		[token("as")] ?
		     CASE items[].Count > 1 ?
		          Err("'AS' is not applicable, close into brackets (x AS y)"
		          ""._EAsTooManyOperands)
		     ;
		     STR type_n|ame_to_cast = scan_pascal_type
		     RESULT = items[0] "." type_convert(type_n) ==>
		[token("in")] ?
		     CASE items[].Count > 1 ?
		          Err("'IN' is not applicable, close into brackets (x IN y)"
		          ""._EInTooManyOperands)
		     ;
		     Wait("[")
		     RESULT = items[0] " IN [" scan_array_constants "]" ==>
		ELSE BREAK xdummy ;
		CASE End_of_line ? Fin ;
	;
	-------------- 'fix idiotic pascal operations order'
	FOR i IN [op[].Count-1 DOWNTO 0] :
	    CASE op[i] IN ["==", "!=", "<=", ">=", "<", ">", "<>"]
	    	 && (op[i-1] IN ["&&", "||"] || op[i+1] IN ["&&", "||"])
	    	 ?
	         items[i] = "(" items[i] op[i] items[i+1] ")"
	         items[].Delete(i+1)
	         op[].Delete(i) ;
	;
	RESULT = items[0]
	FOR i IN [0 TO op[].Count-1] :
	    RESULT = RESULT " " op[i] " " items[i+1] ; .
	    
METHOD scan_operand_expression ==> STR, RECURSIVE :
	-----------' {@} to skip 1 identifier (e.g. typecasting)'
	CASE ?
	[Symbol("{@}")] ?
	     NONE = Ident
	[Symbol("{@as()}")] ? STR as_what|_type = Ident
						  as_what = type_convert(as_what)
						  Wait("(")
						  RESULT = scan_expression "." as_what
						  Wait(")")
						  RESULT = finish_operand(RESULT) ==>
	;
	CASE ?
	[Symbol("(")] ? RESULT = "(" scan_expression ")" 
					Wait(")")
					Skip_sp
					CASE End_of_line ? Fin ;
					CASE Next_char IN ".[" ?
					     RESULT = scan_chained_operand(RESULT) ;
					CASE ?
					[Symbol("{@.")] ?
					     RESULT = RESULT "." Skip_to("}")
					     Wait("}")
					[Symbol("{@as")] ?
					     RESULT = RESULT "." type_convert(Ident)
					     Wait("}")
					;
					==>
	[Symbol("+")] ? RESULT = scan_operand_expression ==>
	[Symbol("-")] ? RESULT = "-" scan_operand_expression ==>
	[Next_char IN "$'#0123456789"] ? RESULT = scan_const ==> ;
	CASE token("not") ?
	     CASE Symbol("{@~}") ? RESULT = "~" scan_operand_expression
	     ELSE RESULT = "!(" scan_operand_expression ")" ;
	     ==> ;
	----------------------- 'function call, variable, named constant'
	STR id|entifier = Ident
	RESULT = id
	INT pos|ition_before = Pos
	CASE Symbol("{@") ?
	     CASE Is_letter(Next_char) ?
		      id = Ident
		      BOOL ok|_ident_redirection_only = Symbol("}")
		      CASE ok ? RESULT = id ;
	     ;
	     CASE !ok ? Pos = pos ;
	;
	CASE End_of_line ? Fin ;
	CASE id ?
	["true", "false"]: RESULT = id.Upper ==>
	["nil"]: RESULT = "NONE" ==> ;
	----------- '{@Ident} to replace any identifier in an expression'
	CASE !Next_symbol("{@}") ?
	     CASE Symbol("{@") ?
		      RESULT = Skip_to("}")
		      Wait("}") ==> ;
	;
	RESULT = finish_operand(RESULT) .
	
METHOD finish_operand(STR A|lready_scanned) ==> STR :
	RESULT = A
	FOR xdummy IN [Pos TO Last] :
	    CASE ?
	    [Symbol("{@}")] ?
	         CASE Symbol(".") ? NONE ;
	         NONE = Ident
	         CONTINUE xdummy
	    [token("as")] ?
	         STR type_n|ame = scan_pascal_type
	         STR t|ype_final = type_convert(type_n)
	         CASE t.[0] != "{" ?
	              Err("Type cast with 'as' operation must be to a class"
	              ""._ECastNotToClass ": " t)
	         ;
	         RESULT = RESULT "." t
	         CONTINUE xdummy
	    [token("is")] ?
	         type_n = scan_pascal_type
	         t = type_convert(type_n)
	         CASE t.[0] != "{" ?
	              Err("Type check with 'is' operation must be to a class"
	              ""._ECheckTypeWithIsNotClass ": " t)
	         ;
	         RESULT = RESULT "." t " != NONE"
	         CONTINUE xdummy
	    ;
	    STR was|_before = RESULT
	    RESULT = scan_chained_operand(RESULT)
	    CASE was == RESULT ? ==> ;
	; .
	
METHOD scan_chained_operand(STR Prev|ious_value) ==> STR :
	RESULT = Prev
	CASE ?
	[Symbol("(")] ?
	     	CASE Prev == "assigned" ?
	     	     RESULT = Ident " != NONE"
	     	ELSE RESULT = Prev "(" scan_parameters ")" ;
	     	Wait(")")
	[Symbol("[")] ?
	     	CASE Symbol("{@()}") ?
	     	     RESULT = Prev "(" scan_parameters ")"
	     	     Wait("]")
	     	ELSE RESULT = Prev "[" scan_expression "]"
	     		 Wait("]") ;
	[Symbol(".")] ?
		 CASE Symbol("{@}") ?
		      NONE = Ident 
		      RESULT = RESULT " " ==> ;
		 INT was|_position = Pos
		 BOOL cond|ition = from_kol
		 cond &&= token("items") 
		 CASE cond ?
		      CASE Next_symbol("[") ? RESULT = RESULT " " ==> ;
		 	  Pos = was ;
		 	  
		 BOOL left_s|ide = in_left_side
	     CASE ?
	     [left_s && Next_token("add")] ?
	     	  Wait("add")
	     	  CASE Symbol("(") ?
	          	   RESULT = RESULT " << " scan_expression
	          	   Wait(")") ==> ;
	     	  Pos = was
	     	  
	     [Prev == "application" && Next_token("exename")] ?
	          Wait("exename")
	          RESULT = "(Start_dir Application_name)" ==>
	          
	     [Prev IN imported_units[]] ?
	          CASE ?
	          [Prev IN ["tstringlist", "tlist"] && token("create")] ?
	          	   skip_brackets
	          	   RESULT = ".Clear"
	          [token("create")] ?
	               Wait("(")
	               
	          	   RESULT = "{" Prev.[0].Upper Prev.[1 TO *] 
	          	   			"}(" scan_parameters ")"
	          	   			
	          	   Wait(")")
	          	       
	          ELSE RESULT = "{" Prev.[0].Upper Prev.[1 TO *] 
	          				"}." scan_operand_expression
	          ;
	          ==> 
	     ;
	     RESULT = Prev "." scan_operand_expression
	; .
	     
METHOD skip_brackets :
	CASE Symbol("(") ? Wait(")") ; .
	
METHOD scan_array_constants ==> STR :
	FOR xdummy IN [Pos TO Last] :
	    -------------------------- 'single constant'
	    STR sconst|_single = scan_const
	    RESULT = RESULT sconst ", "
	    CASE Symbol("..") ?
	         INT from|value = sconst.Int
	         INT to_v|alue = Number
	         FOR i IN [from+1 TO to_v] :
	             RESULT = RESULT i.Str ", " ;
	    ;
	    CASE !Symbol(",") ? BREAK xdummy ;
	;
	Wait("]")
	RESULT = RESULT.TrimR.Remove_ending(",") .
	
METHOD scan_parameters ==> STR :
	FOR xdummy IN [Pos TO Last] :
	    CASE Next_symbol(")") ? ==> ;
	    CASE Symbol("{@") ?
	         RESULT = RESULT Ident "= "
	         Wait("}") ;
	    RESULT = RESULT scan_expression ", "
	    CASE !Symbol(",") ? BREAK xdummy ;
	;
	RESULT = RESULT.TrimR.Remove_ending(",") .
	
STR Delphi_unit_from[]
STR Delphi_unit_to[]

METHOD delphi_unit_to_al4_class(STR Unit|_name) ==> STR :
	RESULT = Unit
	CASE Delphi_unit_from[].Count == 0 ?
	     FOR s IN Compiler.Source_paths[] :
	         STR path|_possible = s.Trailing_slash
	         	 "Delphi_units_to_al4.convert"
	         	 
	         CASE !path.Exists ? CONTINUE s ;
		     STR text|_loaded[]
		     Text_load(path, text[])
		     STR spl|itted_line[]
		     FOR t IN text[] :
		         Split(spl[],
		         	t.Replace_all(#TAB, " ").Replace_all("  ", " "), " ")
		         	
		         Delphi_unit_from[] << spl[0]
		         Delphi_unit_to[] << spl[1] ;
	     ;
	;
	FOR s IN Delphi_unit_from[] :
	    CASE s == Unit ? RESULT = Delphi_unit_to[s.Index] ==> ;
	;
	CASE RESULT.[0] == "{" ? ==> ;
	RESULT = "{" RESULT.[0].Upper RESULT.[1 TO *] "}" .
	 
END

<TYPE CONVERT>
{form}		{Form_Delphi}
{label}		{Label}
{bitbtn}	{Button}
{button}	{Button}
{paintbox}	{Paintbox}
{timer}		{Timer}
{bitmap}	{Bitmap}
{object}	{Object}
{mousebutton} 	{button}
{shiftstate}	INT
{mediaplayer}	{Mediaplayer}
{stringlist}	STR[]
<END>

<DELPHI UNITS TO AL4 CLASSES>
windows
messages
sysutils	{System_functions},{String_functions},{File_path}
variants
classes
graphics	{Canvas},{Geometry},{Bitmap},{Bitmap_direct},{Icon}
controls	{Control}
forms		{Screen_delphi},{Form_Delphi},{Form}
dialogs
extctrls	{Date_picker},{Paintbox},{Timer},{Separator},{Paint_tabs},
			{Paint_lines}
stdctrls	{Button},{Label},{Edit},{Memo},{Combo},{Checkbox},{Listbox}
buttons		{Paint_turtle}
math		{Mathematics}
mplayer		{Mediaplayer}
mmsystem
<END>
*/


