Exemple #1
0
        static string ScanTo(char previousChar,
                             RandomAccessIterator <char> iterator,
                             Func <char, char, char, bool> terminal)
        {
            var  tokenNameBuilder = new StringBuilder();
            char c = iterator.Current;

            tokenNameBuilder.Append(c);
            if (!terminal(previousChar, c, iterator.PeekNext()))
            {
                previousChar = c;
                c            = iterator.GetNext();
                while (!terminal(previousChar, c, iterator.PeekNext()))
                {
                    tokenNameBuilder.Append(c);
                    if (char.IsWhiteSpace(c))
                    {
                        break;
                    }
                    previousChar = c;
                    c            = iterator.GetNext();
                }
            }

            return(tokenNameBuilder.ToString());
        }
        public double EvaluateFunction(RandomAccessIterator <Token> tokens)
        {
            tokens.MoveNext();
            var param = tokens.GetNext();

            if (param.Equals(")"))
            {
                throw new SyntaxException(param.Position, "Expected argument not provided.");
            }
            var symbolLookup = Services.SymbolManager.GetSymbol(param, false);

            if (symbolLookup == null)
            {
                if (param.Type != TokenType.Operand || !char.IsLetter(param.Name[0]) || param.Name[0] != '_')
                {
                    throw new SyntaxException(param.Position, "Function \"len\" expects a symbol.");
                }
                if (Services.CurrentPass > 0)
                {
                    throw new SymbolException(param, SymbolException.ExceptionReason.NotDefined);
                }
                Services.PassNeeded = true;
                return(0);
            }
            param = tokens.GetNext();
            if (!param.Name.Equals(")"))
            {
                param = tokens.GetNext();
                int subscript = -1;
                if (param.Name.Equals("["))
                {
                    subscript = (int)Services.Evaluator.Evaluate(tokens, 0, int.MaxValue);
                }
                if (subscript < 0 || !tokens.PeekNext().Equals(")"))
                {
                    throw new SyntaxException(param.Position, "Unexpected argument.");
                }
                if (symbolLookup.StorageType != StorageType.Vector)
                {
                    throw new SyntaxException(param.Position, "Type mismatch.");
                }
                if (symbolLookup.DataType == DataType.String)
                {
                    if (subscript >= symbolLookup.StringVector.Count)
                    {
                        throw new SyntaxException(param.Position, "Index out of range.");
                    }
                    return(symbolLookup.StringVector[subscript].Length);
                }
                if (subscript >= symbolLookup.NumericVector.Count)
                {
                    throw new SyntaxException(param.Position, "Index out of range.");
                }
                return(symbolLookup.NumericVector[subscript].Size());
            }
            return(symbolLookup.Length);
        }
Exemple #3
0
        /// <summary>
        /// Gets the formatted string from the tokenized expression.
        /// </summary>
        /// <param name="iterator">The iterator to the tokenized expression.</param>
        /// <param name="services">The shared assembly services.</param>
        /// <returns></returns>
        public static string GetFormatted(RandomAccessIterator <Token> iterator, AssemblyServices services)
        {
            iterator.MoveNext();
            var format = iterator.GetNext();

            if (Token.IsEnd(format))
            {
                return(null);
            }
            string fmt;

            if (!format.IsDoubleQuote())
            {
                if (format.Type != TokenType.Function && !format.Name.Equals("format", services.StringComparison))
                {
                    return(null);
                }
                fmt = GetFormatted(iterator, services);
            }
            else
            {
                fmt = Regex.Unescape(format.Name.TrimOnce('"').ToString());
            }
            var parms = new List <object>();

            if (iterator.MoveNext())
            {
                while (!Token.IsEnd(iterator.GetNext()))
                {
                    if (ExpressionIsAString(iterator, services))
                    {
                        parms.Add(GetString(iterator, services));
                    }
                    else
                    {
                        var parmVal = services.Evaluator.Evaluate(iterator, false);
                        if (Regex.IsMatch(fmt, $"\\{{{parms.Count}(,-?\\d+)?:(d|D|x|X)\\d*\\}}"))
                        {
                            parms.Add((int)parmVal);
                        }
                        else
                        {
                            parms.Add(parmVal);
                        }
                    }
                }
            }
            if (parms.Count == 0)
            {
                return(fmt);
            }
            return(string.Format(fmt, parms.ToArray()));
        }
