Esempio n. 1
0
 public void ParseG29()
 {
     DuetAPI.Commands.Code code = new DuetAPI.Commands.Code("G29 S1 ; load heightmap");
     Assert.AreEqual(CodeType.GCode, code.Type);
     Assert.AreEqual(29, code.MajorNumber);
     Assert.AreEqual(null, code.MinorNumber);
     Assert.AreEqual(1, code.Parameters.Count);
     Assert.AreEqual('S', code.Parameters[0].Letter);
     Assert.AreEqual(1, (int)code.Parameter('S', 0));
 }
Esempio n. 2
0
        public void ParseM92()
        {
            DuetAPI.Commands.Code code = new DuetAPI.Commands.Code("M92 E810:810:407:407");
            Assert.AreEqual(CodeType.MCode, code.Type);
            Assert.AreEqual(92, code.MajorNumber);

            Assert.AreEqual(1, code.Parameters.Count);

            int[] steps = { 810, 810, 407, 407 };
            Assert.AreEqual(steps, (int[])code.Parameter('E'));
        }
        /// <summary>
        /// Parse the next available G/M/T-code from the given stream asynchronously
        /// </summary>
        /// <param name="reader">Input to read from</param>
        /// <param name="result">Code to fill</param>
        /// <param name="buffer">Internal buffer for parsing codes</param>
        /// <returns>Whether anything could be read</returns>
        /// <exception cref="CodeParserException">Thrown if the code contains errors like unterminated strings or unterminated comments</exception>
        public static async Task <bool> ParseAsync(StreamReader reader, Code result, CodeParserBuffer buffer)
        {
            char   letter = '\0', lastC, c = '\0';
            string value = string.Empty;

            bool contentRead = false, unprecedentedParameter = false;
            bool inFinalComment = false, inEncapsulatedComment = false, inChunk = false, inQuotes = false, inExpression = false, inCondition = false;
            bool readingAtStart = buffer.SeenNewLine, isLineNumber = false, hadLineNumber = false, isNumericParameter = false, endingChunk = false;
            bool wasQuoted = false, wasCondition = false, wasExpression = false;
            int  numCurlyBraces = 0, numRoundBraces = 0;

            buffer.SeenNewLine = false;

            result.Flags      = buffer.EnforcingAbsolutePosition ? CodeFlags.EnforceAbsolutePosition : CodeFlags.None;
            result.Indent     = buffer.Indent;
            result.Length     = 0;
            result.LineNumber = buffer.LineNumber;

            do
            {
                // Check if the buffer needs to be filled
                if (buffer.BufferPointer >= buffer.BufferSize)
                {
                    buffer.BufferSize = await reader.ReadAsync(buffer.Buffer);

                    buffer.BufferPointer = 0;
                }

                // Read the next character
                lastC          = c;
                c              = (buffer.BufferPointer < buffer.BufferSize) ? buffer.Buffer[buffer.BufferPointer] : '\n';
                result.Length += reader.CurrentEncoding.GetByteCount(buffer.Buffer, buffer.BufferPointer, 1);
                buffer.BufferPointer++;

                if (c == '\n' && !hadLineNumber && buffer.LineNumber != null)
                {
                    // Keep track of the line number (if possible)
                    buffer.LineNumber++;
                }
                if (c == '\r')
                {
                    // Ignore CR
                    continue;
                }

                // Stop if another G/M/T code is coming up and this one is complete
                if (contentRead && !inFinalComment && !inEncapsulatedComment && !inCondition && !inChunk)
                {
                    char nextChar = char.ToUpperInvariant(c);
                    if (result.MajorNumber != null && result.MajorNumber != 53 && (nextChar == 'G' || nextChar == 'M' || nextChar == 'T') &&
                        (nextChar == 'M' || result.Type != CodeType.MCode || result.Parameters.Any(item => item.Letter == nextChar)))
                    {
                        // Note that M-codes may have G or T parameters but only one
                        buffer.BufferPointer--;
                        break;
                    }
                }

                if (inFinalComment)
                {
                    // Reading a comment ending the current line
                    if (c != '\n')
                    {
                        // Add next character to the comment unless it is the "artificial" 0-character termination
                        result.Comment += c;
                    }
                    continue;
                }

                if (inEncapsulatedComment)
                {
                    // Reading an encapsulated comment in braces
                    if (c != ')')
                    {
                        // Add next character to the comment
                        result.Comment += c;
                    }
                    else
                    {
                        // End of encapsulated comment
                        inEncapsulatedComment = false;
                        if (wasCondition)
                        {
                            inCondition  = true;
                            wasCondition = false;
                        }
                    }
                    continue;
                }

                if (inCondition)
                {
                    switch (c)
                    {
                    case '\n':
                        // Ignore final NL
                        break;

                    case ';':
                        inCondition    = false;
                        inFinalComment = true;
                        break;

                    case '{':
                        result.KeywordArgument += '{';
                        numCurlyBraces++;
                        break;

                    case '}':
                        result.KeywordArgument += '}';
                        numCurlyBraces--;
                        break;

                    case '(':
                        if (numCurlyBraces > 0)
                        {
                            result.KeywordArgument += '(';
                            numRoundBraces++;
                        }
                        else
                        {
                            inCondition           = false;
                            wasCondition          = true;
                            inEncapsulatedComment = true;
                        }
                        break;

                    case ')':
                        if (numRoundBraces > 0)
                        {
                            result.KeywordArgument += ')';
                            numRoundBraces--;
                        }
                        else
                        {
                            throw new CodeParserException("Unexpected closing round brace", result);
                        }
                        break;

                    default:
                        if (!char.IsWhiteSpace(c) || !string.IsNullOrEmpty(result.KeywordArgument))
                        {
                            // In fact, it should be possible to leave out whitespaces here but we here don't check for quoted strings yet
                            result.KeywordArgument += c;
                        }
                        break;
                    }

                    if (inCondition)
                    {
                        continue;
                    }
                }

                if (inChunk)
                {
                    if (inQuotes)
                    {
                        if (c == '"')
                        {
                            if (buffer.BufferPointer >= buffer.BufferSize)
                            {
                                buffer.BufferSize = await reader.ReadAsync(buffer.Buffer);

                                buffer.BufferPointer = 0;
                            }

                            char nextC = (buffer.BufferPointer < buffer.BufferSize) ? buffer.Buffer[buffer.BufferPointer] : '\0';
                            if (nextC == '"')
                            {
                                // Treat subsequent double quotes as a single quote char
                                value += '"';
                                buffer.BufferPointer++;
                                result.Length++;
                            }
                            else
                            {
                                // No longer in an escaped parameter
                                inQuotes    = false;
                                wasQuoted   = true;
                                endingChunk = true;
                            }
                        }
                        else
                        {
                            // Add next character to the parameter value
                            value += c;
                        }
                    }
                    else if (inExpression)
                    {
                        if (c == '{')
                        {
                            // Starting inner expression
                            numCurlyBraces++;
                        }
                        else if (c == '}')
                        {
                            numCurlyBraces--;
                            if (numCurlyBraces == 0)
                            {
                                // Check if the round braces are properly terminated
                                if (numRoundBraces > 0)
                                {
                                    throw new CodeParserException("Unterminated round brace", result);
                                }
                                if (numRoundBraces < 0)
                                {
                                    throw new CodeParserException("Too many closing round braces", result);
                                }

                                // No longer in an expression
                                inExpression  = false;
                                wasExpression = true;
                                endingChunk   = true;
                            }
                        }
                        else if (c == '(')
                        {
                            // Starting inner expression
                            numRoundBraces++;
                        }
                        else if (c == ')')
                        {
                            // Ending inner expression
                            numRoundBraces--;
                        }
                        value += c;
                    }
                    else if (c == ';')
                    {
                        inFinalComment = true;
                        inChunk        = endingChunk = false;
                    }
                    else if (c == '(')
                    {
                        inEncapsulatedComment = true;
                        inChunk = endingChunk = false;
                    }
                    else if (!endingChunk && string.IsNullOrEmpty(value))
                    {
                        if (char.IsWhiteSpace(c))
                        {
                            // Parameter is empty
                            endingChunk = true;
                        }
                        else if (c == '"')
                        {
                            // Parameter is a quoted string
                            inQuotes           = true;
                            isNumericParameter = false;
                        }
                        else if (c == '{')
                        {
                            // Parameter is an expression
                            value              = "{";
                            inExpression       = true;
                            isNumericParameter = false;
                            numCurlyBraces++;
                        }
                        else
                        {
                            // Starting numeric or string parameter
                            isNumericParameter = (c != 'e') && (c == ':' || NumericParameterChars.Contains(c)) && !unprecedentedParameter;
                            value += c;
                        }
                    }
                    else if (endingChunk ||
                             (unprecedentedParameter && c == '\n') ||
                             (!unprecedentedParameter && char.IsWhiteSpace(c)) ||
                             (isNumericParameter && c != ':' && !NumericParameterChars.Contains(c)))
                    {
                        // Parameter has ended
                        inChunk = endingChunk = false;
                    }
                    else
                    {
                        // Reading more of the current chunk
                        value += c;
                    }

                    if (endingChunk && c == '\n')
                    {
                        // Last character - process the last parameter being read
                        inChunk = endingChunk = false;
                    }
                }

                if (readingAtStart)
                {
                    isLineNumber = (char.ToUpperInvariant(c) == 'N');
                    if (char.IsWhiteSpace(c) && c != '\n')
                    {
                        if (result.Indent == byte.MaxValue)
                        {
                            throw new CodeParserException("Indentation too big", result);
                        }
                        result.Indent++;
                        buffer.Indent++;
                    }
                    else
                    {
                        readingAtStart = false;
                    }
                }

                if (!inCondition && !inChunk && !readingAtStart)
                {
                    if (letter != '\0' || !string.IsNullOrEmpty(value) || wasQuoted)
                    {
                        // Chunk is complete
                        char upperLetter = char.ToUpperInvariant(letter);
                        if (isLineNumber)
                        {
                            // Process line number
                            if (long.TryParse(value, out long lineNumber))
                            {
                                result.LineNumber = lineNumber;
                                buffer.LineNumber = lineNumber;
                            }
                            isLineNumber  = false;
                            hadLineNumber = true;
                        }
                        else if ((upperLetter == 'G' || upperLetter == 'M' || upperLetter == 'T') &&
                                 (result.MajorNumber == null || (result.Type == CodeType.GCode && result.MajorNumber == 53)))
                        {
                            // Process G/M/T identifier(s)
                            if (result.Type == CodeType.GCode && result.MajorNumber == 53)
                            {
                                result.MajorNumber = null;
                                result.Flags      |= CodeFlags.EnforceAbsolutePosition;
                                buffer.EnforcingAbsolutePosition = true;
                            }

                            result.Type = (CodeType)upperLetter;
                            if (wasExpression)
                            {
                                if (result.Type == CodeType.TCode)
                                {
                                    AddParameter(result, 'T', value, false, true);
                                }
                                else
                                {
                                    throw new CodeParserException("Dynamic command numbers are only supported for T-codes");
                                }
                            }
                            else if (value.Contains('.'))
                            {
                                string[] args = value.Split('.');
                                if (int.TryParse(args[0], out int majorNumber))
                                {
                                    result.MajorNumber = majorNumber;
                                    // Codes with unprecedented parameters are not dot-separated
                                }
                                else
                                {
                                    throw new CodeParserException($"Failed to parse major {char.ToUpperInvariant((char)result.Type)}-code number ({args[0]})", result);
                                }
                                if (sbyte.TryParse(args[1], out sbyte minorNumber) && minorNumber >= 0)
                                {
                                    result.MinorNumber = minorNumber;
                                }
                                else
                                {
                                    throw new CodeParserException($"Failed to parse minor {char.ToUpperInvariant((char)result.Type)}-code number ({args[1]})", result);
                                }
                            }
                            else if (int.TryParse(value, out int majorNumber))
                            {
                                result.MajorNumber     = majorNumber;
                                unprecedentedParameter = (upperLetter == 'M') && (majorNumber == 23 || majorNumber == 28 || majorNumber == 30 || majorNumber == 32 || majorNumber == 36 || majorNumber == 117);
                            }
                            else if (!string.IsNullOrWhiteSpace(value) || result.Type != CodeType.TCode)
                            {
                                throw new CodeParserException($"Failed to parse major {char.ToUpperInvariant((char)result.Type)}-code number ({value})", result);
                            }
                        }
                        else if (result.Type == CodeType.Comment && result.MajorNumber == null && result.Keyword == KeywordType.None && !wasQuoted && !wasExpression)
                        {
                            // Check for conditional G-code
                            if (letter == 'i' && value == "f")
                            {
                                result.Keyword         = KeywordType.If;
                                result.KeywordArgument = string.Empty;
                                inCondition            = true;
                            }
                            else if (letter == 'e' && value == "lif")
                            {
                                result.Keyword         = KeywordType.ElseIf;
                                result.KeywordArgument = string.Empty;
                                inCondition            = true;
                            }
                            else if (letter == 'e' && value == "lse")
                            {
                                result.Keyword = KeywordType.Else;
                            }
                            else if (letter == 'w' && value == "hile")
                            {
                                result.Keyword         = KeywordType.While;
                                result.KeywordArgument = string.Empty;
                                inCondition            = true;
                            }
                            else if (letter == 'b' && value == "reak")
                            {
                                result.Keyword = KeywordType.Break;
                                inCondition    = true;
                            }
                            else if (letter == 'c' && value == "ontinue")
                            {
                                result.Keyword = KeywordType.Continue;
                                inCondition    = true;
                            }
                            else if (letter == 'r' && value == "eturn")
                            {
                                result.Keyword         = KeywordType.Return;
                                result.KeywordArgument = string.Empty;
                                inCondition            = true;
                            }
                            else if (letter == 'a' && value == "bort")
                            {
                                result.Keyword = KeywordType.Abort;
                                inCondition    = true;
                            }
                            else if (letter == 'v' && value == "ar")
                            {
                                result.Keyword         = KeywordType.Var;
                                result.KeywordArgument = string.Empty;
                                inCondition            = true;
                            }
                            else if (letter == 's' && value == "et")
                            {
                                result.Keyword         = KeywordType.Set;
                                result.KeywordArgument = string.Empty;
                                inCondition            = true;
                            }
                            else if (letter == 'e' && value == "cho")
                            {
                                result.Keyword         = KeywordType.Echo;
                                result.KeywordArgument = string.Empty;
                                inCondition            = true;
                            }
                            else if (result.Parameter(letter) == null)
                            {
                                AddParameter(result, char.ToUpperInvariant(letter), value, false, false);
                            }
                            // Ignore duplicate parameters
                        }
                        else
                        {
                            if (letter == '\0')
                            {
                                letter = '@';
                            }
                            else if (!unprecedentedParameter)
                            {
                                letter = char.ToUpperInvariant(letter);
                            }

                            if (result.Parameter(letter) == null)
                            {
                                AddParameter(result, letter, value, wasQuoted, unprecedentedParameter || isNumericParameter || wasExpression);
                            }
                            // Ignore duplicate parameters
                        }

                        letter    = '\0';
                        value     = string.Empty;
                        wasQuoted = wasExpression = false;
                    }

                    if (c == ';')
                    {
                        // Starting final comment
                        contentRead = inFinalComment = true;
                    }
                    else if (c == '(' && !inExpression)
                    {
                        // Starting encapsulated comment
                        contentRead = inEncapsulatedComment = true;
                    }
                    else if (!char.IsWhiteSpace(c))
                    {
                        // Starting a new parameter
                        contentRead = inChunk = true;
                        if (c == '{')
                        {
                            value        = "{";
                            inExpression = true;
                            inQuotes     = false;
                            numCurlyBraces++;
                        }
                        else if (c == '"')
                        {
                            inQuotes = true;
                        }
                        else
                        {
                            letter = c;
                        }
                    }
                }
            } while (c != '\n');

            // Check if this was the last code on the line and if the state can be reset
            if (c == '\n')
            {
                result.Flags |= CodeFlags.IsLastCode;
                buffer.InvalidateData();
            }

            // Do not allow malformed codes
            if (inEncapsulatedComment)
            {
                throw new CodeParserException("Unterminated encapsulated comment", result);
            }
            if (inQuotes)
            {
                throw new CodeParserException("Unterminated string", result);
            }
            if (numCurlyBraces > 0)
            {
                throw new CodeParserException("Unterminated curly brace", result);
            }
            if (numCurlyBraces < 0)
            {
                throw new CodeParserException("Too many closing curly braces", result);
            }
            if (result.KeywordArgument != null)
            {
                result.KeywordArgument = result.KeywordArgument.Trim();
                if (result.KeywordArgument.Length > 255)
                {
                    throw new CodeParserException("Keyword argument too long (> 255)", result);
                }
            }
            if (result.Parameters.Count > 255)
            {
                throw new CodeParserException("Too many parameters (> 255)", result);
            }

            // M569, M584, and M915 use driver identifiers
            result.ConvertDriverIds();

            // End
            return(contentRead);
        }
