Eliza in a programming language (not that one, silly)

Gods have smiled upon me once more, granting me the ability to come up with yet another unwise project idea to pour time into.

So what is Eliza? Eliza was the original LISP-based AI people got parasocial about. Many implementations exist today, but I like borrowing data form this one.

Eliza is a great example of a problem well suited for LISP. So why in the ever-lasting forever-damned blood-soaked god-forsaken Hell am I writing it in JavaScript? Well, you see, JS was originally supposed to be a Scheme, or at least take significant inspiration from Scheme. The devs at Netscape did not have enough balls to actually go this route, tho, so I guess it's left up to me then.

Yea, and you can also access it at Unit37 I guess.

Lists

So the first thing one needs to do in order to turn any language into LISP is to implement a cons cell. (right, Fennel? A cons cell.)

const nil = null

function cons(h, t) {
  return {
    "contents of the address part of register number":   h,
    "contents of the decrement part of register number": t,
  }
}

function car(cell) {
  return cell["contents of the address part of register number"]
}

function cdr(cell) {
  return cell["contents of the decrement part of register number"]
}


/// CONS is any object that has car and cdr
function consp(x) {
  return x && // not happy about looking into null
         "contents of the address part of register number"   in x &&
         "contents of the decrement part of register number" in x
}

With cons cells, you can go onto creating lists.

// somethime We need to convert from legacy datastructures
function arr2list(argv) {
  let res = nil
  argv.forEach((s) => {
    res = cons(s, res)
  })
  return lrev(res)
}


// just create list from args
const l = list
function list(...argv) {
  return arr2list(argv)
}

And just like this, we have a LISP! Sure, it's a bit rough and the syntax is more of a M-expression based, rahter than the usual S-expressions we all know and love. (RIGHT?) But it should be enough.

HTML

Now it's time for my favorite little trick: HTML links can invoke JS, which includes '<form>' submit.

<form action="javascript:evalForm()">
  <input type="submit" value="$>">
  <input type="text" id="inp">
  <div id="text-area">
    <p class="de-a">
      Đe-Alchmst | Welcome to my awesome
      <a href="https://en.wikipedia.org/wiki/ELIZA">Eliza</a> clone written
      in most normal JavaScript.
    </p>
  </div>
</form>

To put new element at the top of '<div>', you can just use '.prepend(x)', which is very helpful and I wish to finally remember this for once.

DOCTOR

So, first we need to tokenize the user input. JavaScript does not support the 'SYMBOL' type, but strings will do for now.

function EEval(input) {
  return resolve(tokenize(input.toLowerCase().replace(/[,.~!;?]/, '')))
}

// string to list of tokens
const t = tokenize
function tokenize(str) {
  return arr2list(str.trim().split(/[\s]+/).filter(x => x != ""))
}


function resolve(tok) { // it's like DNS, but worse (so NIS?)
  let matched = matchWithRules(tok)
  return lstrJoin(" ", matched)
}

'resolve' is used to match the input against a list of 'RULES' which contain a pattern and a list of responses, from which is one randomly chosen.

I'm not gonna spoil the entire codebase for you, but I will share this pice of code, which transforms direction of speech:

// turn stuff back at the user
function mirror(str) {
  let aux = (lst) => {
    if (lst == nil)       return str
    if (str == caar(lst)) return cdar(lst)
    else                  return aux(cdr(lst))
  }
  return aux(list(
    cons("i"   , "you"),
    cons("you" , "I"),
    cons("me"  , "you"),
    cons("am"  , "are"),
    cons("I'm" , "you are")
  ))
}

I like 'aux'. 'if' with 'return' can be written somewhat like 'cond', so that's nice.

Now go and play with the source code, I dare you!