Exemple #4
0
        /// <summary>
        /// Creates a new instance of the Function class.
        /// </summary>
        /// <param name="name">The function's name.</param>
        /// <param name="parameterList">The list of parameters for the function.</param>
        /// <param name="iterator">The <see cref="SourceLine"/> iterator to traverse to define the function block.</param>
        /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param>
        /// <param name="caseSensitive">Determines whether to compare the passed parameters
        /// to the source block's own defined parameters should be case-sensitive.</param>
        /// <exception cref="SyntaxException"></exception>
        public Function(StringView name,
                        List <Token> parameterList,
                        RandomAccessIterator <SourceLine> iterator,
                        AssemblyServices services,
                        bool caseSensitive)
            : base(parameterList,
                   caseSensitive)
        {
            Name          = name;
            _services     = services;
            _definedLines = new List <SourceLine>();
            SourceLine line;

            while ((line = iterator.GetNext()) != null)
            {
                if (line.Label != null && line.Label.Name.Equals("+"))
                {
                    _services.Log.LogEntry(line.Label,
                                           "Anonymous labels are not supported inside functions.", false);
                }
                if (line.Instruction != null)
                {
                    if (line.Instruction.Name.Equals(".global", _services.StringViewComparer))
                    {
                        throw new SyntaxException(line.Instruction,
                                                  $"Directive \".global\" not allowed inside a function block.");
                    }
                    if (line.Instruction.Name.Equals(".endfunction", _services.StringViewComparer))
                    {
                        if (line.Operands.Count > 0)
                        {
                            throw new SyntaxException(line.Operands[0],
                                                      "Unexpected expression found after \".endfunction\" directive.");
                        }
                        break;
                    }
                }
                _definedLines.Add(line);
            }
            if (line == null)
            {
                throw new SyntaxException(iterator.Current.Instruction,
                                          "Function definition does not have a closing \".endfunction\" directive.");
            }
        }
Exemple #5
0
        double CallFunction(RandomAccessIterator <Token> tokens, bool returnValueExpected)
        {
            var functionToken = tokens.Current;
            var functionName  = functionToken.Name;

            tokens.MoveNext();
            var   evalParms = new List <object>();
            Token token     = tokens.GetNext();

            while (!token.Name.Equals(")"))
            {
                if (token.IsSeparator())
                {
                    tokens.MoveNext();
                }
                if (StringHelper.ExpressionIsAString(tokens, Services))
                {
                    evalParms.Add(StringHelper.GetString(tokens, Services));
                }
                else
                {
                    evalParms.Add(Services.Evaluator.Evaluate(tokens, false));
                }
                token = tokens.Current;
            }
            Services.SymbolManager.PushScopeEphemeral();
            var value = _functionDefs[functionName].Invoke(evalParms);

            Services.SymbolManager.PopScopeEphemeral();
            if (double.IsNaN(value) && returnValueExpected)
            {
                throw new ReturnException(functionToken.Position,
                                          $"Function name \"{functionName}\" did not return a value.");
            }
            return(value);
        }