Esempio n. 4
0
        /// <summary>
        /// Parse the next available G/M/T-code from the given stream
        /// </summary>
        /// <param name="reader">Input to read from</param>
        /// <param name="result">Code to fill</param>
        /// <param name="enforcingAbsolutePosition">If G53 is in effect for the current line</param>
        /// <returns>Whether anything could be read</returns>
        /// <exception cref="CodeParserException">Thrown if the code contains errors like unterminated strings or unterminated comments</exception>
        public static bool Parse(TextReader reader, Code result, ref bool enforcingAbsolutePosition)
        {
            if (enforcingAbsolutePosition)
            {
                result.Flags |= CodeFlags.EnforceAbsolutePosition;
            }

            char   letter = '\0', c;
            string value = "";

            bool contentRead = false;
            bool inFinalComment = false, inEncapsulatedComment = false, inChunk = false, inQuotes = false, inExpression = false, inCondition = false;
            bool readingAtStart = true, isLineNumber = false, hadLineNumber = false, isNumericParameter = false, endingChunk = false, wasQuoted = false;

            do
            {
                int currentChar = reader.Read();
                if (currentChar == '\n' && !hadLineNumber && result.LineNumber.HasValue)
                {
                    // Keep track of the line number (if possible)
                    result.LineNumber++;
                }

                c = (currentChar < 0) ? '\n' : (char)currentChar;
                if (c == '\r')
                {
                    // Ignore CR
                    continue;
                }

                if (inFinalComment)
                {
                    // Reading a comment ending the current line
                    if (c != '\n')
                    {
                        // Add next character to the comment unless it is the "artificial" 0-character termination
                        result.Comment += c;
                    }
                    continue;
                }

                if (inEncapsulatedComment)
                {
                    // Reading an encapsulated comment in braces
                    if (c != ')')
                    {
                        // Add next character to the comment
                        result.Comment += c;
                    }
                    else
                    {
                        // Even though RepRapFirmware treats comments in braces differently,
                        // the correct approach should be to switch back to reading mode when the comment tag is closed
                        inEncapsulatedComment = false;
                    }
                    continue;
                }

                if (inCondition)
                {
                    switch (c)
                    {
                    case '\n':
                        // Ignore final NL
                        break;

                    case ';':
                        inCondition    = false;
                        inFinalComment = true;
                        break;

                    case '(':
                        inCondition           = false;
                        inEncapsulatedComment = true;
                        break;

                    default:
                        if (!char.IsWhiteSpace(c) || result.KeywordArgument != "")
                        {
                            // In fact, it should be possible to leave out whitespaces here but we here don't check for quoted strings yet
                            result.KeywordArgument += c;
                        }
                        break;
                    }

                    if (inCondition)
                    {
                        continue;
                    }
                }

                if (inChunk)
                {
                    if (!endingChunk && value == "")
                    {
                        if (char.IsWhiteSpace(c))
                        {
                            // Parameter is empty
                            endingChunk = true;
                        }
                        else if (c == '"')
                        {
                            // Parameter is quoted
                            inQuotes           = true;
                            isNumericParameter = false;
                        }
                        else if (c == '{')
                        {
                            // Parameter is an expression
                            value              = "{";
                            inExpression       = true;
                            isNumericParameter = false;
                        }
                        else
                        {
                            // Starting numeric or string parameter
                            value += c;
                            isNumericParameter = (c != 'e') && (NumericParameterChars.Contains(c));
                        }
                    }
                    else if (inQuotes)
                    {
                        if (c == '"')
                        {
                            if (reader.Peek() == '"')
                            {
                                // Treat subsequent double quotes as a single quote char
                                value += '"';
                                reader.Read();
                            }
                            else
                            {
                                // No longer in an escaped parameter
                                inQuotes    = false;
                                wasQuoted   = true;
                                endingChunk = true;
                            }
                        }
                        else
                        {
                            // Add next character to the parameter value
                            value += c;
                        }
                    }
                    else if (inExpression)
                    {
                        if (c == '}')
                        {
                            // No longer in an expression
                            inExpression = false;
                            endingChunk  = true;
                        }
                        value += c;
                    }
                    else if (endingChunk || char.IsWhiteSpace(c) || (isNumericParameter && !NumericParameterChars.Contains(c)))
                    {
                        // Parameter has ended
                        inChunk = endingChunk = false;
                    }
                    else
                    {
                        // Reading more of the current chunk
                        value += c;
                    }

                    if (endingChunk && c == '\n')
                    {
                        // Last character - process the last parameter being read
                        inChunk = endingChunk = false;
                    }
                }

                if (readingAtStart)
                {
                    isLineNumber = (char.ToUpperInvariant(c) == 'N');
                    if (char.IsWhiteSpace(c) && c != '\n')
                    {
                        result.Indent++;
                    }
                    else
                    {
                        readingAtStart = false;
                    }
                }

                if (!inChunk && !readingAtStart)
                {
                    if (letter != '\0' || value != "" || wasQuoted)
                    {
                        // Chunk is complete
                        char upperLetter = char.ToUpperInvariant(letter);
                        if (isLineNumber)
                        {
                            // Process line number
                            if (int.TryParse(value, out int lineNumber))
                            {
                                result.LineNumber = lineNumber;
                            }
                            isLineNumber  = false;
                            hadLineNumber = true;
                        }
                        else if ((upperLetter == 'G' || upperLetter == 'M' || upperLetter == 'T') &&
                                 (result.MajorNumber == null || (result.Type == CodeType.GCode && result.MajorNumber == 53)))
                        {
                            // Process G/M/T identifier(s)
                            if (result.Type == CodeType.GCode && result.MajorNumber == 53)
                            {
                                result.MajorNumber        = null;
                                result.Flags             |= CodeFlags.EnforceAbsolutePosition;
                                enforcingAbsolutePosition = true;
                            }

                            result.Type = (CodeType)upperLetter;
                            if (value.Contains('.'))
                            {
                                string[] args = value.Split('.');
                                if (int.TryParse(args[0], out int majorNumber))
                                {
                                    result.MajorNumber = majorNumber;
                                }
                                else
                                {
                                    throw new CodeParserException($"Failed to parse major {char.ToUpperInvariant((char)result.Type)}-code number ({args[0]})");
                                }
                                if (sbyte.TryParse(args[1], out sbyte minorNumber) && minorNumber >= 0)
                                {
                                    result.MinorNumber = minorNumber;
                                }
                                else
                                {
                                    throw new CodeParserException($"Failed to parse minor {char.ToUpperInvariant((char)result.Type)}-code number ({args[1]})");
                                }
                            }
                            else if (int.TryParse(value, out int majorNumber))
                            {
                                result.MajorNumber = majorNumber;
                            }
                            else
                            {
                                throw new CodeParserException($"Failed to parse major {char.ToUpperInvariant((char)result.Type)}-code number ({value})");
                            }
                        }
                        else if (!result.MajorNumber.HasValue && result.Keyword == KeywordType.None && !wasQuoted)
                        {
                            // Check for conditional G-code
                            if (letter == 'i' && value == "f")
                            {
                                result.Keyword         = KeywordType.If;
                                inCondition            = true;
                                result.KeywordArgument = "";
                            }
                            else if (letter == 'e' && value == "lif")
                            {
                                result.Keyword         = KeywordType.ElseIf;
                                result.KeywordArgument = "";
                                inCondition            = true;
                            }
                            else if (letter == 'e' && value == "lse")
                            {
                                result.Keyword = KeywordType.Else;
                            }
                            else if (letter == 'w' && value == "hile")
                            {
                                result.Keyword         = KeywordType.While;
                                result.KeywordArgument = "";
                                inCondition            = true;
                            }
                            else if (letter == 'b' && value == "reak")
                            {
                                result.Keyword = KeywordType.Break;
                                inCondition    = true;
                            }
                            else if (letter == 'r' && value == "eturn")
                            {
                                result.Keyword         = KeywordType.Return;
                                result.KeywordArgument = "";
                                inCondition            = true;
                            }
                            else if (letter == 'a' && value == "bort")
                            {
                                result.Keyword = KeywordType.Abort;
                                inCondition    = true;
                            }
                            else if (letter == 'v' && value == "ar")
                            {
                                result.Keyword         = KeywordType.Var;
                                result.KeywordArgument = "";
                                inCondition            = true;
                            }
                            else if (letter == 's' && value == "et")
                            {
                                result.Keyword         = KeywordType.Set;
                                result.KeywordArgument = "";
                                inCondition            = true;
                            }
                            else if (result.Parameter(letter) == null)
                            {
                                // Add parsed parameter
                                result.Parameters.Add(new CodeParameter(letter, value, false));
                            }
                            else
                            {
                                throw new CodeParserException($"Duplicate {letter} parameter");
                            }
                        }
                        else if (letter == '\0' || result.Parameter(letter) == null)
                        {
                            // Add parsed parameter
                            result.Parameters.Add(new CodeParameter(letter, value, wasQuoted));
                        }
                        else
                        {
                            throw new CodeParserException($"Duplicate {letter} parameter");
                        }

                        letter    = '\0';
                        value     = "";
                        wasQuoted = false;
                    }

                    if (c == ';')
                    {
                        // Starting final comment
                        contentRead = inFinalComment = true;
                    }
                    else if (c == '(')
                    {
                        // Starting encapsulated comment
                        contentRead = inEncapsulatedComment = true;
                    }
                    else if (!char.IsWhiteSpace(c))
                    {
                        // Starting a new parameter
                        contentRead = inChunk = true;
                        inQuotes    = (c == '"');
                        letter      = inQuotes ? '\0' : c;
                    }
                }

                if (!inFinalComment && !inEncapsulatedComment && !inCondition && !inChunk)
                {
                    // Stop if another G/M/T code is coming up and this one is complete
                    int  next     = reader.Peek();
                    char nextChar = (next == -1) ? '\n' : char.ToUpperInvariant((char)next);
                    if (result.MajorNumber.HasValue && result.MajorNumber != 53 && (nextChar == 'G' || nextChar == 'M' || nextChar == 'T') &&
                        (nextChar == 'M' || result.Type != CodeType.MCode || result.Parameters.Any(item => item.Letter == nextChar)))
                    {
                        // Note that M-codes may have G or T parameters but only one
                        break;
                    }
                }
            } while (c != '\n');
            enforcingAbsolutePosition &= (c != '\n');

            // Do not allow malformed codes
            if (inEncapsulatedComment)
            {
                throw new CodeParserException("Unterminated encapsulated comment");
            }
            if (inQuotes)
            {
                throw new CodeParserException("Unterminated string");
            }
            if (inExpression)
            {
                throw new CodeParserException("Unterminated expression");
            }
            if (result.KeywordArgument != null)
            {
                result.KeywordArgument = result.KeywordArgument.Trim();
                if (result.KeywordArgument.Length > 255)
                {
                    throw new CodeParserException("Keyword argument too long (> 255)");
                }
            }
            if (result.Parameters.Count > 255)
            {
                throw new CodeParserException("Too many parameters (> 255)");
            }

            // M584, M569 and M915 use driver identifiers
            if (result.Type == CodeType.MCode)
            {
                switch (result.MajorNumber)
                {
                case 569:
                case 915:
                    foreach (CodeParameter parameter in result.Parameters)
                    {
                        if (char.ToUpperInvariant(parameter.Letter) == 'P')
                        {
                            parameter.ConvertDriverIds();
                        }
                    }
                    break;

                case 584:
                    foreach (CodeParameter parameter in result.Parameters)
                    {
                        if ("XYZUVWABCE".Contains(char.ToUpperInvariant(parameter.Letter)))
                        {
                            parameter.ConvertDriverIds();
                        }
                    }
                    break;
                }
            }

            // End
            return(contentRead);
        }
