private string HandleTimesDirective(IReadOnlyList <string> aSegments, ref int aArgIndex, ref DirectiveCodes aDirCode) { // Skip the TIMES directive term as it will // trigger an early loop break below. ++aArgIndex; object?dirCode = null; string timesExprStr = string.Empty; var hasValidDir = false; var len = aSegments.Count; for (; aArgIndex < len; aArgIndex++) { if (!Enum.TryParse(typeof(DirectiveCodes), aSegments[aArgIndex].ToUpper(), out dirCode)) { // We have not found a valid directive. // Add this argument to the expression string. timesExprStr += aSegments[aArgIndex]; continue; } // Ensure that this directive can use the times // prefix. hasValidDir = dirCode switch { DirectiveCodes.DB => true, _ => false }; break; } // We did not find a valid directive after the times // prefix. This syntax is invalid. // We have to null check dirCode here as the compiler // doesn't understand that it cannot ever be null here. if (!hasValidDir || dirCode is null) { AsmParser.Assert(true, AsmParser.ExIDs.InvalidDirectiveType); return(string.Empty); } // Update the directive code. aDirCode = (DirectiveCodes)dirCode; return(timesExprStr); }
public CompilerDir?ParseDirLine(ReadOnlySpan <char> aLine) { // Fast path return for lines that are comments. if (aLine[0] == ';') { return(null); } var len = aLine.Length; var buffer = new StringBuilder(len); var segments = new List <string>(); var skipNext = false; var inString = false; var skipUntilEnd = false; var pushString = false; for (var i = 0; i <= len; i++) { // Always ensure that we push the last segment to // the list. // This needs to be done here otherwise we might end // up skipping the entire last segment of the line. if (i == len) { // We should not be attempting to push a line // that has an unmatched string. AsmParser.Assert(inString, AsmParser.ExIDs.MismatchedString, segments.Count + 1, i); if (buffer.Length > 0) { segments.Add(buffer.ToString()); } break; } var c = aLine[i]; switch (c) { case '"': case '\'': inString = !inString; break; case ';': skipUntilEnd = !inString; break; case ',': skipNext = !inString; pushString = !inString; break; case { } when c == AsmParser.LineContinuation: skipNext = !inString; break; case { } when char.IsWhiteSpace(c): pushString = !inString; skipNext = !inString; break; } // Do we need to push the contents of the buffer // into the segment list? if (pushString) { if (buffer.Length > 0) { segments.Add(buffer.ToString()); buffer.Clear(); } pushString = false; } // Do we need to skip this character? if (skipNext || skipUntilEnd) { skipNext = false; continue; } buffer.Append(c); } return(BuildDirective(segments.ToArray())); }
private CompilerDir?BuildDirective(IReadOnlyList <string> aSegments, int aStartIndex = 0) { // The line could not be a valid directive with // less than three segments. if (aSegments.Count < 3) { return(null); } // If this is the first segment (startIndex == 0) // then the directive label will be the first entry // of the segment array. Otherwise it will be empty. // This will be the case in sub directives. var dirLabel = aStartIndex == 0 ? aSegments[0] : string.Empty; object?dirCodeObj = null; var foundDirective = false; var len = aSegments.Count; var argIndex = aStartIndex == 0 ? 1 : aStartIndex; for (; argIndex < len; argIndex++) { // Did we find a known directive identifier? foundDirective = Enum.TryParse(typeof(DirectiveCodes), aSegments[argIndex].ToUpper(), out dirCodeObj); if (foundDirective) { break; } } // We did not find a valid directive within the // input arguments. The data must be invalid. if (!foundDirective || dirCodeObj is null) { AsmParser.Assert(true, AsmParser.ExIDs.InvalidDirectiveType); return(null); } var dirCode = (DirectiveCodes)dirCodeObj; // Are we working with a times directive? string?timesPreExprStr = null; if (dirCode == DirectiveCodes.TIMES) { timesPreExprStr = HandleTimesDirective(aSegments, ref argIndex, ref dirCode); } // Advance to the next segment. ++argIndex; // Which type of directive are we parsing? var canHaveSubDirective = false; string?directiveStrData = null; byte[]? directiveData = null; switch (dirCode) { case DirectiveCodes.DB: canHaveSubDirective = true; directiveData = ParseDbDirectiveArgs(aSegments, ref argIndex); break; case DirectiveCodes.EQU: // We only expect a single argument with // the EQU directive, any additional arguments // will simply be disregarded. directiveStrData = aSegments[2]; break; case DirectiveCodes.TIMES: default: AsmParser.Assert(true, AsmParser.ExIDs.InvalidDirectiveType); break; } // Do we need to handle a sub directive here? CompilerDir?subDir = null; if (canHaveSubDirective && argIndex < len) { // We do not handle nested sub directives. AsmParser.Assert((aStartIndex > 0), AsmParser.ExIDs.NestedSubQuery); // We have a sub directive to handle. // This will usually be a "TIMES" directive. // We will start processing the sub directive // from the current argument index. subDir = BuildDirective(aSegments, argIndex); } // We are done! Construct and return the // compiler directive object. return (new CompilerDir(dirCode, dirLabel, directiveData, directiveStrData, timesPreExprStr, subDir)); }