Exemple #6
0
        public override void ExecuteDirective(RandomAccessIterator <SourceLine> lines)
        {
            var line = lines.Current;

            if (line.Instruction.Name.Equals(".endswitch", Services.StringComparison))
            {
                return;
            }
            CaseBlock <string> stringBlock  = null;
            CaseBlock <double> numericBlock = null;
            SwitchContext      context      = null;
            var it = line.Operands.GetIterator();

            if (it.MoveNext())
            {
                if (StringHelper.ExpressionIsAString(it, Services))
                {
                    context = new SwitchContext(StringHelper.GetString(it, Services));
                }
                else
                {
                    context = new SwitchContext(Services.Evaluator.Evaluate(it, false));
                }
                if (it.Current != null)
                {
                    throw new SyntaxException(it.Current, "Unexpected expression.");
                }
            }
            if (context == null)
            {
                string error;
                if (line.Operands.Count == 0)
                {
                    error = "Expression must follow \".switch\" directive.";
                }
                else
                {
                    error = "Expression must be a valid symbol or an expression.";
                }
                Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position, error);
                return;
            }
            var defaultIndex = -1;

            if (!string.IsNullOrEmpty(context.StringValue))
            {
                stringBlock = new CaseBlock <string>();
            }
            else
            {
                numericBlock = new CaseBlock <double>();
            }
            while ((line = lines.GetNext()) != null &&
                   (line.Instruction == null || !line.Instruction.Name.Equals(".endswitch", Services.StringComparison)))
            {
                if (line.Instruction != null)
                {
                    if (line.Instruction.Name.Equals(".case", Services.StringComparison))
                    {
                        if (defaultIndex > -1)
                        {
                            Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                                  "\".case\" directive cannot follow a \".default\" directive.");
                        }
                        else if (stringBlock?.FallthroughIndex > -1 || numericBlock?.FallthroughIndex > -1)
                        {
                            Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                                  "\".case\" does not fall through.");
                        }
                        else if (line.Operands.Count == 0)
                        {
                            Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                                  "Expression expected.");
                        }
                        else
                        {
                            var iterator = line.Operands.GetIterator();
                            if (stringBlock != null)
                            {
                                if (!StringHelper.ExpressionIsAString(iterator, Services))
                                {
                                    Services.Log.LogEntry(line.Filename, line.LineNumber, line.Operands[0].Position,
                                                          "String expression expected.");
                                }
                                else
                                {
                                    stringBlock.Cases.Add(StringHelper.GetString(iterator, Services));
                                }
                            }
                            else
                            {
                                numericBlock?.Cases.Add(Services.Evaluator.Evaluate(iterator));
                            }
                            if (iterator.Current != null)
                            {
                                throw new SyntaxException(iterator.Current, "Unexpected expression.");
                            }
                        }
                    }
                    else if (Reserved.IsOneOf("BreakContReturn", line.Instruction.Name))
                    {
                        if ((stringBlock?.Cases.Count == 0 || numericBlock?.Cases.Count == 0) &&
                            defaultIndex < 0)
                        {
                            Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                                  $"\"{line.Instruction}\" directive must follow a \".case\" or \".default\" directive.");
                        }
                        else
                        {
                            if (line.Instruction.Name.Equals(".return", Services.StringComparison) &&
                                (stringBlock?.FallthroughIndex < 0 || numericBlock?.FallthroughIndex < 0))
                            {
                                if (stringBlock != null)
                                {
                                    stringBlock.FallthroughIndex = lines.Index;
                                }
                                if (numericBlock != null)
                                {
                                    numericBlock.FallthroughIndex = lines.Index;
                                }
                            }
                            else if (!line.Instruction.Name.Equals(".return", Services.StringComparison) && line.Operands.Count > 0)
                            {
                                throw new SyntaxException(line.Operands[0], "Unexpected expression.");
                            }
                            context.AddBlock(stringBlock);
                            context.AddBlock(numericBlock);
                            Services.SymbolManager.PopScope();
                            if (stringBlock != null)
                            {
                                stringBlock = new CaseBlock <string>();
                            }
                            else
                            {
                                numericBlock = new CaseBlock <double>();
                            }
                        }
                    }
                    else if (line.Instruction.Name.Equals(".default", Services.StringComparison))
                    {
                        if (line.Operands.Count > 0)
                        {
                            throw new SyntaxException(line.Operands[0], "Unexpected expression.");
                        }
                        if (defaultIndex > -1)
                        {
                            Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                                  "There can only be one \".default\" directive in a switch block.");
                        }
                        else
                        {
                            defaultIndex = lines.Index + 1;
                        }
                    }
                    else if (line.Label != null)
                    {
                        Services.Log.LogEntry(line.Filename, line.LineNumber, line.Label.Position,
                                              "Label cannot be defined inside a switch block.");
                    }
                    else
                    {
                        if ((stringBlock?.Cases.Count == 0 || numericBlock?.Cases.Count == 0) && defaultIndex < 0)
                        {
                            Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position, "\".case\" or \".default\" directive expected");
                        }
                        else if (stringBlock?.FallthroughIndex < 0 || numericBlock?.FallthroughIndex < 0)
                        {
                            if (stringBlock != null)
                            {
                                stringBlock.FallthroughIndex = lines.Index;
                            }
                            if (numericBlock != null)
                            {
                                numericBlock.FallthroughIndex = lines.Index;
                            }
                        }
                    }
                }
            }
            if (line != null)
            {
                if (defaultIndex < 0 || !context.AnyCaseDefined())
                {
                    if (defaultIndex >= 0)
                    {
                        Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                              "Only a default case was specified.", false);
                    }
                    else if (!context.AnyCaseDefined())
                    {
                        Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                              "Switch statement did not encounter any cases to evaluate.");
                        return;
                    }
                    else
                    {
                        Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position,
                                              "Switch statement does not have a default case.", false);
                    }
                }
                var fallthroughIndex = context.GetFallthroughIndex();
                if (fallthroughIndex < 0)
                {
                    fallthroughIndex = defaultIndex;
                }

                if (fallthroughIndex > -1)
                {
                    lines.Rewind(fallthroughIndex - 1);
                }
                Services.SymbolManager.PushScope(lines.Index.ToString());
            }
        }
Exemple #7
0
        /// <summary>
        /// Construct a new instance of a symbol class.
        /// </summary>
        /// <param name="tokens">The tokenized expression of the symbol definition.</param>
        /// <param name="eval">The <see cref="Evaluator"/> to evaluate the expression.</param>
        /// <param name="isMutable">The symbol's mutability flag.</param>
        public Symbol(RandomAccessIterator <Token> tokens, Evaluator eval, bool isMutable)
            : this()
        {
            IsMutable   = isMutable;
            StorageType = StorageType.Vector;

            var opens = 1;
            var token = tokens.GetNext();

            if (TokenType.End.HasFlag(token.Type))
            {
                throw new SyntaxException(token.Position, "Expression expected.");
            }

            if (StringHelper.IsStringLiteral(tokens))
            {
                DataType = DataType.String;
            }
            else
            {
                DataType = DataType.Numeric;
            }

            int index = 0;

            while (opens > 0)
            {
                if (token.Type == TokenType.Open && token.Name.Equals("["))
                {
                    opens++;
                }
                else if (token.Name.Equals("]"))
                {
                    opens--;
                }
                else
                {
                    if (DataType == DataType.String)
                    {
                        if (!StringHelper.IsStringLiteral(tokens))
                        {
                            throw new SyntaxException(token.Position, "Type mismatch.");
                        }
                        StringVector.Add(index++, token.Name.TrimOnce('"'));
                        token = tokens.GetNext();
                        if (token.Name.Equals("]"))
                        {
                            opens--;
                        }
                    }
                    else
                    {
                        NumericVector.Add(index++, eval.Evaluate(tokens, false));
                        token = tokens.Current;
                        if (token.Name.Equals("]"))
                        {
                            continue;
                        }
                    }
                }
                token = tokens.GetNext();
            }
        }
