
%{
//OpenSCADA system module DAQ.JavaLikeCalc file: func_analise.ypp
/***************************************************************************
 *   Copyright (C) 2005-2025 by Roman Savochenko, <roman@oscada.org>       *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; version 2 of the License.               *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <string.h>

#include <tsys.h>
#include <tmess.h>

#include "freefunc.h"
#include "freelib.h"
#include "virtual.h"

#define YYERROR_VERBOSE true

namespace JavaLikeCalc
{

struct YYLTYPE;

int yylex( );
void yyerror( const char *s );
void sfErr( const string &mess, YYLTYPE *ps );

%}

/************************* GRAMMAR ****************************/
%union
{
    int	id;		//Function id
    Reg	*reg;		//Variable and Constant registers list index and function list index
    int	fp_cnt;		//Function parameters counter
    int	prg_cd;		//Program code
    char op[256];	//Object's properties, internal function
}

%token	ERR VAR_DIR IF ELSE WHILE FOR IN BREAK CONTINUE ADDEQ SUBEQ MULEQ DIVEQ BITSHLEFT BITSHRIGHT RETURN NEW DELETE OBJECT ARRAY REGEXP
%token	<id>	FUNC INT_FUNC B_FUNC B_FUNC1 B_FUNC2
%token	<reg>	VAR CONST FUNC_R
%token	<op>	O_PRP_F FUNCTION
%type	<reg>	var expr
%type	<prg_cd>	if if_expr OR_expr AND_expr end cycl fnc_in
%type	<fp_cnt>	prmlst varlst

%right	'=' ADDEQ SUBEQ MULEQ DIVEQ
%right	'?' ':'
%left	OR AND
%left	'|' '&' '^'
%left	'>' '<' GE LE EQ NE
%left	BITSHLEFT BITSHRIGHT
%left	'+' '-'
%left	'*' '/' '%'
%right	UN_MINUS '!' '~'
%right	INCREM DECREM

%%
root:	solver end
	| error	{ sfErr("",&yylloc); YYABORT; }
solver:	/*empty*/
	| solver solve
solve:  assign ';'
	| var ';'	{ $1->free(); }
	| IF '(' expr ')' if solve end
	    { try{ pF->cdCond($3,$5,$7,$7); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| IF '(' expr ')' if solve end ELSE solve end
	    { try{ pF->cdCond($3,$5,$7,$10); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| WHILE cycl '(' expr end ')' solve end
	    { try{ pF->cdCycle($2,$4,$5,$8,0); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| FOR '(' assign ';' cycl expr end ';' assign end ')' solve end
	    { try{ pF->cdCycle($5,$6,$10,$13,$7); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| FOR '(' var IN cycl expr end ')' solve end
	    { try{ pF->cdCycleObj($5,$6,$7,$10,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| FUNCTION '(' varlst ')' fnc_in solve end
	    {
		if($5 > 65535) { sfErr(TSYS::strMess(_("Internal function inaccessible by offset %d > %d"),$5,65535), &@1); YYABORT; }
		uint16_t fSz = $7 - $5;
		pF->prg.replace($5+1, sizeof(fSz), (char*)&fSz, sizeof(fSz));
		pF->inFuncDef($1, -1);
	    }
	| BREAK ';'	{ pF->prg = pF->prg+(char)Reg::Break; }
	| CONTINUE ';'	{ pF->prg = pF->prg+(char)Reg::Continue; }
	| RETURN ';'	{ pF->prg += (char)Reg::EndFull; }
	| RETURN expr ';'
	    {
		if(pF->mInFnc.size()) {
		    struct SCode { uint8_t cod; uint16_t sz; uint8_t n; uint16_t rez; } __attribute__((packed));
		    const struct SCode *ptr = (const struct SCode *)(pF->prg.data()+pF->inFuncGet(pF->mInFnc));
		    pF->cdMove(pF->regAt(ptr->rez), $2);
		}
		else for(int i_io = 0; i_io < pF->ioSize(); i_io++)
			if(pF->io(i_io)->flg()&IO::Return) {
			    Reg *rez = pF->regAt(pF->regGet(pF->io(i_io)->id()));
			    if(!rez) {
				rez = pF->regAt(pF->regNew(true));
				rez->setName(pF->io(i_io)->id());
				rez->setVar(i_io); rez->setLock(true);
			    }
			    pF->cdAssign(rez, $2);
			    break;
			}
		pF->prg += (char)Reg::EndFull;
	    }
	| DELETE var ';'	{
		pF->prg += (uint8_t)Reg::Delete;
		uint16_t addr = $2->pos();
		pF->prg.append((char*)&addr, sizeof(uint16_t));
	    }
	| FUNC '(' prmlst ')' ';'
	    { try{ pF->cdExtFnc($1,$3,true); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| FUNC_R '(' prmlst ')' ';'
	    { try{ pF->cdExtFnc(-1,$3,true,$1); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| INT_FUNC '(' prmlst ')' ';'
	    { try{ pF->cdIntFnc($1,$3,true); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| '{' solver '}'
var:	VAR			{ $$ = $1; }
	| VAR_DIR VAR
	    {
		if(pF->mInFnc.size() && $2->inFnc().empty()) {	//Replacing one from global NS
		    Reg *repl = pF->regAt(pF->regNew(true));
		    repl->setName(pF->mInFnc+":"+$2->name()); repl->setLock(true);
		    $2 = repl;
		}
		$2->setType(Reg::Real); $$ = $2;
	    }
	| VAR VAR
	    {
		if($1->name() == "var") { $2->setType(Reg::Real); $$ = $2; }	//???? Maybe get code from the previous item
		else { sfErr(TSYS::strMess(_("Variable '%s' just after variable '%s'"),$2->name().c_str(),$1->name().c_str()),&@1); YYABORT; }
	    }
	| var O_PRP_F		{ $$ = pF->cdProp($1,$2); }
	| var '[' expr ']'	{ $$ = pF->cdProp($1,"",$3); }
	| var O_PRP_F '(' prmlst ')'
	    { try{ $$ = pF->cdObjFnc($1,$2,$4); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
assign: assign ',' assign
	| var '=' expr
	    {
		try {
		    if($1->type() == Reg::Free) $1->setType($3->vType(pF));
		    pF->cdAssign($1, $3);
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var ADDEQ expr
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    pF->cdBinaryOp(Reg::AddAss, $1, $3, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Add,$1,$3));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var SUBEQ expr
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    pF->cdBinaryOp(Reg::SubAss, $1, $3, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Sub,$1,$3));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var MULEQ expr
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    pF->cdBinaryOp(Reg::MulAss, $1, $3, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Mul,$1,$3));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var DIVEQ expr
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    pF->cdBinaryOp(Reg::DivAss, $1, $3, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Div,$1,$3));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var INCREM
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    Reg *reg = pF->regTmpNew(); *reg = 1;
		    pF->cdBinaryOp(Reg::AddAss, $1, reg, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Add,$1,reg));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var DECREM
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    Reg *reg = pF->regTmpNew(); *reg = 1;
		    pF->cdBinaryOp(Reg::SubAss, $1, reg, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Sub,$1,reg));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| /*empty*/
expr:	CONST	{ $$ = $1; }
	| expr CONST
	    {
		try {
		    if($1->type() != Reg::String || $2->type() != Reg::String)
			throw TError(pF->nodePath().c_str(),_("Error constant after constant"));
		    $1->val().s->append(*($2->val().s));
		    $2->free();
		    $$ = $1;
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var
	    {
		try {
		    if($1->type() == Reg::Free)
			throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    $$ = $1;
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var '=' expr
	    {
		try {
		    if($1->type() == Reg::Free) $1->setType($3->vType(pF));
		    pF->cdAssign($1, $3);
		    $$ = $1;
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var INCREM
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    $$ = pF->cdMove(NULL, $1);
		    Reg *reg = pF->regTmpNew(); *reg = 1;
		    pF->cdBinaryOp(Reg::AddAss, $1, reg, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Add,$1,reg));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| var DECREM
	    {
		try {
		    if($1->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $1->name().c_str());
		    $$ = pF->cdMove(NULL, $1);
		    Reg *reg = pF->regTmpNew(); *reg = 1;
		    pF->cdBinaryOp(Reg::SubAss, $1, reg, $1);
		    //pF->cdAssign($1, pF->cdBinaryOp(Reg::Sub,$1,reg));
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| INCREM var
	    {
		try {
		    if($2->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $2->name().c_str());
		    Reg *reg = pF->regTmpNew(); *reg = 1;
		    pF->cdBinaryOp(Reg::AddAss, $2, reg, $2);
		    //pF->cdAssign($2, pF->cdBinaryOp(Reg::Add,$2,reg));
		    $$ = $2;
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| DECREM var
	    {
		try {
		    if($2->type() == Reg::Free) throw TError(pF->nodePath().c_str(), _("Variable '%s' is used but undefined"), $2->name().c_str());
		    Reg *reg = pF->regTmpNew(); *reg = 1;
		    pF->cdBinaryOp(Reg::SubAss, $2, reg, $2);
		    //pF->cdAssign($2, pF->cdBinaryOp(Reg::Sub,$2,reg));
		    $$ = $2;
		} catch(TError &err){ sfErr(err.mess,&@1); YYABORT; }
	    }
	| B_FUNC '(' ')'		{ try{ $$ = pF->cdBldFnc($1); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| B_FUNC1 '(' expr ')'		{ try{ $$ = pF->cdBldFnc($1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| B_FUNC2 '(' expr ',' expr ')'	{ try{ $$ = pF->cdBldFnc($1,$3,$5); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| FUNC '(' prmlst ')'		{ try{ $$ = pF->cdExtFnc($1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| FUNC_R '(' prmlst ')'		{ try{ $$ = pF->cdExtFnc(-1,$3,false,$1); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| INT_FUNC '(' prmlst ')'	{ try{ $$ = pF->cdIntFnc($1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| NEW OBJECT '(' ')'		{ try{ $$ = pF->cdMviObject(); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| NEW ARRAY '(' prmlst ')'	{ try{ $$ = pF->cdMviArray($4); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| NEW REGEXP '(' prmlst ')'	{ try{ $$ = pF->cdMviRegExp($4); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '+' expr	{ try{ $$ = pF->cdBinaryOp(Reg::Add,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '-' expr	{ try{ $$ = pF->cdBinaryOp(Reg::Sub,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '*' expr	{ try{ $$ = pF->cdBinaryOp(Reg::Mul,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '/' expr	{ try{ $$ = pF->cdBinaryOp(Reg::Div,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '%' expr	{ try{ $$ = pF->cdBinaryOp(Reg::RstI,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '|' expr	{ try{ $$ = pF->cdBinaryOp(Reg::BitOr,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '&' expr	{ try{ $$ = pF->cdBinaryOp(Reg::BitAnd,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '^' expr	{ try{ $$ = pF->cdBinaryOp(Reg::BitXor,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| '(' expr ')'	{ $$ = $2; }
//	| expr OR expr	{ try{ $$ = pF->cdBinaryOp(Reg::LOr,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr OR_expr expr end		{ try{ $$ = pF->cdCondBinaryOp($2,$1,$3,$4); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
//	| expr AND expr	{ try{ $$ = pF->cdBinaryOp(Reg::LAnd,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr AND_expr expr end	{ try{ $$ = pF->cdCondBinaryOp($2,$1,$3,$4); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr BITSHLEFT expr		{ try{ $$ = pF->cdBinaryOp(Reg::BitShLeft,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr BITSHRIGHT expr		{ try{ $$ = pF->cdBinaryOp(Reg::BitShRight,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '<' expr	{ try{ $$ = pF->cdBinaryOp(Reg::LT,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr '>' expr	{ try{ $$ = pF->cdBinaryOp(Reg::GT,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr GE expr 	{ try{ $$ = pF->cdBinaryOp(Reg::GEQ,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr LE expr 	{ try{ $$ = pF->cdBinaryOp(Reg::LEQ,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr EQ expr 	{ try{ $$ = pF->cdBinaryOp(Reg::EQU,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr NE expr 	{ try{ $$ = pF->cdBinaryOp(Reg::NEQ,$1,$3); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| '!' expr	{ try{ $$ = pF->cdUnaryOp(Reg::Not,$2); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| '~' expr	{ try{ $$ = pF->cdUnaryOp(Reg::BitNot,$2); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| '-' expr %prec UN_MINUS { try{ $$ = pF->cdUnaryOp(Reg::Neg,$2); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr if_expr expr end ':' expr end
	    { try{ $$ = pF->cdCond($1,$2,$4,$7,$3,$6); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
	| expr O_PRP_F		{ $$ = pF->cdProp($1,$2); }
	| expr '[' expr ']'	{ $$ = pF->cdProp($1,"",$3); }
	| expr O_PRP_F '(' prmlst ')'
	    { try{ $$ = pF->cdObjFnc($1,$2,$4); } catch(TError &err){ sfErr(err.mess,&@1); YYABORT; } }
prmlst: /*empty*/		{ $$ = 0; }
	| expr			{ $$ = 1; pF->fPrmst.push_front($1); }
	| prmlst ',' prmlst	{ $$ = $1 + $3; }
if:	/*empty*/	{ $$ = pF->prg.size(); pF->prg = pF->prg+(char)Reg::If+"RR00nn"; }
if_expr: '?'		{ $$ = pF->prg.size(); pF->prg = pF->prg+(char)Reg::If+"RR00nn"; }
OR_expr: OR		{ $$ = pF->prg.size(); pF->prg = pF->prg+(char)Reg::LCOr+"RRrrRRnn"; }
AND_expr: AND		{ $$ = pF->prg.size(); pF->prg = pF->prg+(char)Reg::LCAnd+"RRrrRRnn"; }
cycl:	/*empty*/	{ $$ = pF->prg.size(); pF->prg = pF->prg+(char)Reg::Cycle+"RRbbaann"; }
varlst: /*empty*/		{ $$ = 0; }
	| VAR			{ $$ = 1; pF->fPrmst.push_front($1); }
	| varlst ',' varlst	{ $$ = $1 + $3; }
fnc_in:	/*empty*/	{ $$ = pF->prg.size();
			    pF->prg = pF->prg + (char)Reg::IFuncDef + "NN";
			    pF->prg += (uint8_t)pF->fPrmst.size();
			    uint16_t addr;
			    int ckAddr;
			    //Place the return
			    if((ckAddr=pF->regGet("<<IN-RET>>")) < 0) {
				addr = pF->regNew(false, -1, false);
				pF->regAt(addr)->setName("<<IN-RET>>"); pF->regAt(addr)->setLock(true); //pF->regAt(addr)->setInFnc(false);
			    } else addr = ckAddr;
			    pF->prg.append((char*)&addr,sizeof(addr));
			    //Place the arguments
			    while(pF->fPrmst.size()) {
				pF->fPrmst.back()->setType(Reg::Real);
				addr = pF->fPrmst.back()->pos(); pF->prg.append((char*)&addr,sizeof(addr));
				pF->fPrmst.pop_back();
			    }
			}
end:	/*empty*/	{ pF->prg+=(char)Reg::End; $$ = pF->prg.size(); }
/************************* END OF GRAMMAR ****************************/
%%

void sfErr( const string &mess, YYLTYPE *ps )
{
    char m_buf[prmStrBuf_SZ];
    snprintf(m_buf, sizeof(m_buf), _("%s, pos: (%d,%d)-(%d,%d)."),
	mess.c_str(), ps->first_line, ps->first_column, ps->last_line, ps->last_column);
    yyerror(m_buf);
}

void yyerror( const char *s )	{ pF->pErr += s; }

int yylex( )
{
    unsigned c = 0, st_pos;
    unsigned &pos = pF->laPos;
    string &prog = pF->sprg;

    if(pos == 0) { yylloc.last_line = yylloc.last_column = 1; }

retry:
    //Empty space
    while(pos < prog.size() &&
	((c = prog[pos]) == ' ' || c == '\n' || c == '\t' ||
	c == '\f' || c == '\r' || c == '\v'))
    {
	if(c == '\n')	{ yylloc.last_column = 1; yylloc.last_line++; }
	else yylloc.last_column++;
	pos++;
    }
    //End string
    if(pos >= prog.size()) return 0;
    //Comentars // and /* ... */
    if(prog.substr(pos,2) == "//") {
	pos += 2;
	while(pos < prog.size() && prog[pos] != '\n') pos++;
	goto retry;
    }
    if(prog.substr(pos,2) == "/*") {
	pos += 3;
	while(pos < prog.size() && prog.substr(pos-1,2) != "*/") {
	    if(prog[pos] == '\n')	{ yylloc.last_column = 1; yylloc.last_line++; }
	    else yylloc.last_column++;
	    pos++;
	}
	pos++;
	goto retry;
    }

    yylloc.first_line = yylloc.last_line;
    yylloc.first_column = yylloc.last_column;

    //Number
    if(isdigit(c) || (c == '.' && (pos+1) < prog.size() && isdigit(prog[pos+1]))) {
	st_pos = pos;
	int tp = 0;	//Type constant (0-detect;1-dec;2-oct;3-hex;4-real)
	if(c == '.')	tp = 4;
	else {
	    while(pos < prog.size() && isdigit(c=prog[pos]))
	    { pos++; yylloc.last_column++; }
	    if(pos >= prog.size())			tp = (prog[st_pos]=='0')?2:1;
	    else if(c == 'x' || c == 'X')		tp = 3;
	    else if(c == '.' || c == 'e' || c == 'E')	tp = 4;
	    else tp = (prog[st_pos]=='0') ? 2 : 1;
	}
	if(tp == 1) {
	    yylval.reg = pF->regTmpNew();
	    *yylval.reg = (int64_t)strtoll(prog.substr(st_pos,pos-st_pos).c_str(),NULL,10);
	    return CONST;
	}
	else if(tp == 2) {
	    yylloc.last_column -= pos-st_pos;
	    pos = st_pos;
	    while(pos < prog.size() && (c=prog[pos]) >= '0' && c <= '7')
	    { pos++; yylloc.last_column++; }

	    yylval.reg = pF->regTmpNew();
	    *yylval.reg = (int64_t)strtoll(prog.substr(st_pos,pos-st_pos).c_str(),NULL,8);
	    return CONST;
	}
	else if(tp == 3) {
	    pos++; yylloc.last_column++;
	    while(pos < prog.size() && (isdigit(c=prog[pos]) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
	    { pos++; yylloc.last_column++; }

	    yylval.reg = pF->regTmpNew();
	    *yylval.reg = (int64_t)strtoll(prog.substr(st_pos,pos-st_pos).c_str(),NULL,16);
	    return CONST;
	}
	else if(tp == 4) {
	    bool e_ok = (c=='e'||c=='E') ? true : false;
	    pos++; yylloc.last_column++;
	    while(pos < prog.size() && (isdigit(c=prog[pos]) || c == 'E' || c == 'e' ||
		((c == '+' || c == '-') && ((prog[pos-1]) == 'e' || (prog[pos-1]) == 'E'))))
	    {
		if(c == 'E' || c == 'e') {
		    if(e_ok)	break;
		    else e_ok = true;
		}
		pos++; yylloc.last_column++;
	    }

	    yylval.reg = pF->regTmpNew();
	    *yylval.reg = s2r(prog.substr(st_pos,pos-st_pos));
	    return CONST;
	}
	else return 0;
    }
    //String
    if(c == '"' && (pos+1) < prog.size()) {
	st_pos = ++pos;
	string rez_str;
	yylloc.last_column++;
	while(pos < prog.size() && prog[pos] != '"' && prog[pos] != '\n') {
	    int elSzPass = 1;
	    if(prog[pos] == '\\' && (pos+1) < prog.size()) {
		switch(prog[pos+1]) {
		    case 'n':	rez_str += '\n';	break;
		    case 't':	rez_str += '\t';	break;
		    case 'b':	rez_str += '\b';	break;
		    case 'f':	rez_str += '\f';	break;
		    case 'r':	rez_str += '\r';	break;
		    case 'x': case 'X':
			if((pos+3) < prog.size() && isxdigit(prog[pos+2]) && isxdigit(prog[pos+3]))
			{ rez_str += (char)strtol(prog.substr(pos+2,2).c_str(),NULL,16); elSzPass += 2; }
			break;
		    default:
			if((pos+3) < prog.size() && prog[pos+1] >= '0' && prog[pos+1] <= '7' &&
						    prog[pos+2] >= '0' && prog[pos+2] <= '7' &&
						    prog[pos+3] >= '0' && prog[pos+3] <= '7')
			{ rez_str += (char)strtol(prog.substr(pos+1,3).c_str(),NULL,8); elSzPass += 2; }
			else rez_str += prog[pos+1];
		}
		elSzPass++;
	    }
	    else rez_str += prog[pos];
	    pos += elSzPass; yylloc.last_column += elSzPass;
	}
	if(pos >= prog.size()) {
	    yyerror(_("Unexpected end program, expect end string symbol '\"', "));
	    return ERR;
	}
	else if(prog[pos] == '\n') {
	    yyerror(_("The next line symbol failed into string, "));
	    return ERR;
	}
	yylloc.last_column++;

	yylval.reg = pF->regTmpNew();
	*yylval.reg = rez_str;
	//*yylval.reg = prog.substr(st_pos,pos-st_pos);
	pos++;
	return CONST;
    }
    //Symbols
    if(c == '_' || isalpha(c) || c > 0x7F) {
	bool is_dot = false;
	st_pos = pos;
	while(pos < prog.size() && ((c = prog[pos]) == '_' || c == '.' || isalnum(c) || c > 0x7F)) {
	    if(c == '.') is_dot = true;
	    pos++;
	    yylloc.last_column++;
	}
	string smb_nm = prog.substr(st_pos,pos-st_pos);
	//Keywords
	if(smb_nm == "if")	return IF;
	if(smb_nm == "else")	return ELSE;
	if(smb_nm == "while")	return WHILE;
	if(smb_nm == "for")	return FOR;
	if(smb_nm == "break")	return BREAK;
	if(smb_nm == "continue")return CONTINUE;
	if(smb_nm == "return")	return RETURN;
	if(smb_nm == "new")	return NEW;
	if(smb_nm == "delete")	return DELETE;
	if(smb_nm == "Object")	return OBJECT;
	if(smb_nm == "Array")	return ARRAY;
	if(smb_nm == "RegExp")	return REGEXP;
	if(smb_nm == "using") {
	    char buf[255]; buf[0] = 0;
	    while(pos < prog.size() && prog[pos] != ';') pos++;
	    if(pos < prog.size())
		sscanf(prog.substr(st_pos,pos-st_pos).c_str(),"using %254s",buf);
	    if(pos >= prog.size() || buf[0] == 0) {
		yyerror(_("Using namespace is error or empty, "));
		return ERR;
	    }
	    string fVal;
	    for(int off = 0; !(fVal=TSYS::strSepParse(pF->mUsings,0,';',&off)).empty(); )
		if(fVal == buf) break;
	    if(fVal.empty())	pF->mUsings = pF->mUsings+buf+";";
	    pos++;
	    goto retry;
	}
	if(smb_nm == "function") {
	    yylval.op[0] = 0;
	    while(pos < prog.size() && !(prog[pos] == '(' || prog[pos] == '=')) pos++;
	    if(pos < prog.size())
		sscanf(prog.substr(st_pos,pos-st_pos).c_str(),"function %254s",yylval.op);
	    if(pos >= prog.size() || yylval.op[0] == 0) { yyerror(_("Internal function declaration error, ")); return ERR; }
	    // Static function assign
	    if(prog[pos] == '=') {
		smb_nm = yylval.op;
		if(!(yylval.reg=pF->regAt(pF->regGet(EXT_F_AS_PREF+smb_nm)))) {
		    yylval.reg = pF->regAt(pF->regNew(true));
		    yylval.reg->setName(EXT_F_AS_PREF+smb_nm);
		    yylval.reg->setType(Reg::Function);
		    yylval.reg->setLock(true);
		}
		return VAR;
	    }
	    // Internal function define
	    if(pF->inFuncGet(yylval.op) >= 0) { yyerror(TSYS::strMess(_("Internal function '%s' defined early, "),yylval.op).c_str()); return ERR; }
	    if(pF->mInFnc.size()) { yyerror(_("Do not include declaration of one internal function to other, ")); return ERR; }
	    pF->inFuncDef(yylval.op, pF->prg.size());	//Early mark of the function context
	    return FUNCTION;
	}
	//Boolean
	if(smb_nm == "true" || smb_nm == "false") {
	    yylval.reg = pF->regTmpNew();
	    *yylval.reg = (smb_nm == "true") ? true : false;
	    return CONST;
	}

	//Check IO
	if(c != '(' && (yylval.reg=pF->regAt(pF->ioGet(smb_nm)))) return VAR;

	//Constants
	NConst *cnst_v;
	if((cnst_v = mod->constGet(smb_nm.c_str())) != NULL) {
	    yylval.reg = pF->regTmpNew();
	    switch(cnst_v->tp) {
		case TFld::Real:	*yylval.reg = TSYS::getUnalignDbl(cnst_v->val.data());	break;
		case TFld::Integer:	*yylval.reg = (int64_t)TSYS::getUnalign64(cnst_v->val.data());	break;
		case TFld::Boolean:	*yylval.reg = (char)cnst_v->val[0];	break;
		case TFld::String:	*yylval.reg = cnst_v->val;		break;
		default: break;
	    }
	    return CONST;
	}
	//Constant objects
	string sel = TSYS::strSepParse(smb_nm,0,'.');
	if(sel == "SYS" || sel == "arguments") {
	    yylval.reg = pF->regTmpNew();
	    yylval.reg->setType(Reg::Obj);
	    yylval.reg->setName(sel);
	    pos -= (smb_nm.size()-sel.size());
	    yylloc.last_column -= (smb_nm.size()-sel.size());
	    return VAR;
	}

	//Find buildin function
	BFunc *bf = NULL;
	if(c == '(' && (bf=mod->bFuncGet(smb_nm.c_str()))) {
	    yylval.id = bf->code;
	    switch(bf->prm) {
		case 0: return B_FUNC;
		case 1: return B_FUNC1;
		case 2: return B_FUNC2;
		default:
		    yyerror(_("Build in function has too much parameters, "));
		    return ERR;
	    }
	}

	//Find internal, external or object's functions execution
	if(c == '(') {
	    // Internal functions
	    if((yylval.id=pF->inFuncGet(smb_nm)) >= 0)	return INT_FUNC;

	    // Static linked external functions
	    if((yylval.reg=pF->regAt(pF->regGet(EXT_F_AS_PREF+smb_nm)))) return FUNC_R;

	    // External/static functions
	    if((yylval.id=pF->funcGet(smb_nm)) >= 0)	return FUNC;

	    // Check for object's functions and system object's function
	    //string sel = TSYS::strSepParse(smb_nm,0,'.');
	    if((yylval.reg=pF->regAt(pF->regGet(sel,true))) || (yylval.reg=pF->regAt(pF->ioGet(sel)))) {
		pos -= (smb_nm.size()-sel.size());
		yylloc.last_column -= (smb_nm.size()-sel.size());
		return VAR;
	    }

	    yyerror(_("Function error, "));
	    return ERR;
	}

	//Check allowed registers
	if((yylval.reg=pF->regAt(pF->regGet(smb_nm,true)))) return VAR;

	//Check for register as dynamic linked external function, mostly for allow it check for connection
	if((yylval.reg=pF->regAt(pF->regGet(EXT_F_AS_PREF+smb_nm)))) return VAR;

	//Global attributes and object's parameters.
	if(is_dot) {
	    // Check to parameter attribute (TValue)
	    try {
		AutoHD<TVal> vl = SYS->daq().at().at(TSYS::strSepParse(smb_nm,0,'.')).at().
						     at(TSYS::strSepParse(smb_nm,1,'.')).at().
						     at(TSYS::strSepParse(smb_nm,2,'.')).at().
						     vlAt(TSYS::strSepParse(smb_nm,3,'.'));
		yylval.reg = pF->regAt(pF->regNew(true));
		yylval.reg->setName(smb_nm);
		yylval.reg->setPAttr(vl);
		yylval.reg->setLock(true);
		return VAR;
	    } catch(...) { }

	    // Check for object's parameters
	    //string sel = TSYS::strSepParse(smb_nm,0,'.');
	    if((yylval.reg=pF->regAt(pF->regGet(sel,true))) || (yylval.reg=pF->regAt(pF->ioGet(sel)))) {
		pos -= (smb_nm.size()-sel.size());
		yylloc.last_column -= (smb_nm.size()-sel.size());
		return VAR;
	    }

	    yyerror(_("Symbol error, "));
	    return ERR;
	}
	//Check IO
	//if((yylval.reg=pF->regAt(pF->ioGet(smb_nm)))) return VAR;

	//Keywords
	if(smb_nm == "in")	return IN;
	if(smb_nm == "var")	return VAR_DIR;

	//Get new register
	yylval.reg = pF->regAt(pF->regNew(true));
	yylval.reg->setName(smb_nm);
	if(pF->mInFnc.size()) yylval.reg->setName(pF->mInFnc+":"+yylval.reg->name());
	yylval.reg->setLock(true);
	return VAR;
    }
    //Object's properties or functions
    else if(c == '.' && (pos+1) < prog.size()) {
	st_pos = ++pos;
	yylloc.last_column++;
	while(pos < prog.size() && ((c = prog[pos]) == '_' || isalpha(c) || (pos > st_pos && isdigit(c)) || c > 0x7F))
	{ pos++; yylloc.last_column++; }
	if((pos-st_pos) > 255) { yyerror(_("Object's properties too long, ")); return ERR; }
	strncpy(yylval.op,prog.substr(st_pos,pos-st_pos).data(),255);
	// pF->o_prpf.push_back(prog.substr(st_pos,pos-st_pos));
	return O_PRP_F;
    }
    //Multi symbol operations
    else if(prog.substr(pos,2) == "++") { pos += 2; yylloc.last_column += 2; return INCREM; }
    else if(prog.substr(pos,2) == "--") { pos += 2; yylloc.last_column += 2; return DECREM; }
    else if(prog.substr(pos,2) == "||") { pos += 2; yylloc.last_column += 2; return OR; }
    else if(prog.substr(pos,2) == "&&") { pos += 2; yylloc.last_column += 2; return AND; }
    else if(prog.substr(pos,2) == ">=") { pos += 2; yylloc.last_column += 2; return GE; }
    else if(prog.substr(pos,2) == "<=") { pos += 2; yylloc.last_column += 2; return LE; }
    else if(prog.substr(pos,2) == "==") { pos += 2; yylloc.last_column += 2; return EQ; }
    else if(prog.substr(pos,2) == "!=") { pos += 2; yylloc.last_column += 2; return NE; }
    else if(prog.substr(pos,2) == "<<") { pos += 2; yylloc.last_column += 2; return BITSHLEFT; }
    else if(prog.substr(pos,2) == ">>") { pos += 2; yylloc.last_column += 2; return BITSHRIGHT; }
    else if(prog.substr(pos,2) == "+=") { pos += 2; yylloc.last_column += 2; return ADDEQ; }
    else if(prog.substr(pos,2) == "-=") { pos += 2; yylloc.last_column += 2; return SUBEQ; }
    else if(prog.substr(pos,2) == "*=") { pos += 2; yylloc.last_column += 2; return MULEQ; }
    else if(prog.substr(pos,2) == "/=") { pos += 2; yylloc.last_column += 2; return DIVEQ; }
    else { pos++; yylloc.last_column++; }

    return c;
}

}
