public double CallFunction(Token function, Token parameters, bool returnValueExpected) { if (!_functionDefs.ContainsKey(function.Name)) { throw new ExpressionException(function.Position, $"Unknown function name \"{function.Name}\"."); } var evalParms = new List <object>(parameters.Children.Count); foreach (Token parm in from parm in parameters.Children where parm.HasChildren select parm) { if (parm.ToString().EnclosedInDoubleQuotes()) { evalParms.Add(parm.ToString()); } else { evalParms.Add(Evaluator.Evaluate(parm)); } } // save pre-call context var returnIndex = Assembler.LineIterator.Index; BlockProcessorBase lastBlock = _currentBlock; Assembler.SymbolManager.PushScopeEphemeral(); var fcnBlock = new FunctionBlock(Assembler.LineIterator.Current, BlockType.Functional); _blocks.Push(fcnBlock); _currentBlock = fcnBlock; // invoke the function, get the return var value = _functionDefs[function.Name].Invoke(this, evalParms); // restore context post-call while (_currentBlock != lastBlock) { _currentBlock.SeekBlockEnd(); PopBlock(); } Assembler.SymbolManager.PopScope(); Assembler.LineIterator.SetIndex(returnIndex); if (double.IsNaN(value) && returnValueExpected) { throw new ExpressionException(function.Position, $"Function \"{function.Name}\" did not return a value."); } return(value); }
void DoPop(RandomAccessIterator <SourceLine> lines) { _currentBlock.PopScope(lines); _blocks.Pop(); if (_blocks.Count > 0) { _currentBlock = _blocks.Peek(); } else { _currentBlock = null; } }
void PopBlock() { if (!Assembler.CurrentLine.InstructionName.Equals(_currentBlock.Directive.Closure)) { Assembler.Log.LogEntry(Assembler.CurrentLine, Assembler.CurrentLine.Instruction, $"Missing closure for \"{_currentBlock.Directive.Open}\" directive."); _currentBlock = null; } else { DoPop(); } }
void DoPop() { if (_currentBlock != null) { _currentBlock.Pop(); } _blocks.Pop(); if (_blocks.Count > 0) { _currentBlock = _blocks.Peek(); } else { _currentBlock = null; } }
/// <summary> /// Constructs a new instance of a block assembler. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> public BlockAssembler(AssemblyServices services) : base(services) { _blocks = new Stack <BlockProcessorBase>(); _functionDefs = new Dictionary <StringView, Function>(services.StringViewComparer); _currentBlock = null; _openClosures = new Dictionary <StringView, StringView>(services.StringViewComparer) { { ".block", ".endblock" }, { ".for", ".next" }, { ".foreach", ".next" }, { ".function", ".endfunction" }, { ".if", ".endif" }, { ".ifdef", ".endif" }, { ".ifndef", ".endif" }, { ".namespace", ".endnamespace" }, { ".page", ".endpage" }, { ".repeat", ".endrepeat" }, { ".switch", ".endswitch" }, { ".while", ".endwhile" } }; Reserved.DefineType("Functional", ".function", ".endfunction"); Reserved.DefineType("NonOpens", ".break", ".case", ".continue", ".default", ".endblock", ".endif", ".endfunction", ".endpage", ".endnamespace", ".endrepeat", ".endswitch", ".endwhile", ".else", ".elseif", ".elseifdef", ".elseifdef", ".elseifndef", ".next"); Reserved.DefineType("BreakContinue", ".break", ".continue"); Reserved.DefineType("Goto", ".goto"); ExcludedInstructionsForLabelDefines.Add(".function"); ExcludedInstructionsForLabelDefines.Add(".block"); Services.Evaluator.AddFunctionEvaluator(this); Services.IsReserved.Add(s => _functionDefs.ContainsKey(s)); }
/// <summary> /// Constructs a new instance of the multiline assembler class. /// </summary> public MultiLineAssembler() { _blocks = new Stack <BlockProcessorBase>(); _functionDefs = new Dictionary <string, Function>(); _currentBlock = null; Reserved.DefineType("Scope", ".block", ".endblock"); Reserved.DefineType("Conditional", ".if", ".ifdef", ".ifndef", ".else", ".elseif", ".elseifdef", ".elseifndef", ".endif"); Reserved.DefineType("SwitchCase", ".switch", ".case", ".default", ".endswitch"); Reserved.DefineType("Functional", ".function", ".endfunction", ".invoke", ".return"); Reserved.DefineType("ForNext", ".for", ".next"); Reserved.DefineType("While", ".while", ".endwhile"); Reserved.DefineType("Repeat", ".repeat", ".endrepeat"); Reserved.DefineType("BreakContinue", ".break", ".continue"); Reserved.DefineType("Page", ".page", ".endpage"); Reserved.DefineType("GotoEnd", ".goto", ".end"); Evaluator.AddFunctionEvaluator(this); Assembler.PassChanged += CheckCurrentLineAtPass; }
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); }
protected override string OnAssembleLine(SourceLine line) { if (line.InstructionName.Equals(".end")) { while (Assembler.LineIterator.MoveNext()) { } return(string.Empty); } if (Reserved.IsOneOf("Functional", line.InstructionName)) { if (line.InstructionName.Equals(".function")) { DefineFunction(line); } else if (line.InstructionName.Equals(".invoke")) { if (!line.OperandHasToken) { Assembler.Log.LogEntry(line, line.Operand, "Missing function name from invocation directive."); } else { var fcnName = line.Operand.Children[0].Children[0].Name; if (line.Operand.Children[0].Children.Count > 2 || line.Operand.Children[0].Children[0].OperatorType != OperatorType.Function) { Assembler.Log.LogEntry(line, line.Operand.LastChild, "Bad function call."); } else if (!_functionDefs.ContainsKey(fcnName)) { Evaluator.Evaluate(line.Operand.Children[0]); } else { _ = CallFunction(line.Operand.Children[0].Children[0], line.Operand.Children[0].Children[1], false); } } } else { Assembler.Log.LogEntry(line, line.Instruction, $"Directive \"{line.InstructionName}\" can only be made inside function block."); } return(string.Empty); } if (_blockOpenTypes.ContainsKey(line.InstructionName)) { BlockProcessorBase block; BlockType type = _blockOpenTypes[line.InstructionName]; switch (type) { case BlockType.ForNext: block = new ForNextBlock(line, type); break; case BlockType.Repeat: block = new RepeatBlock(line, type); break; case BlockType.Conditional: case BlockType.ConditionalDef: case BlockType.ConditionalNdef: block = new ConditionalBlock(line, type); break; case BlockType.Scope: block = new ScopeBlock(line, type); break; case BlockType.Goto: DoGoto(line); return(string.Empty); case BlockType.Page: block = new PageBlockProcessor(line, type); break; case BlockType.Switch: block = new SwitchBlock(line, type); break; default: block = new WhileBlock(line, type); break; } _blocks.Push(block); _currentBlock = block; } if (_currentBlock != null) { if (Assembler.CurrentLine.InstructionName.Equals(".break") || Assembler.CurrentLine.InstructionName.Equals(".continue")) { var contBreakLine = Assembler.LineIterator.Current; if (!_currentBlock.AllowContinue && contBreakLine.InstructionName.Equals(".continue")) { _currentBlock.SeekBlockEnd(); while (_currentBlock != null) { _currentBlock.SeekBlockEnd(); if (_currentBlock.AllowContinue) { break; } else { PopBlock(); } } if (_currentBlock == null) { Assembler.Log.LogEntry(contBreakLine, contBreakLine.Instruction, "No enclosing loop out of which to continue."); return(string.Empty); } } else if (!_currentBlock.AllowBreak && contBreakLine.InstructionName.Equals(".break")) { _currentBlock.SeekBlockEnd(); while (_currentBlock != null) { _currentBlock.SeekBlockEnd(); if (_currentBlock.AllowBreak) { PopBlock(); return(string.Empty); } else { PopBlock(); } } if (_currentBlock == null) { Assembler.Log.LogEntry(contBreakLine, contBreakLine.Instruction, "No enclosing loop out of which to break."); return(string.Empty); } } else { _currentBlock.SeekBlockEnd(); if (contBreakLine.Instruction.Equals(".break")) { return(string.Empty); } } } _currentBlock.ExecuteDirective(); if (Assembler.CurrentLine == null || Assembler.CurrentLine.InstructionName.Equals(BlockDirective.Directives[_currentBlock.Type].Closure)) { if (Assembler.CurrentLine == null) { line = SourceLineHelper.GetLastInstructionLine(); Assembler.Log.LogEntry(line, $"Missing closure for \"{BlockDirective.Directives[_currentBlock.Type].Open}\" directive.", true); } DoPop(); } } else { Assembler.Log.LogEntry(line, line.Instruction.Position, $"\"{line.InstructionName}\" directive must come inside a block."); } if (line.Label != null) { return($".{Assembler.Output.LogicalPC,-42:x4}{line.UnparsedSource.Substring(line.Label.Position - 1, line.Label.Name.Length)}"); } return(string.Empty); }