Exemple #8
0
        double EvaluateSymbol(RandomAccessIterator <Token> tokens)
        {
            var token     = tokens.Current;
            var subscript = -1;
            var converted = double.NaN;
            var isString  = token.IsDoubleQuote();

            if (char.IsLetter(token.Name[0]) || token.Name[0] == '_')
            {
                var next = tokens.GetNext();
                if (next != null && next.IsOpen() && next.Name.Equals("["))
                {
                    subscript = (int)Evaluator.Evaluate(tokens, 0, int.MaxValue);
                }
                var symbol = SymbolManager.GetSymbol(token, CurrentPass > 0);
                if (symbol == null)
                {
                    if (token.Line.Label != null && token.Line.Label.Name.Equals(token.Name, StringViewComparer))
                    {
                        throw new SymbolException(token, SymbolException.ExceptionReason.NotDefined);
                    }
                    PassNeeded = true;
                    return(0x100);
                }
                if (subscript >= 0)
                {
                    if (symbol.StorageType != StorageType.Vector)
                    {
                        throw new SyntaxException(token.Position, "Type mismatch.");
                    }
                    if ((symbol.IsNumeric && subscript >= symbol.NumericVector.Count) ||
                        (!symbol.IsNumeric && subscript >= symbol.StringVector.Count))
                    {
                        throw new SyntaxException(token.Position, "Index was out of range.");
                    }
                    if (symbol.IsNumeric)
                    {
                        return(symbol.NumericVector[subscript]);
                    }
                    token    = new Token(symbol.StringVector[subscript], TokenType.Operand);
                    isString = true;
                }
                else if (symbol.IsNumeric)
                {
                    if (symbol.DataType == DataType.Address && symbol.Bank != Output.CurrentBank)
                    {
                        return((int)symbol.NumericValue | (symbol.Bank * 0x10000));
                    }
                    return(symbol.NumericValue);
                }
                else
                {
                    token    = new Token(symbol.StringValue, TokenType.Operand);
                    isString = true;
                }
            }
            if (isString || token.IsQuote())
            {
                // is it a string literal?
                var literal = token.IsQuote() ? token.Name.TrimOnce(token.Name[0]).ToString() : token.Name.ToString();
                if (string.IsNullOrEmpty(literal))
                {
                    throw new SyntaxException(token.Position, "Cannot evaluate empty string.");
                }
                literal = Regex.Unescape(literal);
                if (!isString)
                {
                    var charsize = 1;
                    if (char.IsSurrogate(literal[0]))
                    {
                        charsize++;
                    }
                    if (literal.Length > charsize)
                    {
                        throw new SyntaxException(token.Position, "Invalid char literal.");
                    }
                }
                // get the integral equivalent from the code points in the string
                converted = Encoding.GetEncodedValue(literal);
            }
            else if (token.Name.Equals("*"))
            {    // get the program counter
                converted = Output.LogicalPC;
            }
            else if (token.Name[0].IsSpecialOperator())
            {    // get the special character value
                if (token.Name[0] == '+' && CurrentPass == 0)
                {
                    converted  = Output.LogicalPC;
                    PassNeeded = true;
                }
                else
                {
                    converted = SymbolManager.GetLineReference(token.Name, token);
                    if (double.IsNaN(converted))
                    {
                        var reason = token.Name[0] == '+' ? SymbolException.ExceptionReason.InvalidForwardReference :
                                     SymbolException.ExceptionReason.InvalidBackReference;
                        throw new SymbolException(token, reason);
                    }
                }
            }
            if (double.IsNaN(converted))
            {
                throw new ExpressionException(token.Position,
                                              $"\"{token.Name}\" is not a expression.");
            }
            return(converted);
        }
