{"body":"## Tauto is a system for defining equivalencies. Very simply, it collects facts\n## in the form of domain:sign binaries, and associates them with each other.\n## Facts have no external meaning; they simply are defined as sharing\n## referents. \n##\n## After equivalencies are defined, Tauto can then be queried for equivalencies on any fact.\n\nimport tables, sets, strutils, future\n\nconst BOTTOM = \"%\"\nconst DELIMITER = \"/\"\n\ntype\n  Referent* = range[int.low..0]\n  Domain = string\n  Sign = string\n  Proposal* = tuple[domain: Domain, sign: Sign]\n  Statement* = tuple[domain: Domain, sign: Sign, referent: Referent]\n  StatementTable = TableRef[string, Statement]\n  DomainIndex = TableRef[Sign, StatementTable]\n  ReferentIndex = TableRef[Referent, HashSet[Statement]]\n  Host = ref HostObj\n  HostObj = object\n    name: string\n    domainIndex: DomainIndex\n    referentIndex: ReferentIndex\n\nvar registry = newTable[string, Referent]()\n\nlet newSymbol = iterator: int =\n  for n in countdown(-1, int.low):\n    yield n\n\nproc newReferent(): Referent =\n  newSymbol()\n\nproc newHost(name: string = \"\"): Host =\n  var\n    di: DomainIndex = newTable[Domain, StatementTable]()\n    ri: ReferentIndex = newTable[Referent, HashSet[Statement]]()\n\n  let hostRef = newReferent()\n  registry[name] = hostRef\n\n  var h = Host(name: name, domainIndex: di, referentIndex: ri)\n  new(h)\n  return h\n\nproc getName(host: Host): Sign =\n  let \n    referent = registry[host.name]\n    ri = host.referentIndex\n  if referent in ri:\n    # The host might contain multiple names for itself, so simply return the\n    # first one.\n    for statement in ri[referent]:\n      if statement.domain != BOTTOM:\n        return statement.sign\n  else:\n    return \"%\" & DELIMITER & host.name\n\nproc `$`(s: Statement): string =\n  s.domain & DELIMITER & s.sign\n\nproc setDefault(idx: DomainIndex, domain: Domain): var StatementTable = \n  if not (domain in idx):\n    idx[domain] = newTable[Sign, Statement]()\n  idx[domain]\nproc setDefault(idx: ReferentIndex, referent: Referent): var HashSet[Statement] = \n  if not (referent in idx):\n    idx[referent] = initSet[Statement]()\n  idx[referent]\n\ntype LogicError = object of Exception\n\nproc propose*(host: Host, proposals: seq[Proposal]): Referent {.raises: [LogicError, KeyError].} = \n  ## Accepts a series of proposals, being a sequence of domain-sign binaries\n  ## that are proposed to be equivalent.\n  # Look for an existing referent to expand upon.\n  for proposal in proposals:\n    let\n      domain = proposal.domain\n      sign = proposal.sign\n    case domain:\n      of BOTTOM:\n        result = registry[sign]\n      else: \n        if domain in host.domainIndex and sign in host.domainIndex[domain]:\n          let fromDomain = host.domainIndex[domain][sign].referent\n          if result == 0:\n            result = fromDomain\n          elif result != fromDomain:\n            # If more than one existing referent is found, we are proposing that\n            # two things known to be distinct are equivalent. This is a\n            # contradiction.\n            raise newException(LogicError, \"Multiple referents found in proposal.\")\n  # Otherwise, create a new entity.\n  if result == 0:\n    result = newReferent()\n\n  for proposal in proposals:\n    let\n      domain = proposal.domain\n      sign = proposal.sign\n      statement = (domain, sign, result)\n\n    host.domainIndex.setDefault(domain)[sign] = statement\n    host.referentIndex.setDefault(result).incl(statement)\n\nproc retrieve*(host: Host, domain: Domain, sign: Sign): HashSet[Statement] =\n  ## Given a domain-sign binary, attempt to resolve it to a reference entity\n  ## and return all the statements pertaining to that referent (that is, all\n  ## the statements equivalent to the given binary).\n  if domain in host.domainIndex and sign in host.domainIndex[domain]:\n    let statement = host.domainIndex[domain][sign]\n    return host.referentIndex[statement.referent]\n  else: \n    return initSet[Statement](1)\n\nwhen isMainModule:\n\n  type \n    TautoUI = object\n      host: Host\n\n  proc prompt(ui: TautoUI): string =\n    \"[\" & ui.host.getName & \"] >> \" \n\n  proc newProposal(ui: TautoUI, proposalString: string): Proposal =\n    let \n      splitString = proposalString.strip().split(sep=DELIMITER, maxsplit=1)\n      token = splitString[0]\n    case splitString.len:\n      of 1:\n        (ui.host.name, token)\n      else:\n        (token, splitString[1])\n\n  proc propose(ui: TautoUI, commandString: string) =\n    let proposals = lc[ \n      ui.newProposal(proposalString) | (proposalString <- commandString.split(sep=',')), \n      Proposal ]\n    try:\n      discard ui.host.propose(proposals)\n      echo \"ok\"\n    except LogicError:\n      echo \"Error: \" & $getCurrentExceptionMsg()\n\n  proc render(ui: TautoUI, s: Statement): string =\n    ## Render a statement into a string.\n    # If the statement's domain is of the auto-assigned form, do a lookup for a\n    # native name of the domain.\n    if s.domain == ui.host.name:\n      let renderedDomain = ui.host.getName()\n      let naturalized = (renderedDomain, s.sign, s.referent) \n      return $naturalized\n    else:\n      return $s\n\n  proc retrieve(ui: TautoUI, proposalString: string) =\n    let proposal = ui.newProposal(proposalString)\n    let retrievedStatements = ui.host.retrieve(proposal.domain, proposal.sign)\n    for statement in retrievedStatements.items:\n      echo ui.render(statement)\n  \n  proc debug(ui: TautoUI) =\n    echo \"..\"\n    for value in ui.host.referentIndex.values:\n      echo $value\n    echo \"..\"\n\n  proc main*(ui: var TautoUI) =\n    ## The main UI. Accepts commands to propose new equivalencies and query\n    ## existing ones.\n    echo \"TAUTO\"\n    while true:\n      stdout.write(ui.prompt)\n      try:\n        let \n          input = stdin.readLine.string\n          stripped = input.strip()\n        if stripped.len > 0:\n          if stripped[0] == '/' and stripped.len > 1:\n            # Command mode\n            let \n              splitCommand = stripped[1..stripped.high].split()\n              command = splitCommand[0]\n            case command:\n              of \"ok\":\n                break\n              of \"s\":\n                let hostName = splitCommand[1]\n                var host = newHost(hostName)\n                ui.host = host\n              else: discard\n          else:\n            let \n              splitInput = input.split(maxsplit=1)\n              command = splitInput[0]\n            case command:\n              of \"!\", \"p\": \n                ui.propose(splitInput[1])\n              of \"?\", \"q\":\n                if splitInput.len == 1:\n                  ui.debug()\n                else:\n                  ui.retrieve(splitInput[1])\n              else:\n                echo \"?\"\n      except IOError:\n        echo \"ok\"\n        break\n\n  let host = newHost(\"ENG\")\n  var ui = TautoUI(host: host)\n  ui.main()\n","name":"","extension":"py","url":"https://www.irccloud.com/pastebin/nr3WBA5a","modified":1483375343,"id":"nr3WBA5a","size":6809,"lines":216,"own_paste":false,"theme":"","date":1483375343}