Esempio n. 5
0
        public async Task ParseAsync()
        {
            string codeString = "G53 G1 X0 Y5 F3000 G0 X5 Y10";

            byte[] codeBytes = Encoding.UTF8.GetBytes(codeString);
            using (MemoryStream memoryStream = new MemoryStream(codeBytes))
            {
                using StreamReader reader = new StreamReader(memoryStream);
                CodeParserBuffer      buffer = new CodeParserBuffer(128, true);
                DuetAPI.Commands.Code code   = new DuetAPI.Commands.Code()
                {
                    LineNumber = 1
                };

                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeType.GCode, code.Type);
                Assert.AreEqual(1, code.MajorNumber);
                Assert.AreEqual(CodeFlags.EnforceAbsolutePosition, code.Flags);
                Assert.AreEqual(1, code.LineNumber);
                Assert.AreEqual(3, code.Parameters.Count);
                Assert.AreEqual(0, (int)code.Parameter('X'));
                Assert.AreEqual(5, (int)code.Parameter('Y'));
                Assert.AreEqual(3000, (int)code.Parameter('F'));

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeType.GCode, code.Type);
                Assert.AreEqual(0, code.MajorNumber);
                Assert.AreEqual(CodeFlags.EnforceAbsolutePosition | CodeFlags.IsLastCode, code.Flags);
                Assert.AreEqual(1, code.LineNumber);
                Assert.AreEqual(2, code.Parameters.Count);
                Assert.AreEqual(5, (int)code.Parameter('X'));
                Assert.AreEqual(10, (int)code.Parameter('Y'));
            }

            codeString = "G1 X1 Y5 F3000\nG1 X5 F300\nG0 Y40";
            codeBytes  = Encoding.UTF8.GetBytes(codeString);
            using (MemoryStream memoryStream = new MemoryStream(codeBytes))
            {
                using StreamReader reader = new StreamReader(memoryStream);
                CodeParserBuffer buffer = new CodeParserBuffer(128, true);

                DuetAPI.Commands.Code code = new DuetAPI.Commands.Code()
                {
                    LineNumber = 0
                };
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeType.GCode, code.Type);
                Assert.AreEqual(0, code.MajorNumber);
                Assert.AreEqual(3, code.LineNumber);
            }

            codeString = "G1 X1 Y5 F3000\n  G53 G1 X5 F300\n    G53 G0 Y40 G1 Z50\n  G4 S3\nG1 Z3";
            codeBytes  = Encoding.UTF8.GetBytes(codeString);
            using (MemoryStream memoryStream = new MemoryStream(codeBytes))
            {
                using StreamReader reader = new StreamReader(memoryStream);
                CodeParserBuffer buffer = new CodeParserBuffer(128, true);

                DuetAPI.Commands.Code code = new DuetAPI.Commands.Code()
                {
                    LineNumber = 0
                };
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeFlags.IsLastCode, code.Flags);
                Assert.AreEqual(0, code.Indent);
                Assert.AreEqual(1, code.LineNumber);

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeFlags.EnforceAbsolutePosition | CodeFlags.IsLastCode, code.Flags);
                Assert.AreEqual(2, code.Indent);
                Assert.AreEqual(2, code.LineNumber);

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeFlags.EnforceAbsolutePosition, code.Flags);
                Assert.AreEqual(4, code.Indent);
                Assert.AreEqual(3, code.LineNumber);

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeFlags.EnforceAbsolutePosition | CodeFlags.IsLastCode, code.Flags);
                Assert.AreEqual(4, code.Indent);
                Assert.AreEqual(3, code.LineNumber);

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeFlags.IsLastCode, code.Flags);
                Assert.AreEqual(2, code.Indent);
                Assert.AreEqual(4, code.LineNumber);

                code.Reset();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeFlags.IsLastCode, code.Flags);
                Assert.AreEqual(0, code.Indent);
                Assert.AreEqual(5, code.LineNumber);
            }

            codeString = "M291 P\"Please go to <a href=\"\"https://www.duet3d.com/StartHere\"\" target=\"\"_blank\"\">this</a> page for further instructions on how to set it up.\" R\"Welcome to your new Duet 3!\" S1 T0";
            codeBytes  = Encoding.UTF8.GetBytes(codeString);
            using (MemoryStream memoryStream = new MemoryStream(codeBytes))
            {
                using StreamReader reader = new StreamReader(memoryStream);
                CodeParserBuffer buffer = new CodeParserBuffer(128, true);

                DuetAPI.Commands.Code code = new DuetAPI.Commands.Code();
                await DuetAPI.Commands.Code.ParseAsync(reader, code, buffer);

                Assert.AreEqual(CodeType.MCode, code.Type);
                Assert.AreEqual(291, code.MajorNumber);
                Assert.AreEqual("Please go to <a href=\"https://www.duet3d.com/StartHere\" target=\"_blank\">this</a> page for further instructions on how to set it up.", (string)code.Parameter('P'));
                Assert.AreEqual("Welcome to your new Duet 3!", (string)code.Parameter('R'));
                Assert.AreEqual(1, (int)code.Parameter('S'));
                Assert.AreEqual(0, (int)code.Parameter('T'));
            }
        }