Exemple #9
0
        /// <summary>
        /// Parses the source string into a tokenized <see cref="SourceLine"/> collection.
        /// </summary>
        /// <param name="fileName">The source file's path/name.</param>
        /// <param name="source">The source string.</param>
        /// <returns>A collection of <see cref="SourceLine"/>s whose components are
        /// properly tokenized for further evaluation and assembly.</returns>
        /// <exception cref="ExpressionException"/>
        public static IEnumerable <SourceLine> Parse(string fileName, string source)
        {
            var   iterator = new RandomAccessIterator <char>(source.ToCharArray());
            Token rootParent, currentParent;
            Token token = null;

            Reset();

            Token currentOpen = null;
            int   currentLine = 1, lineNumber = currentLine;

            // lineIndex is the iterator index at the start of each line for purposes of calculating token
            // positions. sourceLindeIndex is the iterator index at the start of each new line
            // of source. Usually lineIndex and sourceLindeIndex are the same, but for those source lines
            // whose source code span multiple lines, they will be different.
            int lineIndex = -1, opens = 0, sourceLineIndex = lineIndex;

            var  lines        = new List <SourceLine>();
            char previousChar = iterator.Current;

            while (iterator.GetNext() != EOF)
            {
                if (iterator.Current != NewLine && iterator.Current != ':' && iterator.Current != ';')
                {
                    try
                    {
                        token = ParseToken(previousChar, token, iterator);
                        if (token != null)
                        {
                            previousChar   = iterator.Current;
                            token.Parent   = currentParent;
                            token.Position = iterator.Index - lineIndex - token.Name.Length + 1;
                            if (token.OperatorType == OperatorType.Open || token.OperatorType == OperatorType.Closed || token.OperatorType == OperatorType.Separator)
                            {
                                if (token.OperatorType == OperatorType.Open)
                                {
                                    opens++;
                                    currentParent.AddChild(token);
                                    currentOpen       =
                                        currentParent = token;
                                    AddBlankSeparator();
                                }
                                else if (token.OperatorType == OperatorType.Closed)
                                {
                                    if (currentOpen == null)
                                    {
                                        throw new ExpressionException(token, $"Missing opening for closure \"{token.Name}\"");
                                    }

                                    // check if matching ( to )
                                    if (!Groups[currentOpen.Name].Equals(token.Name))
                                    {
                                        throw new ExpressionException(token, $"Mismatch between \"{currentOpen.Name}\" in column {currentOpen.Position} and \"{token.Name}\"");
                                    }

                                    // go up the ladder
                                    currentOpen = currentParent = token.Parent = currentOpen.Parent;

                                    while (currentOpen != null && currentOpen.OperatorType != OperatorType.Open)
                                    {
                                        currentOpen = currentOpen.Parent;
                                    }
                                    opens--;
                                }
                                else
                                {
                                    currentParent = currentParent.Parent;
                                    currentParent.AddChild(token);
                                    currentParent = token;
                                }
                            }
                            else if (token.Type == TokenType.Instruction)
                            {
                                while (currentParent.Parent != rootParent)
                                {
                                    currentParent = currentParent.Parent;
                                }
                                currentParent.AddChild(token);
                                AddBlankSeparator();
                                AddBlankSeparator();
                            }
                            else
                            {
                                currentParent.AddChild(token);
                            }
                        }
                    }
                    catch (ExpressionException ex)
                    {
                        Assembler.Log.LogEntry(fileName, lineNumber, ex.Position, ex.Message);
                    }
                    if (iterator.PeekNext() == NewLine)
                    {
                        iterator.MoveNext();
                    }
                }
                if (iterator.Current == ';')
                {
                    _ = iterator.Skip(c => c != NewLine && (c != ':' || Assembler.Options.IgnoreColons) && c != EOF);
                }


                if (iterator.Current == NewLine || iterator.Current == ':' || iterator.Current == EOF)
                {
                    previousChar = iterator.Current;

                    /* A new source line is when:
                     * 1. A line termination character (New Line, colon, EOF) is encountered
                     * 2. And either there are no more characters left or the most recent token created
                     * 3. Is not a binary operator nor it is a comma separator.
                     */
                    var newLine = iterator.Current == EOF ||
                                  (opens == 0 &&
                                   (token == null ||
                                    (token.OperatorType != OperatorType.Binary &&
                                     token.OperatorType != OperatorType.Open &&
                                     !token.Name.Equals(",")
                                    )
                                   )
                                  );
                    if (iterator.Current == NewLine)
                    {
                        currentLine++;
                    }
                    if (newLine)
                    {
                        var newSourceLine = new SourceLine(fileName, lineNumber, GetSourceLineSource(), rootParent.Children[0]);
                        lines.Add(newSourceLine);
                        if (Assembler.Options.WarnLeft && newSourceLine.Label != null && newSourceLine.Label.Position != 1)
                        {
                            Assembler.Log.LogEntry(newSourceLine, newSourceLine.Label, "Label is not at the beginning of the line.", false);
                        }
                        Reset();
                        lineNumber = currentLine;
                    }
                    else
                    {
                        token = null;
                    }
                    lineIndex = iterator.Index;
                    if (newLine)
                    {
                        sourceLineIndex = iterator.Index;
                    }
                }
            }
            if (currentOpen != null && currentOpen.OperatorType == OperatorType.Open)
            {
                Assembler.Log.LogEntry(fileName, 1, currentOpen.LastChild.Position, $"End of source reached without finding closing \"{Groups[currentOpen.Name]}\".");
            }

            if (token != null)
            {
                lines.Add(new SourceLine(fileName, lineNumber, GetSourceLineSource(), rootParent.Children[0]));
            }

            return(lines);

            void AddBlankSeparator()
            {
                var sepToken = new Token()
                {
                    Type         = TokenType.Operator,
                    OperatorType = OperatorType.Separator,
                    Name         = string.Empty,
                    Position     = token == null ? 1 : token.Position,
                    Children     = new List <Token>()
                };

                currentParent.AddChild(sepToken);
                currentParent = sepToken;
            }

            string GetSourceLineSource()
            {
                if (iterator.Index > sourceLineIndex + 1)
                {
                    return(source.Substring(sourceLineIndex + 1, iterator.Index - sourceLineIndex - 1));
                }
                return(string.Empty);
            }

            void Reset()
            {
                currentParent          =
                    rootParent         = new Token();
                currentParent.Children = new List <Token>();
                AddBlankSeparator();
                AddBlankSeparator();
                token = null;
            }
        }
