public ByteCodeExecutor(ByteCodePoint[] program, ILiteral[] literals, int maxVariableIndex) { if (program == null) throw new ArgumentNullException(nameof(program)); _program = program; _literals = literals; _programCounter = 0; _environment = new ByteCodeEnvironment(0, 0, null); _registers = new SimpleReference[maxVariableIndex]; for (var registerIndex = 0; registerIndex<_registers.Length; ++registerIndex) { _registers[registerIndex] = new SimpleReference(); } }
/// <summary> /// Sets a label referencing the next instruction to be written /// </summary> public int Label(object label) { // Write the label var labelIp = _program.Count; _ipsWithLabel[label] = labelIp; // Update any instructions that reference the label List <int> existingReferences; if (_ipsReferencingLabel.TryGetValue(label, out existingReferences)) { foreach (var refIp in existingReferences) { var old = _program[refIp]; _program[refIp] = new ByteCodePoint(old.Op, labelIp - refIp, old.Arg2); } existingReferences.Clear(); } return(labelIp); }
/// <summary> /// Takes any call operations found in this program and replaces them with CallAddress operations /// </summary> /// <param name="predicateToLabel">Given the predicate for a Call instruction, returns null or the object representing the label for the address to call</param> public void BindCalls(Func <ILiteral, object> predicateToLabel) { if (predicateToLabel == null) { throw new ArgumentNullException(nameof(predicateToLabel)); } for (int address = 0; address < _program.Count; ++address) { if (_program[address].Op == Operation.Call) { // Ask for the value of this label var label = predicateToLabel(_literals[_program[address].Literal]); var argumentCount = _program[address].Arg2; int labelAddress; // Try to map it to an existing address if (label != null && _ipsWithLabel.TryGetValue(label, out labelAddress)) { _program[address] = new ByteCodePoint(Operation.CallAddress, labelAddress, argumentCount); } } } }
/// <summary> /// Dispatches a particular code point /// </summary> public void Dispatch(ByteCodePoint codePoint) { // Action depends on the opcode switch (codePoint.Op) { case Operation.Nothing: break; case Operation.GetStructure: GetStructure(codePoint.Literal, codePoint.Arg1, codePoint.Arg2); break; case Operation.GetVariable: GetVariable(codePoint.Arg1, codePoint.Arg2); break; case Operation.GetValue: GetValue(codePoint.Arg1, codePoint.Arg2); break; case Operation.PutStructure: PutStructure(codePoint.Literal, codePoint.Arg1, codePoint.Arg2); break; case Operation.PutVariable: PutVariable(codePoint.Arg1, codePoint.Arg2); break; case Operation.PutValue: PutValue(codePoint.Arg1, codePoint.Arg2); break; case Operation.SetVariable: SetVariable(codePoint.Arg1); break; case Operation.SetValue: SetValue(codePoint.Arg1); break; case Operation.UnifyVariable: UnifyVariable(codePoint.Arg1); break; case Operation.UnifyValue: UnifyValue(codePoint.Arg1); break; case Operation.Allocate: Allocate(codePoint.Arg1, codePoint.Arg2); break; case Operation.Deallocate: Deallocate(); break; case Operation.Proceed: Proceed(); break; case Operation.CallAddress: CallAddress(codePoint.Arg1, codePoint.Arg2); break; case Operation.TryMeElse: TryMeElse(codePoint.Arg1); break; case Operation.RetryMeElse: RetryMeElse(codePoint.Arg1); break; case Operation.TrustMe: TrustMe(); break; case Operation.Call: throw new NotImplementedException("External calls not yet supported"); default: throw new NotImplementedException("Unknown opcode"); } }