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); }
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); }