Exemple #10
0
        static Token ParseToken(char previousChar, Token previousToken, RandomAccessIterator <char> iterator, bool parsingAssembly = true)
        {
            char c = iterator.Current;

            while (char.IsWhiteSpace(c))
            {
                if (c == NewLine && parsingAssembly)
                {
                    iterator.Rewind(iterator.Index - 1);
                    return(null);
                }
                c = iterator.GetNext();
            }
            if ((c == ';' && parsingAssembly) || c == EOF)
            {
                return(null);
            }

            var token = new Token();

            //first case, simplest
            var nextChar = iterator.PeekNext();

            if (char.IsDigit(c) ||
                char.IsLetter(c) ||
                c == '_' ||
                c == '?' ||
                (c == '.' && char.IsLetterOrDigit(nextChar)) ||
                (c == '\\' && char.IsLetterOrDigit(nextChar)))
            {
                token.Type = TokenType.Operand;
                if (char.IsDigit(c) || (c == '.' && char.IsDigit(nextChar)))
                {
                    if (char.IsDigit(c) && previousChar == '$')
                    {
                        token.Name = ScanTo(previousChar, iterator, FirstNonHex);
                    }
                    else if (c == '0' && (nextChar == 'b' ||
                                          nextChar == 'B' ||
                                          nextChar == 'o' ||
                                          nextChar == 'O' ||
                                          nextChar == 'x' ||
                                          nextChar == 'X'))
                    {
                        token.Name = ScanTo(previousChar, iterator, FirstNonNonBase10);
                    }
                    else
                    {
                        token.Name = ScanTo(previousChar, iterator, FirstNonNumeric);
                    }
                }
                else if (c == '\\')
                {
                    iterator.MoveNext();
                    token.Name = c + ScanTo(previousChar, iterator, FirstNonLetterOrDigit);
                }
                else if (c == '?')
                {
                    token.UnparsedName =
                        token.Name     = "?";
                    return(token);
                }
                else
                {
                    token.UnparsedName =
                        token.Name     = ScanTo(previousChar, iterator, FirstNonSymbol);
                    if (parsingAssembly && !Assembler.Options.CaseSensitive)
                    {
                        token.Name = token.Name.ToLower();
                    }

                    if (parsingAssembly && Assembler.InstructionLookupRules.Any(rule => rule(token.Name)))
                    {
                        token.Type = TokenType.Instruction;
                    }
                    else if (iterator.Current == '(' ||
                             (iterator.Current != NewLine && char.IsWhiteSpace(iterator.Current) &&
                              iterator.PeekNextSkipping(NonNewLineWhiteSpace) == '('))
                    {
                        token.Type         = TokenType.Operator;
                        token.OperatorType = OperatorType.Function;
                    }
                    else
                    {
                        token.Type = TokenType.Operand;
                    }
                }
            }
            else if (previousToken != null &&
                     previousToken.Name.Equals("%") &&
                     previousToken.OperatorType == OperatorType.Unary &&
                     (c == '.' || c == '#'))
            {
                // alternative binary string parsing
                token.Type = TokenType.Operand;
                token.Name = ScanTo(previousChar, iterator, FirstNonAltBin).Replace('.', '0')
                             .Replace('#', '1');
            }
            else if (c == '"' || c == SingleQuote)
            {
                var open         = c;
                var quoteBuilder = new StringBuilder(c.ToString());
                var escaped      = false;
                while ((c = iterator.GetNext()) != open && c != char.MinValue)
                {
                    quoteBuilder.Append(c);
                    if (c == '\\')
                    {
                        escaped = true;
                        quoteBuilder.Append(iterator.GetNext());
                    }
                }
                if (c == char.MinValue)
                {
                    throw new ExpressionException(iterator.Index, $"Quote string not enclosed.");
                }
                quoteBuilder.Append(c);
                var unescaped = escaped ? Regex.Unescape(quoteBuilder.ToString()) : quoteBuilder.ToString();
                if (c == '\'' && unescaped.Length > 3)
                {
                    throw new ExpressionException(iterator.Index, "Too many characters in character literal.");
                }
                token.Name = unescaped;
                token.Type = TokenType.Operand;
            }
            else
            {
                if (c == '+' || c == '-')
                {
                    /*
                     * Scenarios for parsing '+' or '-', since they can function as different things
                     * in an expression.
                     * 1. The binary operator:
                     *  a. OPERAND+3 / ...)+(... => single '+' sandwiched between two operands/groupings
                     *  b. OPERAND++3 / ...)++(... => the first '+' is a binary operator since it is to the
                     *     right of an operand/grouping. We need to split off the single '++' to two
                     *     separate '+' tokens. What kind of token is the second '+'? We worry about that later.
                     *  c. OPERAND+++3 / ...)+++(... => again, the first '+' is a binary operator. We need to split
                     *     it off from the rest of the string of '+' characters, and we worry about later.
                     * 2. The unary operator:
                     *  a. +3 / +(... => single '+' immediately preceding an operand/grouping.
                     *  b. ++3 / ++(... => parser doesn't accept C-style prefix (or postfix) operators, so one of these is an
                     *     anonymous label. Which one? Easy, the first. Split the '+' string.
                     * 3. A full expression mixing both:
                     *  a. OPERAND+++3 / ...)+++(... => From scenario 1.c, we know the first '+' is a binary operator,
                     *     which leaves us with => '++3' left, which from scenario 2.b. we know the first '+'
                     *     has to be an operand. So we split the string again, so that the next scan leaves us with
                     *     '+3', so the third and final plus is a unary operator.
                     * OPERAND => operand
                     * +       => binary operator
                     * +       => operand
                     * +       => unary operator
                     * 3/(     => operand/grouping
                     * 4. A line reference:
                     *   a. + => Simplest scenario.
                     *   b. ++, +++, ++++, etc. => Treat as one.
                     */
                    // Get the full string
                    token.Name = ScanTo(previousChar, iterator, FirstNonPlusMinus);
                    if (previousToken != null && (previousToken.Type == TokenType.Operand || previousToken.Name.Equals(")")))
                    {
                        // looking backward at the previous token, if it's an operand or grouping then we
                        // know this is a binary
                        token.Type         = TokenType.Operator;
                        token.OperatorType = OperatorType.Binary;
                        if (token.Name.Length > 1) // we need to split off the rest of the string so we have a single char '+'
                        {
                            token.Name = c.ToString();
                            iterator.Rewind(iterator.Index - token.Position - 1);
                        }
                    }
                    else if (!IsNotOperand(nextChar) || nextChar == '(')
                    {
                        // looking at the very next character in the input stream, if it's an operand or grouping
                        // then we know this is a unary
                        if (token.Name.Length > 1)
                        {
                            // If the string is greater than one character,
                            // then it's not a unary, it's an operand AND a unary. So we split off the
                            // rest of the string.
                            token.Name = c.ToString();
                            iterator.Rewind(iterator.Index - token.Position - 1);
                            token.Type = TokenType.Operand;
                        }
                        else
                        {
                            token.Type         = TokenType.Operator;
                            token.OperatorType = OperatorType.Unary;
                        }
                    }
                    else
                    {
                        token.Type = TokenType.Operand;
                    }
                }
                else if (c == '*')
                {
                    // Same as +/- scenario above, if the previous token is an operand or grouping,
                    // we need to treat the splat as a binary operator.
                    if (previousToken != null && (previousToken.Type == TokenType.Operand || previousToken.Name.Equals(")")))
                    {
                        token.Type         = TokenType.Operator;
                        token.OperatorType = OperatorType.Binary;
                    }
                    else
                    {
                        // but since there is no unary version of this we will treat as an operand, and let the evaluator
                        // deal with any problems like *OPERAND /*(
                        token.Type = TokenType.Operand;
                    }
                    token.Name = c.ToString();
                }
                else
                {
                    // not a number, symbol, string, or special (+, -, *) character. So we just treat as an operator
                    token.Type = TokenType.Operator;
                    if (c.IsSeparator() || c.IsOpenOperator() || c.IsClosedOperator())
                    {
                        token.Name = c.ToString();
                        if (c.IsSeparator())
                        {
                            token.OperatorType = OperatorType.Separator;
                        }
                        else if (c.IsOpenOperator())
                        {
                            token.OperatorType = OperatorType.Open;
                        }
                        else
                        {
                            token.OperatorType = OperatorType.Closed;
                        }
                    }
                    else
                    {
                        token.Name         = ScanTo(previousChar, iterator, FirstNonMatchingOperator);
                        token.UnparsedName = token.Name;

                        /* The general strategy to determine whether an operator is unary or binary:
                         *  1. Is it actually one of the defined unary types?
                         *  2. Peek at the next character. Is it a group or operand, or not?
                         *  3. Look behind at the previous token. Is it also a group or operand, or not?
                         *  4. If the token does NOT follow an operand or group, AND it precedes a group character,
                         *     or operand character, then it is a unary.
                         *  5. All other cases, binary.
                         *
                         */
                        if (
                            (
                                (
                                    c.IsUnaryOperator() &&
                                    (
                                        !IsNotOperand(nextChar) ||
                                        nextChar == '(' ||
                                        nextChar.IsRadixOperator() ||
                                        nextChar.IsUnaryOperator()
                                    )
                                ) ||
                                (
                                    c.IsRadixOperator() && char.IsLetterOrDigit(nextChar)
                                ) ||
                                (
                                    c == '%' && (nextChar == '.' || nextChar == '#')
                                )
                            ) &&
                            (previousToken == null ||
                             (previousToken.Type != TokenType.Operand &&
                              !previousToken.Name.Equals(")")
                             )
                            )
                            )
                        {
                            token.OperatorType = OperatorType.Unary;
                        }
                        else
                        {
                            token.OperatorType = OperatorType.Binary;
                        }
                    }
                }
            }
            if (string.IsNullOrEmpty(token.UnparsedName))
            {
                token.UnparsedName = token.Name;
            }
            if (iterator.Current != token.Name[^ 1])
            {
                iterator.Rewind(iterator.Index - 1);
            }
            return(token);
        }
