Juvix imports
module arch.system.state.resource_machine.notes.nockma;
import prelude open;
import Stdlib.Data.Nat open;
import Stdlib.Data.List open;
import Stdlib.Trait.Show open;
Nockma Implementation¶
| Operation Code | Name | Description | 
|---|---|---|
| 0 | Slash (/) | Address/path selection | 
| 1 | Constant | Returns operand unchanged | 
| 2 | Apply/Ap/S | Function application | 
| 3 | Cell test (?) | Tests if noun is cell | 
| 4 | Increment (+) | Add 1 to atom | 
| 5 | Equality test (=) | Compare nouns | 
| 6 | If-then-else | Conditional execution | 
| 7 | Compose | Function composition | 
| 8 | Extend subject | Extends the subject | 
| 9 | Invoke | Call function by arm name | 
| 10 | Pound (#) | Handle operation | 
| 11 | Match | Case split on Cells vs Atoms | 
| 12 | Scry | Read storage | 
Core Data Types¶
The fundamental data structures for Nockma implementation, including the Noun
type that represents all data in Nock, along with equality and display
instances.
type Noun :=
  | Atom : Nat -> Noun
  | Cell : Noun -> Noun -> Noun;
terminating
nounEq (n1 n2 : Noun) : Bool :=
  case mkPair n1 n2 of
    | mkPair (Noun.Atom x) (Noun.Atom y) := x == y
    | mkPair (Noun.Cell a b) (Noun.Cell c d) := nounEq a c && nounEq b d
    | _ := false;
instance
EqNoun : Eq Noun :=
  Eq.mk@{
    isEqual := nounEq;
  };
Pretty-printer for Noun¶
terminating
showNoun (n : Noun) : String :=
  case n of
    | Noun.Atom a := natToString a
    | Noun.Cell l r := "[" ++str showNoun l ++str " " ++str showNoun r ++str "]";
instance
ShowNoun : Show Noun :=
  Show.mk@{
    show := showNoun;
  };
Storage System and Operation Types¶
Storage abstraction for scrying operations and the enumeration of all Nock operations with their corresponding opcodes.
axiom convertToNoun : {val : Type} -> val -> Noun;
axiom convertFromNoun : {val : Type} -> Noun -> Option val;
type ScryOp :=
  | Direct
  | Index;
type Storage addr val :=
  mkStorage@{
    readDirect : addr -> Option val;
    readIndex : val -> Option val;
  };
emptyStorage {addr val : Type} : Storage addr val :=
  Storage.mkStorage@{
    readDirect := \{_ := none};
    readIndex := \{_ := none};
  };
axiom externalStorage : {addr val : Type} -> Storage addr val;
type NockOp :=
  | Slash
  | Constant
  | Apply
  | CellTest
  | Increment
  | EqualOp
  | IfThenElse
  | Compose
  | Extend
  | Invoke
  | Pound
  | Match
  | Scry;
opOr {A : Type} (n m : Option A) : Option A :=
  case n of
    | none := m
    | some n := some n;
parseOp (n : Nat) : Option NockOp :=
  let
    test :=
      \{m op :=
        case n == m of
          | true := some op
          | false := none};
  in foldr
    opOr
    none
    (zipWith
      test
      [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12]
      [
        NockOp.Slash;
        NockOp.Constant;
        NockOp.Apply;
        NockOp.CellTest;
        NockOp.Increment;
        NockOp.EqualOp;
        NockOp.IfThenElse;
        NockOp.Compose;
        NockOp.Extend;
        NockOp.Invoke;
        NockOp.Pound;
        NockOp.Match;
        NockOp.Scry;
      ]);
Gas State Monad¶
A monadic framework for tracking gas consumption and handling errors during Nock evaluation. This ensures computations can be bounded and failures can be properly handled.
type GasState A :=
  mk@{
    runGasState : Nat -> Result String (Pair A Nat);
  };
instance
GasStateMonad : Monad GasState :=
  Monad.mk@{
    applicative :=
      Applicative.mk@{
        functor :=
          Functor.mk@{
            map :=
              \{f s :=
                GasState.mk
                  \{gas :=
                    case GasState.runGasState s gas of
                      | ok (mkPair x remaining) := ok (mkPair (f x) remaining)
                      | error e := error e}};
          };
        pure := \{x := GasState.mk \{gas := ok (mkPair x gas)}};
        ap :=
          \{sf sa :=
            GasState.mk
              \{gas :=
                case GasState.runGasState sf gas of
                  | ok (mkPair f remaining) :=
                    case GasState.runGasState sa remaining of {
                      | ok (mkPair x final) := ok (mkPair (f x) final)
                      | error e := error e
                    }
                  | error e := error e}};
      };
    bind :=
      \{ma f :=
        GasState.mk
          \{gas :=
            case GasState.runGasState ma gas of
              | ok (mkPair a remaining) := GasState.runGasState (f a) remaining
              | error e := error e}};
  };
err {A : Type} (str : String) : GasState A := GasState.mk \{_ := error str};
Gas Management and Storage Operations¶
Functions for managing computational costs and implementing storage read operations (scrying) that allow Nock programs to interact with external data.
getGasCost (cost : NockOp) : Nat :=
  case cost of
    | NockOp.Slash := 1
    | NockOp.CellTest := 1
    | NockOp.Increment := 1
    | NockOp.EqualOp := 2
    | NockOp.IfThenElse := 3
    | NockOp.Compose := 2
    | NockOp.Extend := 2
    | NockOp.Invoke := 3
    | NockOp.Pound := 1
    | NockOp.Scry := 10
    | _ := 0;
consume (op : NockOp) : GasState Unit :=
  GasState.mk
    \{gas :=
      let
        cost := getGasCost op;
      in case cost > gas of
           | true := error "Out of gas"
           | false := ok (mkPair unit (sub gas cost))};
scry
  {val : Type}
  (stor : Storage Nat val)
  (op : ScryOp)
  (addr : Nat)
  : Result String Noun :=
  case op of
    | ScryOp.Direct :=
      case Storage.readDirect stor addr of {
        | some val := ok (convertToNoun val)
        | none := error "Direct storage read failed"
      }
    | ScryOp.Index :=
      case Storage.readDirect stor addr of
        | some indexFn :=
          case Storage.readIndex stor indexFn of {
            | some val := ok (convertToNoun val)
            | none := error "Index computation failed"
          }
        | none := error "Index function not found";
Helper Operations¶
Implementation of the fundamental slash (/) and pound (#) operations that
provide tree navigation and editing capabilities respectively.
terminating
slash
  {val : Type}
  (stor : Storage Nat val)
  (n : Noun)
  (subject : Noun)
  : GasState Noun :=
  case n of
    | Noun.Atom x :=
      case x == 1 of {
        | true := pure subject
        | false :=
          case x == 2 of {
            | true :=
              case subject of {
                | Noun.Cell a _ := pure a
                | _ :=
                  err
                    ("Cannot take slash (/2) of atom: " ++str showNoun subject)
              }
            | false :=
              case x == 3 of {
                | true :=
                  case subject of {
                    | Noun.Cell _ b := pure b
                    | _ :=
                      err
                        ("Cannot take slash (/3) of atom: "
                          ++str showNoun subject)
                  }
                | false :=
                  case mod x 2 == 0 of {
                    | true :=
                      consume NockOp.Slash
                        >>= \{_ :=
                          slash stor (Noun.Atom (div x 2)) subject
                            >>= \{res :=
                              consume NockOp.Slash
                                >>= \{_ := slash stor (Noun.Atom 2) res}}}
                    | false :=
                      consume NockOp.Slash
                        >>= \{_ :=
                          slash stor (Noun.Atom (div x 2)) subject
                            >>= \{res :=
                              consume NockOp.Slash
                                >>= \{_ := slash stor (Noun.Atom 3) res}}}
                  }
              }
          }
      }
    | _ := err ("Slash axis must be atom, got: " ++str showNoun n);
terminating
pound
  {val : Type}
  (stor : Storage Nat val)
  (n : Noun)
  (b : Noun)
  (c : Noun)
  : GasState Noun :=
  case n of
    | Noun.Atom x :=
      case x == 1 of {
        | true := pure b
        | false :=
          case mod x 2 == 0 of {
            | true :=
              case c of {
                | Noun.Cell _ _ :=
                  consume NockOp.Slash
                    >>= \{_ :=
                      slash stor (Noun.Atom (2 * div x 2 + 1)) c
                        >>= \{slashResult :=
                          consume NockOp.Pound
                            >>= \{_ :=
                              pound
                                stor
                                (Noun.Atom (div x 2))
                                (Noun.Cell b slashResult)
                                c}}}
                | _ :=
                  err ("Invalid pound target (must be cell): " ++str showNoun c)
              }
            | false :=
              case c of {
                | Noun.Cell _ _ :=
                  consume NockOp.Slash
                    >>= \{_ :=
                      slash stor (Noun.Atom (2 * div x 2)) c
                        >>= \{slashResult :=
                          consume NockOp.Pound
                            >>= \{_ :=
                              pound
                                stor
                                (Noun.Atom (div x 2))
                                (Noun.Cell slashResult b)
                                c}}}
                | _ :=
                  err ("Invalid pound target (must be cell): " ++str showNoun c)
              }
          }
      }
    | _ := err ("Pound axis must be atom, got: " ++str showNoun n);
Operation Evaluator¶
The main dispatcher that handles evaluation of each Nock operation according to the Nock specification. Each case implements one of the 13 fundamental Nock operations.
terminating
evalOp
  {val : Type}
  (stor : Storage Nat val)
  (op : NockOp)
  (a : Noun)
  (args : Noun)
  : GasState Noun :=
  case op of
    | NockOp.Slash := slash stor args a
    | NockOp.Constant := pure args
    | NockOp.Apply :=
      case args of {
        | Noun.Cell b c :=
          nock stor (Noun.Cell a b)
            >>= \{r1 :=
              nock stor (Noun.Cell a c)
                >>= \{r2 := nock stor (Noun.Cell r1 r2)}}
        | _ :=
          err ("Invalid apply (2) args (must be cell): " ++str showNoun args)
      }
    | NockOp.CellTest :=
      case args of {
        | Noun.Cell b _ :=
          nock stor (Noun.Cell a b)
            >>= \{res :=
              case res of
                | Noun.Cell _ _ := pure (Noun.Atom 0)
                | _ := pure (Noun.Atom 1)}
        | _ :=
          err
            ("Invalid cell test (3) args (must be cell): " ++str showNoun args)
      }
    | NockOp.Increment :=
      nock stor (Noun.Cell a args)
        >>= \{res :=
          case res of
            | Noun.Atom n := pure (Noun.Atom (suc n))
            | x :=
              err ("Increment (4) target must be atom, got: " ++str showNoun x)}
    | NockOp.EqualOp :=
      case args of {
        | Noun.Cell b c :=
          nock stor (Noun.Cell a b)
            >>= \{r1 :=
              nock stor (Noun.Cell a c)
                >>= \{r2 :=
                  pure
                    (Noun.Atom
                      case nounEq r1 r2 of {
                        | true := 0
                        | false := 1
                      })}}
        | _ :=
          err ("Invalid equality (5) args (must be cell): " ++str showNoun args)
      }
    | NockOp.IfThenElse :=
      case args of {
        | Noun.Cell b (Noun.Cell c d) :=
          nock
              stor
              (Noun.Cell
                a
                (Noun.Cell (Noun.Atom 4) (Noun.Cell (Noun.Atom 4) b)))
            >>= \{r1 :=
              nock
                  stor
                  (Noun.Cell
                    (Noun.Cell (Noun.Atom 2) (Noun.Atom 3))
                    (Noun.Cell (Noun.Atom 0) r1))
                >>= \{r2 :=
                  nock
                      stor
                      (Noun.Cell (Noun.Cell c d) (Noun.Cell (Noun.Atom 0) r2))
                    >>= \{r3 := nock stor (Noun.Cell a r3)}}}
        | _ :=
          err
            ("Invalid if-then-else (6) args (must be [b [c d]]): "
              ++str showNoun args)
      }
    | NockOp.Compose :=
      case args of {
        | Noun.Cell b c :=
          nock stor (Noun.Cell a b) >>= \{r := nock stor (Noun.Cell r c)}
        | _ :=
          err ("Invalid compose (7) args (must be cell): " ++str showNoun args)
      }
    | NockOp.Extend :=
      case args of {
        | Noun.Cell b c :=
          nock stor (Noun.Cell a b)
            >>= \{r := nock stor (Noun.Cell (Noun.Cell r a) c)}
        | _ :=
          err ("Invalid extend (8) args (must be cell): " ++str showNoun args)
      }
    | NockOp.Invoke :=
      case args of {
        | Noun.Cell b c :=
          nock stor (Noun.Cell a c)
            >>= \{core :=
              let
                formula :=
                  Noun.Cell
                    (Noun.Atom 2)
                    (Noun.Cell
                      (Noun.Cell (Noun.Atom 0) (Noun.Atom 1))
                      (Noun.Cell (Noun.Atom 0) b));
              in nock stor (Noun.Cell core formula)}
        | _ :=
          err ("Invalid invoke (9) args (must be cell): " ++str showNoun args)
      }
    | NockOp.Pound :=
      case args of {
        | Noun.Cell (Noun.Cell b c) d :=
          nock stor (Noun.Cell a c)
            >>= \{r1 :=
              nock stor (Noun.Cell a d) >>= \{r2 := pound stor b r1 r2}}
        | _ :=
          err
            ("Invalid pound (10) args (must be [[b c] d]): "
              ++str showNoun args)
      }
    | NockOp.Match :=
      case args of {
        | Noun.Cell (Noun.Cell b c) d :=
          nock stor (Noun.Cell a c)
            >>= \{r1 :=
              nock stor (Noun.Cell a d)
                >>= \{r2 :=
                  nock
                    stor
                    (Noun.Cell
                      (Noun.Cell r1 r2)
                      (Noun.Cell (Noun.Atom 0) (Noun.Atom 3)))}}
        | Noun.Cell b c := nock stor (Noun.Cell a c)
        | _ :=
          err ("Invalid match (11) args (must be cell): " ++str showNoun args)
      }
    | Scry :=
      case args of
        | Noun.Cell b (Noun.Cell c d) :=
          nock stor b
            >>= \{opcode :=
              case opcode of
                | Noun.Atom opval :=
                  nock stor c
                    >>= \{addr :=
                      case addr of
                        | Noun.Atom addrVal :=
                          let
                            scryType :=
                              case opval == 0 of
                                | true := ScryOp.Direct
                                | false := ScryOp.Index;
                          in GasState.mk
                            \{gas :=
                              scry stor scryType addrVal
                                >>= \{scryResult :=
                                  GasState.runGasState
                                    (nock
                                      stor
                                      (Noun.Cell a (Noun.Cell scryResult d)))
                                    gas}}
                        | _ :=
                          err
                            ("Scry address must be atom, got: "
                              ++str showNoun addr)}
                | _ :=
                  err ("Scry type must be atom, got: " ++str showNoun opcode)}
        | _ :=
          err
            ("Invalid scry (12) args (must be [b [c d]]): " ++str showNoun args);
Core Nockma Evaluator¶
The main entry point for Nock evaluation. This function handles the parsing of Nock expressions and dispatches to the appropriate operation evaluators.
terminating
nock {val : Type} (stor : Storage Nat val) (input : Noun) : GasState Noun :=
  case input of
    | Noun.Atom n :=
      err ("Cannot evaluate atom as program: " ++str showNoun (Noun.Atom n))
    | Noun.Cell a b :=
      case b of
        | Noun.Cell first rest :=
          case first of {
            | Noun.Cell b c :=
              nock stor (Noun.Cell a (Noun.Cell b c))
                >>= \{r1 :=
                  nock stor (Noun.Cell a rest)
                    >>= \{r2 := pure (Noun.Cell r1 r2)}}
            | Noun.Atom n :=
              case parseOp n of {
                | some opcode :=
                  consume opcode >>= \{_ := evalOp stor opcode a rest}
                | none :=
                  err
                    ("Invalid operation code: "
                      ++str natToString n
                      ++str " in formula: "
                      ++str showNoun b)
              }
          }
        | Noun.Atom bn :=
          err
            ("Formula cannot be an atom: "
              ++str showNoun (Noun.Atom bn)
              ++str " in input: "
              ++str showNoun input);