private const byte NoRegister = 0xff; // Target register meaning not to write to register // So here's append([], X, X). : // MatchLiteral([], 0) // MatchVarFirst(1) % This is actually a no-op // MatchVar(1, 2) // Return // // And here's append([H|T], X, [H|T1]) :- append(T, X, T1). // MatchStructure(dot, 2, L1, 0) // MatchVarFirst(0>0>3) % H is at 3 // MatchVarFirst(0>1>4) % T is at 4 // L1 MatchVarFirst(1) % Actually a no-op // MatchStructure(dot, 2, L2, 2) // MatchVar(3, 2>0) // MatchVarFirst(5, 2>1) % T1 is at 5 // L2 Call(6, Fail, Succeed, append/3, register(4), register(2), register(5)) #endregion #region Interpreter internal IEnumerable <CutState> StackCall(PrologContext context) { int traceMark = context.MarkTrail(); // On entry, the stack pointer points at the base of our arguments. int framePointer = context.MakeFrame(frameSize); ushort pc = 0; ushort writeModeEndAddress = 0; // We're in write mode if pc < writeModeEndAddress. // ReSharper disable TooWideLocalVariableScope // Resharper is confused - can't move this inside the while loop, or its lifetime changes. ushort endOfBuilds; // PC of last call instruction whose arguments have been built. // ReSharper restore TooWideLocalVariableScope while (true) { switch ((Opcode)code[pc++]) { #region Head opcodes case Opcode.MatchLiteral: { object literal = GetLiteral(ref pc); if (pc < writeModeEndAddress) { // Write mode SetOperand(context, framePointer, ref pc, literal); } else { // Read mode if (!Term.Unify(DecodeOperand(context, framePointer, ref pc), literal, context)) { goto fail; } } } break; case Opcode.MatchVarFirst: { if (pc < writeModeEndAddress) { // Write mode Symbol name = GetSymbol(ref pc); int register = code[pc++]; var l = new LogicVariable(name); SetOperand(context, framePointer, ref pc, l); if (register != NoRegister) { context.SetStack(framePointer, register, l); } } else { pc += 2; // Skip over variable name. int register = code[pc++]; // Read mode object operand = DecodeOperand(context, framePointer, ref pc); if (register != NoRegister) { context.SetStack(framePointer, register, operand); } } } break; case Opcode.MatchVar: { object register = Register(context, framePointer, pc++); if (pc < writeModeEndAddress) { // Write mode SetOperand(context, framePointer, ref pc, register); } else { // Read mode if (!Term.Unify(DecodeOperand(context, framePointer, ref pc), register, context)) { goto fail; } } } break; case Opcode.MatchStructure: { Symbol functor = GetSymbol(ref pc); int arity = code[pc++]; ushort endAddress = GetUShort(ref pc); if (pc < writeModeEndAddress) { // Write mode SetOperand(context, framePointer, ref pc, new Structure(functor, new object[arity])); } else { // Read mode object arg = DecodeOperand(context, framePointer, ref pc); var s = arg as Structure; if (s == null || !s.IsFunctor(functor, arity)) { var l = arg as LogicVariable; if (l == null) { goto fail; } l.UnifyWithStructure(new Structure(functor, new object[arity])); writeModeEndAddress = endAddress; } } } break; #endregion #region Body opcodes: structure building case Opcode.BuildStructure: { Symbol functor = GetSymbol(ref pc); int arity = code[pc++]; SetOperand(context, framePointer, ref pc, new Structure(functor, new object[arity])); } break; case Opcode.BuildLiteral: SetOperand(context, framePointer, ref pc, GetLiteral(ref pc)); break; case Opcode.BuildVar: { Symbol name = GetSymbol(ref pc); SetOperand(context, framePointer, ref pc, new LogicVariable(name)); } break; case Opcode.BuildReg: object registerValue = Register(context, framePointer, pc++); SetOperand(context, framePointer, ref pc, registerValue); break; #endregion #region Body: control flow case Opcode.CallWokenGoals: case Opcode.Call: case Opcode.CallPrimitive: endOfBuilds = pc; // ReSharper disable once InconsistentNaming ushort succeedPC; IEnumerator <CutState> iterator; var failPC = StartCallInstruction(context, framePointer, ref pc, out succeedPC, out iterator); while (true) { if (iterator == null || iterator.MoveNext()) { // Call succeeds if (succeedPC == SuccessContinuationPC) { yield return(CutState.Continue); // If we get here, then our caller is requesting a redo // Fall through and continue this iterator. if (iterator == null) { goto fail; // Kluge: special case to handle CallWokenGoals instruction where there are no woken goals. } } else { if (succeedPC > endOfBuilds) { // Haven't run the build instructions for next goal, so break out and run them. break; } pc = (ushort)(succeedPC + 1); // StartCallInstruction assumes we've skipped over the opcode. // succeedPC is address of call/callprimitive instruction; next byte is iterator register failPC = this.StartCallInstruction(context, framePointer, ref pc, out succeedPC, out iterator); // Continue while loop } } else { // Call fails switch (failPC) { case FailContinuationPC: goto fail; case CutContinuationPC: goto cut; default: // failPC is address of call/callprimitive instruction; next byte is iterator register pc = (ushort)(failPC + 1); // +1 because we're skipping over the opcode iterator = (IEnumerator <CutState>)context.GetStack(framePointer, code[pc++]); if (iterator == null) // Kluge: special case to handle CallWokenGoals instruction where there are no woken goals. { goto fail; } failPC = GetUShort(ref pc); succeedPC = GetUShort(ref pc); break; } } } break; #endregion default: Debug.Assert(false, "Bad opcode in byte compiled Prolog rule, pc=" + (pc - 1) + ", opcode=" + ((Opcode)code[pc - 1])); break; } } // The while loop above never terminates normally; it only yeilds or branches to one of the following labels cut: context.PopFrame(framePointer); context.RestoreVariables(traceMark); yield return(CutState.ForceFail); fail: context.PopFrame(framePointer); context.RestoreVariables(traceMark); }