Exemple #11
0
        IEnumerable <SourceLine> ProcessMacros(IEnumerable <SourceLine> uncommented)
        {
            var macroProcessed = new List <SourceLine>();
            RandomAccessIterator <SourceLine> lineIterator = uncommented.GetIterator();
            SourceLine line = null;

            while ((line = lineIterator.GetNext()) != null)
            {
                try
                {
                    if (string.IsNullOrWhiteSpace(line.ParsedSource))
                    {
                        macroProcessed.Add(line);
                        continue;
                    }
                    if (line.InstructionName.Equals(".macro"))
                    {
                        if (string.IsNullOrEmpty(line.LabelName))
                        {
                            Assembler.Log.LogEntry(line, line.Instruction, "Macro name not specified.");
                            continue;
                        }
                        var macroName = "." + line.LabelName;

                        if (_macros.ContainsKey(macroName))
                        {
                            Assembler.Log.LogEntry(line, line.Label, $"Macro named \"{line.LabelName}\" already defined.");
                            continue;
                        }
                        if (Assembler.IsReserved.Any(i => i.Invoke(macroName)) ||
                            !char.IsLetter(line.LabelName[0]))
                        {
                            Assembler.Log.LogEntry(line, line.Label, $"Macro name \"{line.LabelName}\" is not valid.");
                            continue;
                        }
                        Reserved.AddWord("MacroNames", macroName);

                        var compare = Assembler.Options.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
                        var macro   = new Macro(line.Operand, line.ParsedSource, compare);
                        _macros[macroName] = macro;
                        var instr = line;
                        while ((line = lineIterator.GetNext()) != null && !line.InstructionName.Equals(".endmacro"))
                        {
                            if (macroName.Equals(line.InstructionName))
                            {
                                Assembler.Log.LogEntry(line, line.Instruction, "Recursive macro call not allowed.");
                                continue;
                            }
                            if (line.InstructionName.Equals(".macro"))
                            {
                                Assembler.Log.LogEntry(line, line.Instruction, "Nested macro definitions not allowed.");
                                continue;
                            }
                            if (line.InstructionName.Equals(".include") || line.InstructionName.Equals(".binclude"))
                            {
                                var includes = ExpandInclude(line);
                                foreach (var incl in includes)
                                {
                                    if (macroName.Equals(incl.InstructionName))
                                    {
                                        Assembler.Log.LogEntry(incl, incl.Instruction, "Recursive macro call not allowed.");
                                        continue;
                                    }
                                    macro.AddSource(incl);
                                }
                            }
                            else
                            {
                                macro.AddSource(line);
                            }
                        }
                        if (!string.IsNullOrEmpty(line.LabelName))
                        {
                            if (line.OperandHasToken)
                            {
                                Assembler.Log.LogEntry(line, line.Operand, "Unexpected argument found for macro definition closure.");
                                continue;
                            }
                            line.Instruction  = null;
                            line.ParsedSource = line.ParsedSource.Replace(".endmacro", string.Empty);
                            macro.AddSource(line);
                        }
                        else if (line == null)
                        {
                            line = instr;
                            Assembler.Log.LogEntry(instr, instr.Instruction, "Missing closure for macro definition.");
                            continue;
                        }
                    }
                    else if (line.InstructionName.Equals(".include") || line.InstructionName.Equals(".binclude"))
                    {
                        macroProcessed.AddRange(ExpandInclude(line));
                    }
                    else if (_macros.ContainsKey(line.InstructionName))
                    {
                        if (!string.IsNullOrEmpty(line.LabelName))
                        {
                            SourceLine clone = line.Clone();
                            clone.Operand          =
                                clone.Instruction  = null;
                            clone.UnparsedSource   =
                                clone.ParsedSource = line.LabelName;
                            macroProcessed.Add(clone);
                        }
                        Macro macro = _macros[line.InstructionName];
                        macroProcessed.AddRange(ProcessExpansion(macro.Expand(line.Operand)));
                    }
                    else if (line.InstructionName.Equals(".endmacro"))
                    {
                        Assembler.Log.LogEntry(line, line.Instruction,
                                               "Directive \".endmacro\" does not close a macro definition.");
                        continue;
                    }
                    else
                    {
                        macroProcessed.Add(line);
                    }
                }
                catch (ExpressionException ex)
                {
                    Assembler.Log.LogEntry(line, ex.Position, ex.Message);
                }
            }
            return(macroProcessed);
        }