void ScanBlock(RandomAccessIterator <SourceLine> lines) { var ix = lines.Index; var line = lines.Current; var closures = new Stack <Token>(); closures.Push(line.Instruction); while (lines.MoveNext() && closures.Count > 0) { if (lines.Current.Instruction != null) { if (lines.Current.Instruction.Name.Equals(_openClosures[closures.Peek().Name], Services.StringViewComparer)) { closures.Pop(); } else if (_openClosures.ContainsKey(lines.Current.Instruction.Name)) { closures.Push(lines.Current.Instruction); } } } if (closures.Count > 0) { throw new SyntaxException(closures.Peek(), $"Missing closure \"{_openClosures[closures.Peek().Name]}\" for directive \"{closures.Peek().Name}\"."); } lines.SetIndex(ix); }
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()); }
void DefineFunction(RandomAccessIterator <SourceLine> lines) { if (Services.CurrentPass == 0) { var line = lines.Current; if (_currentBlock != null) { throw new SyntaxException(line.Instruction, "Function definition block cannot be inside another block."); } if (line.Label == null) { throw new SyntaxException(line.Instruction, "Function name not specified"); } var functionName = line.Label.Name; if (_functionDefs.ContainsKey(functionName)) { throw new SyntaxException(line.Label, $"Function name \"{functionName}\" was previous declared."); } if (!Services.SymbolManager.SymbolIsValid(functionName)) { throw new SyntaxException(line.Label, $"Invalid function name \"{functionName}\"."); } _functionDefs.Add(functionName, new Function(line.Label.Name, line.Operands, lines, Services, Services.Options.CaseSensitive)); } else { new FunctionBlock(Services, 1).SeekBlockEnd(lines); } }
/// <summary> /// Assemble the parsed <see cref="SourceLine"/>. /// </summary> /// <param name="line">The line to assembly.</param> /// <returns>The disassembly output from the assembly operation.</returns> /// <exception cref="BlockClosureException"/> /// <exception cref="DirectoryNotFoundException"/> /// <exception cref="DivideByZeroException"/> /// <exception cref="ExpressionException"/> /// <exception cref="FileNotFoundException"/> /// <exception cref="FormatException"/> /// <exception cref="InvalidPCAssignmentException"/> /// <exception cref="BlockAssemblerException"/> /// <exception cref="OverflowException"/> /// <exception cref="ProgramOverflowException"/> /// <exception cref="ReturnException"/> /// <exception cref="SectionException"/> /// <exception cref="SymbolException"/> /// <exception cref="SyntaxException"/> public string Assemble(RandomAccessIterator <SourceLine> lines) { var first = lines.Current; bool isSpecial = first.Label != null && first.Label.IsSpecialOperator(); PCOnAssemble = Services.Output.LogicalPC; if (first.Label != null && !first.Label.Name.Equals("*")) { if (isSpecial) { Services.SymbolManager.DefineLineReference(first.Label, PCOnAssemble); } else if (first.Instruction == null || !ExcludedInstructionsForLabelDefines.Contains(first.Instruction.Name)) { DefineLabel(first.Label, PCOnAssemble, true); } } if (first.Instruction != null) { return(OnAssemble(lines)); } if (first.Label != null && !isSpecial) { var symbol = Services.SymbolManager.GetSymbol(first.Label, false); if (symbol != null && symbol.IsNumeric && symbol.StorageType == StorageType.Scalar && !double.IsNaN(symbol.NumericValue)) { return(string.Format(".{0}{1}", ((int)symbol.NumericValue).ToString("x4").PadRight(42), Services.Options.NoSource ? string.Empty : first.Source)); } } return(string.Empty); }
/// <summary> /// Cleanup the current block's scope. /// </summary> /// <param name="iterator">The source line iterator.</param> public void PopScope(RandomAccessIterator <SourceLine> iterator) { SeekBlockEnd(iterator); if (_createScope) { Services.SymbolManager.PopScope(); } }
/// <summary> /// Gets a 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 GetString(RandomAccessIterator <Token> iterator, AssemblyServices services) { if (iterator.Current == null && !iterator.MoveNext()) { return(string.Empty); } var token = iterator.Current; if (IsStringLiteral(iterator)) { iterator.MoveNext(); return(Regex.Unescape(token.Name.ToString()).TrimOnce('"')); } else if (token.Type == TokenType.Function && token.Name.Equals("format", services.StringComparison)) { var str = GetFormatted(iterator, services); if (!string.IsNullOrEmpty(str) && Token.IsEnd(iterator.Current)) { return(str); } } else if (token.Type == TokenType.Function && token.Name.Equals("char", services.StringComparison)) { var code = (int)services.Evaluator.Evaluate(iterator, 0, 0x10FFFF); return(char.ConvertFromUtf32(services.Encoding.GetCodePoint(code))); } else if (token.Type == TokenType.Operand && (char.IsLetter(token.Name[0]) || token.Name[0] == '_') && !services.Evaluator.IsReserved(token.Name)) { var sym = services.SymbolManager.GetSymbol(token, services.CurrentPass > 0); if (sym == null) { return(string.Empty); } if (sym.DataType == DataType.String) { if ((!iterator.MoveNext() || Token.IsEnd(iterator.Current)) && sym.StorageType == StorageType.Scalar) { return(sym.StringValue.TrimOnce('"').ToString()); } else if (sym.StorageType == StorageType.Vector && iterator.Current.Name.Equals("[")) { var current = iterator.Current; var subscript = (int)services.Evaluator.Evaluate(iterator); if (Token.IsEnd(iterator.Current)) { if (subscript >= 0 && subscript < sym.StringVector.Count) { return(sym.StringVector[subscript].ToString()); } throw new SyntaxException(current, "Index out of range."); } } } } throw new SyntaxException(token, "Type mismatch."); }
public override void ExecuteDirective(RandomAccessIterator <SourceLine> iterator) { var line = iterator.Current; if (_ifTrue) { SeekBlockEnd(iterator); } else { while (line != null && !line.Instruction.Name.Equals(".endif", Services.StringComparison) && !_ifTrue) { if (iterator.Current.Instruction == null) { continue; } var instruction = Services.Options.CaseSensitive ? line.Instruction.Name.ToString() : line.Instruction.Name.ToLower(); if (instruction.StartsWith(".if")) { EvaluateCondition(line); } else { if (!instruction.Equals(".endif")) { if (_elseEvaluated) { throw new SyntaxException(iterator.Current.Instruction, $"Invalid use of \"{instruction}\" directive."); } _elseEvaluated = instruction.Equals(".else"); if (_elseEvaluated) { if (line.Operands.Count > 0) { throw new SyntaxException(line.Operands[0], "Unexpected expression."); } break; } EvaluateCondition(line); } } if (_ifTrue) { break; } SeekBlockDirectives(iterator, Reserved.GetReserved("Keywords").ToArray()); line = iterator.Current; } if (line == null) { throw new SyntaxException(line.Instruction, $"Missing \".endif\" directive."); } } }
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); }
/// <summary> /// Constructs a new instance of a <see cref="RandomAccessIterator{T}"/> class. /// </summary> /// <param name="iterator">An iterator from which to copy.</param> /// <exception cref="ArgumentNullException"></exception> public RandomAccessIterator(RandomAccessIterator <T> iterator) { if (iterator == null) { throw new ArgumentNullException(); } _firstIndex = iterator._firstIndex; Index = iterator.Index; _list = iterator._list; }
/// <summary> /// Constructs a new instance of a <see cref="RandomAccessIterator{T}"/> class. /// </summary> /// <param name="iterator">An iterator from which to copy.</param> /// <param name="reset">Reset the copied indicator.</param> /// <exception cref="ArgumentNullException"></exception> public RandomAccessIterator(RandomAccessIterator <T> iterator, bool reset) { if (iterator == null) { throw new ArgumentNullException(); } _firstIndex = iterator._firstIndex; _list = iterator._list; _length = iterator._length; Index = reset ? _firstIndex : iterator.Index; }
/// <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())); }
void DoPop(RandomAccessIterator <SourceLine> lines) { _currentBlock.PopScope(lines); _blocks.Pop(); if (_blocks.Count > 0) { _currentBlock = _blocks.Peek(); } else { _currentBlock = null; } }
void DoGoto(SourceLine line) { if (!line.OperandHasToken) { Assembler.Log.LogEntry(line, line.Instruction, "Destination not specified for \".goto\" directive."); return; } var gotoExp = line.OperandExpression; if (gotoExp.Equals(line.LabelName)) { Assembler.Log.LogEntry(line, line.Instruction, "Destination cannot be the same line as \".goto\" directive."); return; } var iterCopy = new RandomAccessIterator <SourceLine>(Assembler.LineIterator); iterCopy.Reset(); SourceLine currLine; while ((currLine = iterCopy.Skip(l => !l.LabelName.Equals(gotoExp))) != null) { if (currLine.InstructionName.Contains("=") || currLine.InstructionName.Equals(".equ") || currLine.Instruction.Equals(".let")) { Assembler.Log.LogEntry(line, line.Instruction, $"\"{gotoExp}\" is not a valid destination."); return; } if (iterCopy.Index >= Assembler.LineIterator.Index) { Assembler.LineIterator.FastForward(iterCopy.Index); } else { if (iterCopy.Index == 0) { Assembler.LineIterator.Reset(); } else { Assembler.LineIterator.Rewind(iterCopy.Index); } } return; } Assembler.Log.LogEntry(line, line.Instruction, $"Could not find destination \"{gotoExp}\"."); }
public override void ExecuteDirective(RandomAccessIterator <SourceLine> lines) { var line = lines.Current; if (line.Operands.Count > 0) { throw new SyntaxException(line.Operands[0], "Unexpected expression."); } if (line.Instruction.Name.Equals(".endpage", Services.StringComparison)) { if (!Services.PassNeeded && GetPage(Services.Output.LogicalPC - 1) != _page) { Services.Log.LogEntry(line.Instruction, "Page boundary crossed."); } } }
/// <summary> /// Determines whether the tokenized expression is a string. /// </summary> /// <param name="iterator">The iterator to the tokenized expression.</param> /// <param name="services">The shared assembly services.</param> /// <returns></returns> public static bool ExpressionIsAString(RandomAccessIterator <Token> iterator, AssemblyServices services) { var token = iterator.Current; if (token.IsDoubleQuote()) { return(token.Name.Length > 2 && Token.IsEnd(iterator.PeekNext())); } var ix = iterator.Index; var result = false; if (token.Type == TokenType.Function && (token.Name.Equals("format", services.StringComparison) || token.Name.Equals("char", services.StringComparison))) { iterator.MoveNext(); var parms = Token.GetGroup(iterator); var last = iterator.Current; result = Token.IsEnd(last); if (token.Name.Equals("char", services.StringComparison)) { result &= services.Evaluator.Evaluate(parms.GetIterator(), 0, 0x10FFFF).IsInteger(); } } else if (token.Type == TokenType.Operand && (char.IsLetter(token.Name[0]) || token.Name[0] == '_') && !services.Evaluator.IsReserved(token.Name)) { var sym = services.SymbolManager.GetSymbol(token, false); if (sym != null) { if (iterator.MoveNext() && iterator.Current.Name.Equals("[")) { var subscript = (int)services.Evaluator.Evaluate(iterator); result = Token.IsEnd(iterator.Current) && subscript >= 0 && subscript < sym.StringVector.Count; } else { result = Token.IsEnd(iterator.Current) && sym.StorageType == StorageType.Scalar && sym.DataType == DataType.String; } } } iterator.SetIndex(ix); return(result); }
/// <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."); } }
/// <summary> /// Seeks the <see cref="SourceLine"/> containing the /// first instance one of the directives in the block. /// </summary> /// <param name="iterator">The source line iterator.</param> /// <param name="directives">An array of directives, one of which to seek in the block.</param> protected void SeekBlockDirectives(RandomAccessIterator <SourceLine> iterator, StringView[] directives) { var line = iterator.Current; if (!line.Instruction.Name.Equals(BlockClosure, Services.StringComparison)) { var blockClose = BlockClosure; var keywordsNotToSkip = new List <StringView>(directives) { blockClose }; keywordsNotToSkip.AddRange(BlockOpens.Select(bo => new StringView(bo))); var opens = 1; while (opens != 0) { line = iterator.FirstOrDefault(l => l.Instruction != null && keywordsNotToSkip.Contains(l.Instruction.Name, Services.StringViewComparer)); if (line == null) { throw new BlockClosureException(BlockOpens.First()); } if (BlockOpens.Contains(line.Instruction.Name.ToString(), Services.StringComparer)) { opens++; } if (opens < 2 && directives.Contains(line.Instruction.Name, Services.StringViewComparer)) { break; } if (line.Instruction.Name.Equals(blockClose, Services.StringComparison)) { opens--; } } } }
protected override string OnAssemble(RandomAccessIterator <SourceLine> lines) { var line = lines.Current; if (Reserved.IsOneOf("CPU", line.Instruction.Name)) { var iterator = line.Operands.GetIterator(); if (!iterator.MoveNext() || !StringHelper.IsStringLiteral(iterator) || iterator.PeekNext() != null) { Services.Log.LogEntry(line.Instruction, "String expression expected."); } else { CPU = iterator.Current.Name.ToString().TrimOnce('"'); OnSetCpu(); } return(string.Empty); } Evaluations[0] = Evaluations[1] = Evaluations[2] = double.NaN; return(AssembleCpuInstruction(line)); }
/// <summary> /// Gets a grouping of tokens. /// </summary> /// <param name="tokens">The iterator to the full token expression.</param> /// <returns>The grouped tokens</returns> public static IEnumerable <Token> GetGroup(RandomAccessIterator <Token> tokens) { var list = new List <Token> { tokens.Current }; var open = tokens.Current.Name; var closed = OpenClose[open]; var opens = 1; while (tokens.MoveNext() && opens > 0) { list.Add(tokens.Current); if (tokens.Current.Name.Equals(open)) { opens++; } else if (tokens.Current.Name.Equals(closed)) { opens--; } } return(list); }
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); }
protected override string OnAssemble(RandomAccessIterator <SourceLine> lines) { var line = lines.Current; var instruction = line.Instruction.Name.ToLower(); var iterator = line.Operands.GetIterator(); switch (instruction) { case ".assert": DoAssert(line); break; case ".bank": SetBank(line); break; case ".warnif": case ".errorif": ThrowConditional(line); break; case ".echo": case ".error": case ".warn": if (instruction.Equals(".echo")) { if (iterator.MoveNext() && StringHelper.ExpressionIsAString(iterator, Services)) { Output(line, StringHelper.GetString(iterator, Services)); } else if (Services.Evaluator.ExpressionIsCondition(line.Operands.GetIterator())) { Output(line, Services.Evaluator.EvaluateCondition(iterator, false).ToString()); } else { Output(line, Services.Evaluator.Evaluate(iterator, false, Evaluator.CbmFloatMinValue, Evaluator.CbmFloatMaxValue).ToString()); } } else if (iterator.MoveNext() && StringHelper.ExpressionIsAString(iterator, Services)) { Output(line, StringHelper.GetString(iterator, Services)); } else { Services.Log.LogEntry(line.Instruction, "String expression expected."); } if (iterator.Current != null) { throw new SyntaxException(iterator.Current, "Unexpected expression."); } break; case ".eor": SetEor(line); break; case ".forcepass": if (line.Operands.Count > 0) { Services.Log.LogEntry(line.Operands[0], "Unexpected expression."); } else if (Services.CurrentPass == 0) { Services.PassNeeded = true; } break; case ".invoke": InvokeFunction(line); break; case ".proff": case ".pron": if (line.Operands.Count > 0) { Services.Log.LogEntry(line.Operands[0], "Unexpected expression."); } else { Services.PrintOff = instruction.Equals(".proff"); } break; case ".dsection": DefineSection(line); break; case ".section": return(SetSection(line)); case ".format": case ".target": if (Services.CurrentPass == 0) { if (Services.Output.HasOutput) { Services.Log.LogEntry(line.Instruction, "Cannot specify target format after assembly has started."); } else { if (!iterator.MoveNext() || !StringHelper.ExpressionIsAString(iterator, Services)) { Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position, "Expression must be a string."); } else if (!string.IsNullOrEmpty(Services.OutputFormat)) { Services.Log.LogEntry(line.Filename, line.LineNumber, line.Instruction.Position, "Output format was previously specified."); } else { Services.SelectFormat(StringHelper.GetString(iterator, Services)); } if (iterator.Current != null) { Services.Log.LogEntry(iterator.Current, "Unexpected expression."); } } if (instruction.Equals(".target")) { Services.Log.LogEntry(line.Instruction, "\".target\" is deprecated. Use \".format\" instead.", false); } } break; default: InitMem(line); break; } return(string.Empty); }
public void InvokeFunction(RandomAccessIterator <Token> tokens) => CallFunction(tokens, false);
public double EvaluateFunction(RandomAccessIterator <Token> tokens) => CallFunction(tokens, true);
string DoGoto(RandomAccessIterator <SourceLine> lines) { var line = lines.Current; if (line.Operands.Count == 0) { throw new SyntaxException(line.Instruction.Position, "Destination not specified for \".goto\" directive."); } var gotoExp = line.Operands[0].Name; if ((!char.IsLetter(gotoExp[0]) && gotoExp[0] != '_') || line.Operands.Count > 1) { Services.Log.LogEntry(line.Operands[0], "\".goto\" operand must be a label."); } else if (line.Label != null && gotoExp.Equals(line.Label.Name, Services.StringViewComparer)) { Services.Log.LogEntry(line.Instruction, "Destination cannot be the same line as \".goto\" directive."); } else { var iterCopy = new RandomAccessIterator <SourceLine>(lines, true); SourceLine currLine; if ((currLine = iterCopy.FirstOrDefault(l => { if (l.Instruction != null && _openClosures.ContainsKey(l.Instruction.Name)) { if (l.Instruction.Name.Equals(".function", Services.StringComparison)) { throw new SyntaxException(l.Instruction, "Function block cannot be inside another block."); } // leap over any blocks we find along the way we are not currently in. if (!_blocks.Any(b => b.Index == iterCopy.Index)) { GetProcessor(l, iterCopy.Index).SeekBlockEnd(iterCopy); } return(false); } return(l.Label != null && l.Label.Name.Equals(gotoExp, Services.StringViewComparer)); })) != null) { if (currLine.Instruction != null && (currLine.Instruction.Name.Contains('=') || currLine.Instruction.Name.Equals(".equ", Services.StringComparison) || currLine.Instruction.Name.Equals(".global", Services.StringComparison) ) ) { Services.Log.LogEntry(line.Instruction, $"\"{gotoExp}\" is not a valid destination."); } else { while (_currentBlock != null) { // if where we landed lies outside of the current block scope // we need to pop out of that scope. _currentBlock.SeekBlockEnd(lines); if (iterCopy.Index > _currentBlock.Index) { // did we land in a place still within the block scope? if (iterCopy.Index > lines.Index) { // no, pop out DoPop(lines); } else { // we're still within the current block, don't pop it break; } } else { // we went backwards, pop out of current scope DoPop(lines); } } if (iterCopy.Index >= lines.Index) { lines.FastForward(iterCopy.Index); } else if (iterCopy.Index == 0) { lines.Reset(); } else { lines.Rewind(iterCopy.Index - 1); } } } else { Services.Log.LogEntry(line.Instruction, $"Could not find destination \"{gotoExp}\"."); } } return(string.Empty); }
protected override string OnAssemble(RandomAccessIterator <SourceLine> lines) { var line = lines.Current; if (Reserved.IsOneOf("Goto", line.Instruction.Name)) { return(DoGoto(lines)); } if (Reserved.IsOneOf("Functional", line.Instruction.Name)) { if (line.Instruction.Name.Equals(".function", Services.StringComparison)) { DefineFunction(lines); } else if (_currentBlock != null) { throw new SyntaxException(line.Instruction.Position, "Directive \".endfunction\" can only be made inside function block."); } return(string.Empty); } if (_openClosures.ContainsKey(line.Instruction.Name)) { var block = GetProcessor(line, lines.Index); if (_blocks.Count == 0) { ScanBlock(lines); } _blocks.Push(block); _currentBlock = block; } if (line.Instruction.Name.Equals(".block", Services.StringComparison) && line.Label != null) { DefineLabel(line.Label, PCOnAssemble, false); } var isBreakCont = Reserved.IsOneOf("BreakContinue", line.Instruction.Name); if (_currentBlock == null || (!isBreakCont && !_currentBlock.IsReserved(line.Instruction.Name))) { throw new SyntaxException(line.Instruction.Position, $"\"{line.Instruction.Name}\" directive must come inside a block."); } if (isBreakCont) { if (line.Operands.Count > 0) { throw new SyntaxException(line.Operands[0], "Unexpected expression."); } var isBreak = line.Instruction.Name.Equals(".break", Services.StringComparison); if ((!_currentBlock.AllowContinue && line.Instruction.Name.Equals(".continue", Services.StringComparison)) || (!_currentBlock.AllowBreak && isBreak)) { while (_currentBlock != null) { var allowBreak = false; _currentBlock.SeekBlockEnd(lines); if (isBreak) { allowBreak = _currentBlock.AllowBreak; } else if (!isBreak && _currentBlock.AllowContinue) { break; } DoPop(lines); if (isBreak && allowBreak) { return(string.Empty); } } if (_currentBlock == null) { var err = isBreak ? "break" : "continue"; throw new SyntaxException(line.Instruction.Position, $"No enclosing loop out of which to {err}."); } } else if (isBreak) { DoPop(lines); return(string.Empty); } else { _currentBlock.SeekBlockEnd(lines); } } _currentBlock.ExecuteDirective(lines); if (lines.Current.Instruction != null && lines.Current.Instruction.Name.Equals(_currentBlock.BlockClosure, Services.StringComparison)) { DoPop(lines); } if (line.Label != null) { return($".{Services.Output.LogicalPC,-42:x4}{line.Source.Substring(line.Label.Position - 1, line.Label.Name.Length)}"); } return(string.Empty); }
/// <summary> /// Assemble the collection of parsed <see cref="SourceLine"/> objects. This method must be inherited. /// </summary> /// <param name="lines">A <see cref="RandomAccessIterator{T}"/> of source lines.</param> /// <returns>The disassembly output from the assembly operation.</returns> /// <exception cref="BlockClosureException"/> /// <exception cref="DirectoryNotFoundException"/> /// <exception cref="DivideByZeroException"/> /// <exception cref="ExpressionException"/> /// <exception cref="FileNotFoundException"/> /// <exception cref="FormatException"/> /// <exception cref="InvalidPCAssignmentException"/> /// <exception cref="BlockAssemblerException"/> /// <exception cref="OverflowException"/> /// <exception cref="ProgramOverflowException"/> /// <exception cref="ReturnException"/> /// <exception cref="SectionException"/> /// <exception cref="SymbolException"/> /// <exception cref="SyntaxException"/> protected abstract string OnAssemble(RandomAccessIterator <SourceLine> lines);
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()); } }
/// <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> public Symbol(RandomAccessIterator <Token> tokens, Evaluator eval) : this(tokens, eval, true) { }
/// <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(); } }
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); }