コード例 #1
0
        /// <summary>
        /// Encode a ProgramCode Into Byte Array
        /// </summary>
        /// <param name="Tokens">Preprocessed list of tokens</param>
        /// <returns>byte array</returns>
        public static byte[] EncodeBytes(List <EditorTokenInfo> Tokens)
        {
            var result = new List <byte>();

            byte[] prgsize = { (byte)0x00, (byte)0x00 };
            result.AddRange(prgsize);

            Stack <int>            offsets = new Stack <int>();
            Stack <EditorJumpInfo> Jumps   = new Stack <EditorJumpInfo>();
            List <EditorLineInfo>  Lines   = new List <EditorLineInfo>();

            int offset = 1; //offset is a count of total encoded bytes

            int  tokenIndex   = 0;
            bool isFirstToken = true;


            for (tokenIndex = 0; tokenIndex < Tokens.Count; tokenIndex++)
            {
                var token = Tokens[tokenIndex];

                switch (token.TerminalName)
                {
                    #region IF IF+ IF- THEN ELSE
                case "IF":
                case "IF+":
                case "IF-":
                case "THEN":
                case "ELSE":
                case "EOE":
                    result.Add((byte)token.Token);
                    offset++;

                    if (token.Token == (short)LINE_TOKEN.EOE)
                    {
                        if (offsets.Count > 0)
                        {
                            int newoffset = offset + 1;
                            int idxoffset = offsets.Pop();
                            //encode and replace
                            short  NewOffSetNumber = Convert.ToInt16(newoffset);
                            byte[] byteoffset      = NewOffSetNumber.ToBytes();
                            result[idxoffset]     = byteoffset[0];
                            result[idxoffset + 1] = byteoffset[1];
                        }
                    }

                    break;


                    #endregion

                    #region REM COMMENTS
                case "REM":
                case "ASSIGN":

                    result.Add((byte)token.Token);
                    offset++;
                    break;

                case "Comment":
                case "PhoneNumber":
                    result.Add((byte)token.Type);     //Lenght of String
                    offset++;
                    result.AddRange(token.Text.ToBytes());
                    offset += token.Type;
                    break;
                    #endregion

                    #region Special Numbers
                case "LineNumber":
                    if (isFirstToken)
                    {
                        result.Add((byte)TYPE_TOKEN.NUMBER);     //TYPE OF NEXT TOKEN: NUMBER 1 Byte
                        short LineNumber = Convert.ToInt16(token.Text);
                        result.AddRange(LineNumber.ToBytes());   //LINE NUMBER, 2 Bytes
                        Lines.Add(new EditorLineInfo(Convert.ToInt32(LineNumber), Convert.ToInt32(offset + 1)));
                        offset      += 3;
                        isFirstToken = false;
                    }
                    else
                    {
                        //else: linenumber for a jump

                        short  LineNumber    = Convert.ToInt16(token.Text);
                        byte[] RawLineNumber = LineNumber.ToBytes();
                        RawLineNumber[1] = (byte)LINE_TOKEN.RAWLINE;
                        RawLineNumber[0] = (byte)LINE_TOKEN.RAWLINE;
                        result.AddRange(RawLineNumber);
                        Jumps.Push(new EditorJumpInfo(0, LineNumber, offset + 1));
                        offset += 2;
                    }


                    break;

                case "OFFSET":
                    //push index of next byte for new offset.
                    offsets.Push(offset + 1);
                    short OffSetNumber = Convert.ToInt16(token.Token);
                    result.AddRange(OffSetNumber.ToBytes());     //OFFSET NUMBER, 2 Bytes
                    offset += 2;

                    break;

                //Index and Counters: 1 Byte
                case "ARGCOUNT":
                case "SYSPRG":
                case "PRG":
                case "PANEL":

                    short  IdentCount      = Convert.ToInt16(token.Index);
                    byte[] bytesIdentCount = IdentCount.ToBytes();
                    result.Add(bytesIdentCount[0]);     //OFFSET NUMBER, 1 Bytes
                    offset += 1;

                    break;

                case "PRT_A":
                case "PRT_B":
                case "PRT_0":
                case "ALL":
                    short  prt      = Convert.ToInt16(token.Token);
                    byte[] bytesPrt = prt.ToBytes();
                    result.Add(bytesPrt[0]);     //OFFSET NUMBER, 1 Bytes
                    offset += 1;
                    break;

                case "WAITCOUNTER":
                    result.Add((byte)token.Token);
                    offset++;
                    int WaitCounter = Convert.ToInt32(token.Index);
                    result.AddRange(WaitCounter.ToBytes());     //OFFSET NUMBER, 4 Bytes
                    offset += 4;

                    break;
                    #endregion

                    #region Operators, Functions and Commands
                //OPERATORS
                case "PLUS":
                case "MINUS":
                case "MUL":
                case "DIV":
                case "POW":
                case "MOD":
                case "LT":
                case "GT":
                case "LE":
                case "GE":
                case "EQ":
                case "NE":
                case "AND":
                case "XOR":
                case "OR":
                //FUNCTIONS
                case "ABS":
                case "INTERVAL":
                case "_INT":
                case "LN":
                case "LN_1":
                case "SQR":
                case "_Status":
                case "CONPROP":
                case "CONRATE":
                case "CONRESET":
                case "TBL":
                case "TIME":
                case "TIME_ON":
                case "TIME_OFF":
                case "WR_ON":
                case "WR_OFF":
                case "DOY":
                case "DOM":
                case "DOW":
                case "POWER_LOSS":
                case "UNACK":
                case "SCANS":
                case "USER_A":
                case "USER_B":

                //COMMANDS
                case "START":
                case "STOP":
                case "CLEAR":
                case "RETURN":
                case "WAIT":
                case "HANGUP":
                case "GOTO":
                case "GOSUB":
                case "ON_ERROR":
                case "ON_ALARM":
                case "ENABLEX":
                case "DISABLEX":
                case "ENDPRG":
                case "RUN_MACRO":
                case "CALL":
                case "SET_PRINTER":
                case "DECLARE":
                case "PRINT_AT":
                case "ALARM_AT":
                case "PHONE":
                case "PRINT":
                case "OPEN":
                case "CLOSE":


                    result.Add((byte)token.Token);
                    offset++;
                    break;


                    #region Functions with variable list of expressions, must add count of expressions as last token.
                case "AVG":
                case "MAX":
                case "MIN":

                    result.Add((byte)token.Token);
                    result.Add((byte)token.Index);
                    offset += 2;
                    break;
                    #endregion

                case "NOT":
                    //TODO: Learn how to encode NOT operator -> tests and errors.
                    break;
                    #endregion

                    #region Identifiers: VARS, INS, OUTS, etc 3 bytes
                case "Identifier":
                case "VARS":
                case "INS":
                case "OUTS":
                    //Encode directly: Token + Index + Type

                    result.Add((byte)token.Token);
                    result.Add((byte)token.Index);
                    result.Add((byte)token.Type);
                    offset += 3;

                    break;

                    #endregion

                    #region NUMBERS (4 BYTES ONLY)
                case "Number":
                case "CONNUMBER":
                case "TABLENUMBER":
                case "TIMER":


                    result.Add((byte)token.Token);
                    offset++;
                    //And... voilá...  trick revealed.
                    //All numbers are converted to integer 32b, multiplying by 1000.
                    //on Decoding bytes: reverse operation dividing by 1000, so if not exact
                    //floating point numbers only have 3 decimals
                    int    numVal    = (int)(Convert.ToSingle(token.Text) * 1000);
                    byte[] byteArray = BitConverter.GetBytes(numVal);
                    result.AddRange(byteArray);
                    offset += 4;
                    break;
                    #endregion

                    #region EOF CRLF

                case "LF":
                    isFirstToken = true;

                    break;

                case "EOF":
                    if ((tokenIndex + 1) == Tokens.Count)
                    {
                        //EOF: Last LF, should be replaced with xFE
                        result.Add((byte)LINE_TOKEN.EOF);
                        //No increment to offset, as EOF doesn't count on
                    }

                    //EOL: LF, Just Ignored. Next Token should be another LineNumber
                    break;

                    #endregion

                default:
                    Trace.WriteLine($"Token ignored and not encoded: {token.ToString()}");
                    break;
                }
                isFirstToken = token.TerminalName == "LF" ? true : false;
            }

            //Set Jumps offsets
            if (Jumps.Count > 0 && Lines.Count > 0)
            {
                while (Jumps.Count > 0)
                {
                    EditorJumpInfo NewJump = Jumps.Pop();
                    var            LineIdx = Lines.FindIndex(k => k.Before == NewJump.LineIndex);
                    if (LineIdx >= 0)
                    {
                        short  LineOffset = Convert.ToInt16(Lines[LineIdx].After);
                        byte[] LObytes    = LineOffset.ToBytes();
                        result[NewJump.Offset]     = LObytes[0];
                        result[NewJump.Offset + 1] = LObytes[1];
                    }
                    else //LineIdx is -1, not found!
                    {
                        MessageBox.Show($"Error: Line not found: {NewJump.LineIndex}");
                    }
                }
            }


            offset--;
            byte[] size = offset.ToBytes();
            result[0] = size[0];
            result[1] = size[1];

            //fill with nulls til the end of block
            while (result.Count < 2000)
            {
                result.Add((byte)0x00);
            }
            return(result.ToArray());
        }
コード例 #2
0
        /// <summary>
        /// Encode a ProgramCode Into Byte Array
        /// </summary>
        /// <param name="Tokens">Preprocessed list of tokens</param>
        /// <returns>byte array</returns>
        public byte[] EncodeBytes(List <EditorTokenInfo> Tokens)
        {
            var result = new List <byte>();

            byte[] prgsize = { (byte)0x00, (byte)0x00 };
            result.AddRange(prgsize);

            Stack <int>            offsets = new Stack <int>();
            Stack <EditorJumpInfo> Jumps   = new Stack <EditorJumpInfo>();
            List <EditorLineInfo>  Lines   = new List <EditorLineInfo>();

            int offset = 1; //offset is a count of total encoded bytes

            int  tokenIndex     = 0;
            bool isFirstToken   = true;
            bool isTimeBuffered = false;

            for (tokenIndex = 0; tokenIndex < Tokens.Count; tokenIndex++)
            {
                var token = Tokens[tokenIndex];

                switch (token.TerminalName)
                {
                    #region IF IF+ IF- THEN ELSE
                case "IF":
                case "IF+":
                case "IF-":
                case "THEN":
                case "ELSE":
                case "EOE":
                    //TODO: Ver si es posible eliminar el último EOE antes de LF para evitar que se guarden como comas
                    result.Add((byte)token.Token);
                    offset++;

                    if (token.Token == (short)LINE_TOKEN.EOE)
                    {
                        if (offsets.Count > 0)
                        {
                            int newoffset = offset + 1;
                            int idxoffset = offsets.Pop();
                            //encode and replace
                            short  NewOffSetNumber = Convert.ToInt16(newoffset);
                            byte[] byteoffset      = NewOffSetNumber.ToBytes();
                            result[idxoffset]     = byteoffset[0];
                            result[idxoffset + 1] = byteoffset[1];
                        }
                    }

                    break;


                    #endregion

                    #region REM COMMENTS
                case "REM":
                case "ASSIGN":

                    result.Add((byte)token.Token);
                    offset++;
                    break;

                case "Comment":
                case "PhoneNumber":
                    result.Add((byte)token.Type);     //Lenght of String
                    offset++;
                    result.AddRange(token.Text.ToBytes());
                    offset += token.Type;
                    break;
                    #endregion

                    #region Special Numbers
                    #region Line Numbers
                case "LineNumber":
                    if (isFirstToken)
                    {
                        result.Add((byte)TYPE_TOKEN.NUMBER);     //TYPE OF NEXT TOKEN: NUMBER 1 Byte
                        short LineNumber = Convert.ToInt16(token.Text);
                        result.AddRange(LineNumber.ToBytes());   //LINE NUMBER, 2 Bytes
                        Lines.Add(new EditorLineInfo(Convert.ToInt32(LineNumber), Convert.ToInt32(offset + 1)));
                        offset      += 3;
                        isFirstToken = false;
                    }
                    else
                    {
                        //else: linenumber for a jump

                        short  LineNumber    = Convert.ToInt16(token.Text);
                        byte[] RawLineNumber = LineNumber.ToBytes();
                        RawLineNumber[1] = (byte)LINE_TOKEN.RAWLINE;
                        RawLineNumber[0] = (byte)LINE_TOKEN.RAWLINE;
                        result.AddRange(RawLineNumber);
                        Jumps.Push(new EditorJumpInfo(0, LineNumber, offset + 1));
                        offset += 2;
                    }
                    break;
                    #endregion

                case "OFFSET":     //2 Bytes
                    //push index of next byte for new offset.
                    offsets.Push(offset + 1);
                    short OffSetNumber = Convert.ToInt16(token.Token);
                    result.AddRange(OffSetNumber.ToBytes());     //OFFSET NUMBER, 2 Bytes
                    offset += 2;

                    break;

                //Index and Counters: 1 Byte
                case "ARGCOUNT":
                case "SYSPRG":
                case "PRG":
                case "PANEL":

                    short  IdentCount      = Convert.ToInt16(token.Index);
                    byte[] bytesIdentCount = IdentCount.ToBytes();
                    result.Add(bytesIdentCount[0]);     //OFFSET NUMBER, 1 Bytes
                    offset += 1;

                    break;

                case "PRT_A":
                case "PRT_B":
                case "PRT_0":
                case "ALL":
                    short  prt      = Convert.ToInt16(token.Token);
                    byte[] bytesPrt = prt.ToBytes();
                    result.Add(bytesPrt[0]);     //OFFSET NUMBER, 1 Bytes
                    offset += 1;
                    break;

                case "WAITCOUNTER":
                    result.Add((byte)token.Token);
                    offset++;
                    int WaitCounter = Convert.ToInt32(token.Index);
                    result.AddRange(WaitCounter.ToBytes());     //OFFSET NUMBER, 4 Bytes
                    offset += 4;

                    break;
                    #endregion

                    #region Operators, Functions and Commands
                //OPERATORS
                case "PLUS":
                case "MINUS":
                case "MUL":
                case "DIV":
                case "POW":
                case "MOD":
                case "LT":
                case "GT":
                case "LE":
                case "GE":
                case "EQ":
                case "NE":
                case "AND":
                case "XOR":
                case "OR":
                // Encode NOT token
                case "NOT":
                //FUNCTIONS
                case "ABS":
                case "INTERVAL":
                case "_INT":
                case "LN":
                case "LN_1":
                case "SQR":
                case "_Status":
                case "CONPROP":
                case "CONRATE":
                case "CONRESET":
                case "TBL":
                case "TIME":

                case "WR_ON":
                case "WR_OFF":
                case "DOY":
                case "DOM":
                case "DOW":
                case "POWER_LOSS":
                case "UNACK":
                case "SCANS":
                case "USER_A":
                case "USER_B":

                //COMMANDS
                case "START":
                case "STOP":
                case "CLEAR":
                case "RETURN":
                case "WAIT":
                case "HANGUP":
                case "GOTO":
                case "GOSUB":
                case "ON_ERROR":
                case "ON_ALARM":
                case "ENABLEX":
                case "DISABLEX":
                case "ENDPRG":
                case "RUN_MACRO":
                case "CALL":
                case "SET_PRINTER":
                case "DECLARE":
                case "PRINT_AT":
                case "ALARM_AT":
                case "PHONE":
                case "PRINT":
                case "OPEN":
                case "CLOSE":


                    result.Add((byte)token.Token);
                    offset++;
                    break;

                    #region Buffer time functions
                case "TIME_ON":
                case "TIME_OFF":
                    result.Add((byte)token.Token);
                    offset++;
                    //next token will be an identifier
                    isTimeBuffered = true;
                    break;
                    #endregion

                    #region Functions with variable list of expressions, must add count of expressions as last token.
                case "AVG":
                case "MAX":
                case "MIN":

                    result.Add((byte)token.Token);
                    result.Add((byte)token.Index);
                    offset += 2;
                    break;
                    #endregion

                    #endregion

                    #region Identifiers, Registers, VARS, INS, OUTS, etc 3 bytes
                case "Identifier":
                case "Register":
                case "VARS":
                case "INS":
                case "OUTS":
                case "PIDS":
                    //Encode directly: Token + Index + Type
                    if (!isTimeBuffered)
                    {
                        result.Add((byte)token.Token);
                        result.Add((byte)token.Index);
                        result.Add((byte)token.Type);
                        offset += 3;
                        //Register??
                        if (token.Token == (short)PCODE_CONST.REMOTE_POINT_PRG)
                        {
                            //We need to add 3 bytes: PanelID, Subnet and 1
                            result.Add((byte)Convert.ToInt16(token.PanelID));
                            result.Add((byte)Convert.ToInt16(token.Subnet));
                            result.Add((byte)1);
                            offset += 3;
                        }
                    }
                    else     //It's a time buffered function
                    {
                        //add to time buffer!!
                        short TimeBufferPos = (short)TimeBuff.Add((IdentifierTypes)token.Type, token.Index);
                        result.AddRange(TimeBufferPos.ToBytes());
                        offset += 2;
                    }
                    isTimeBuffered = false;
                    break;

                    #endregion

                    #region NUMBERS (4-5 BYTES ONLY)
                case "Number":
                case "CONNUMBER":
                case "TABLENUMBER":
                case "TIMER":
                case "WRNUMBER":

                    result.Add((byte)token.Token);
                    offset++;
                    //And... voilá...  trick revealed.
                    //All numbers are converted to integer 32b, multiplying by 1000.
                    //on Decoding bytes: reverse operation dividing by 1000, so if not exact
                    //floating point numbers only have 3 decimals
                    int    numVal    = (int)(Convert.ToSingle(token.Text) * 1000);
                    byte[] byteArray = BitConverter.GetBytes(numVal);
                    result.AddRange(byteArray);
                    offset += 4;
                    break;

                case "TimeLiteral":

                    //NEW: Added special treatment for TIME FORMAT numbers, see related TODO for DECODER
                    result.Add((byte)token.Token);
                    offset++;
                    string hh, mm, ss;
                    hh = token.Text.Substring(0, 2);
                    mm = token.Text.Substring(3, 2);
                    ss = token.Text.Substring(6, 2);
                    int numericvalue = Convert.ToInt32(hh) * 3600 + Convert.ToInt32(mm) * 60 + Convert.ToInt32(ss);
                    numericvalue *= 1000;
                    byte[] timeByteArray = BitConverter.GetBytes(numericvalue);
                    result.AddRange(timeByteArray);
                    //extra byte to mark a TIME FORMAT VALUE
                    result.Add((byte)token.Type);
                    offset += 5;
                    break;

                    #endregion


                    #region EOF CRLF

                case "LF":
                    isFirstToken = true;

                    break;

                case "EOF":
                    if ((tokenIndex + 1) == Tokens.Count)
                    {
                        //EOF: Last LF, should be replaced with xFE
                        result.Add((byte)LINE_TOKEN.EOF);
                        //No increment to offset, as EOF doesn't count on
                    }

                    //EOL: LF, Just Ignored. Next Token should be another LineNumber
                    break;

                    #endregion

                case "CMDSEPARATOR":
                default:
                    Trace.WriteLine($"Token ignored and not encoded: {token.ToString()}");
                    break;
                }
                isFirstToken = token.TerminalName == "LF" ? true : false;
            }

            //Set Jumps offsets
            if (Jumps.Count > 0 && Lines.Count > 0)
            {
                while (Jumps.Count > 0)
                {
                    EditorJumpInfo NewJump = Jumps.Pop();
                    var            LineIdx = Lines.FindIndex(k => k.Before == NewJump.LineIndex);
                    if (LineIdx >= 0)
                    {
                        short  LineOffset = Convert.ToInt16(Lines[LineIdx].After);
                        byte[] LObytes    = LineOffset.ToBytes();
                        result[NewJump.Offset]     = LObytes[0];
                        result[NewJump.Offset + 1] = LObytes[1];
                    }
                    else //LineIdx is -1, not found!
                    {
                        MessageBox.Show($"Error: Line not found: {NewJump.LineIndex}");
                    }
                }
            }

            //calculate SIZE of Program
            offset--;
            byte[] size = offset.ToBytes();
            result[0] = size[0];
            result[1] = size[1];

            //Add Time Buffer here

            short bufferCount = (short)TimeBuff.BufferCount;

            if (bufferCount > 0)
            {
                //Test if it fits
                byte[] btime = TimeBuff.ToBytes();
                if (result.Count > (2000 - btime.Length))
                {
                    throw new OverflowException($"Time Buffer not allocated, exceeding 2000 bytes. Current PRG size is {result.Count} and TimeBuffer requires {btime.Length}");
                }
                else
                {
                    result.AddRange(btime);
                }
            }

            //////fill with nulls til the end of block

            while (result.Count < 2000)
            {
                result.Add((byte)0x00);
            }
            return(result.ToArray());
        }