Raw File
pragmaticsWithSemanticParsing.wppl
var makeObj = function() {
  return {blond: flip(0.5), nice: flip(0.5), tall: flip(0.5)}
}

var worldPrior = function(nObjLeft, meaningFn, worldSoFar, prevFactor) {
  var worldSoFar = worldSoFar == undefined ? [] : worldSoFar
  var prevFactor = prevFactor == undefined ? 0 : prevFactor
  if (nObjLeft == 0) {
    factor(-prevFactor)
    return worldSoFar
  } else {
    var newObj = makeObj()
    var newWorld = worldSoFar.concat([newObj])
    var newFactor = meaningFn(newWorld) ? 0 : -100
    factor(newFactor - prevFactor)
    return worldPrior(nObjLeft - 1, meaningFn, newWorld, newFactor)
  }
}

var meaning = function(utterance) {
  return combineMeanings(filter(function(m) {return !(m.sem == undefined)},
      map(lexicalMeaning, utterance.split(' '))))
}

var lexicalMeaning = function(word) {

  var wordMeanings = {

    'blond' : {
      sem: function(world) {return function(obj) {return obj.blond}},
      syn: {dir: 'L', int: 'NP', out: 'S'} },

    'nice' : {
      sem: function(world) {return function(obj) {return obj.nice}},
      syn: {dir: 'L', int: 'NP', out: 'S'} },

    'tall' : {
      sem: function(world) {return function(obj) {return obj.tall}},
      syn: {dir: 'L', int: 'NP', out: 'S'} },

    'Bob' : {
      sem: function(world) {return find(function(obj) {return obj.name == 'Bob'}, world)},
      syn: 'NP' },

    'some' : {
      sem: function(world) {return function(P) {return function(Q) {return filter(Q, filter(P, world)).length > 0}}},
      syn: {dir: 'R',
        int: {dir: 'L', int: 'NP', out: 'S'},
        out: {dir: 'R',
          int: {dir: 'L', int: 'NP', out: 'S'},
          out: 'S'}} },

    'all' : {
      sem: function(world) {
        return function(P) {
          return function(Q) {
            return filter(neg(Q), filter(P, world)).length == 0}}},
      syn: {dir: 'R',
        int: {dir: 'L', int: 'NP', out: 'S'},
        out: {dir: 'R',
          int: {dir: 'L', int: 'NP', out: 'S'},
          out: 'S'}} },

    'none' : {
      sem: function(world) {return function(P) {return function(Q) {return filter(Q, filter(P, world)).length == 0}}},
      syn: {dir: 'R',
        int: {dir: 'L', int: 'NP', out: 'S'},
        out: {dir: 'R',
          int: {dir: 'L', int: 'NP', out: 'S'},
          out: 'S'}} }
  }

  var meaning = wordMeanings[word];
  return meaning == undefined ? {sem: undefined, syn: ''} :meaning;
}

var neg = function(Q) {
  return function(x) {return !Q(x)}
}


// Assume that both f and a will give their actual semantic value
// after being applied to a world. make a new meaning that passes on
// world arg.
var applyWorldPassing = function(f, a) {
  return function(w) {return f(w)(a(w))}
}

var combineMeaning = function(meanings) {
  var possibleComb = canApply(meanings, 0)
  var i = possibleComb[randomInteger(possibleComb.length)]
  var s = meanings[i].syn
  if (s.dir == 'L') {
    var f = meanings[i].sem
    var a = meanings[i - 1].sem
    var newmeaning = {sem: applyWorldPassing(f, a), syn: s.out}
    return meanings.slice(0, i - 1).concat([newmeaning]).concat(meanings.slice(i + 1))
  }
  if (s.dir == 'R') {
    var f = meanings[i].sem
    var a = meanings[i + 1].sem
    var newmeaning = {sem: applyWorldPassing(f, a), syn: s.out}
    return meanings.slice(0, i).concat([newmeaning]).concat(meanings.slice(i + 2))
  }
}

// Make a list of the indexes that can (syntactically) apply.
var canApply = function(meanings, i) {
  if (i == meanings.length) {
    return []
  }
  var s = meanings[i].syn
  if (s.hasOwnProperty('dir')) { //a functor
    var a = ((s.dir == 'L') ? syntaxMatch(s.int, meanings[i - 1].syn) : false) |
        ((s.dir == 'R') ? syntaxMatch(s.int, meanings[i + 1].syn) : false)
    if (a) {return [i].concat(canApply(meanings, i + 1))}
  }
  return canApply(meanings, i + 1)
}


// The syntaxMatch function is a simple recursion to
// check if two syntactic types are equal.
var syntaxMatch = function(s, t) {
  return !s.hasOwnProperty('dir') ? s == t :
      s.dir == t.dir & syntaxMatch(s.int, t.int) & syntaxMatch(s.out, t.out)
}


// Recursively do the above until only one meaning is
// left, return it's semantics.
var combineMeanings = function(meanings) {
  return meanings.length == 1 ? meanings[0].sem : combineMeanings(combineMeaning(meanings))
}


var utterancePrior = function() {
  var utterances = ['some of the blond people are nice',
                    'all of the blond people are nice',
                    'none of the blond people are nice']
  var i = randomInteger(utterances.length)
  return utterances[i]
}


var isall = function(world) {
  return world.length == 0 ? 1 : (world[0].blond ? world[0].nice : 1) & isall(world.slice(1))
}

var literalListener = cache(function(utterance) {
  Enumerate(function() {
    var m = meaning(utterance)
    var world = worldPrior(2, m)
    factor(m(world) ? 0 : -Infinity)
    return world
  }, 100)
})

var speaker = cache(function(world) {
  Enumerate(function() {
    var utterance = utterancePrior()
    var L = literalListener(utterance)
    factor(L.score([], world))
    return utterance
  }, 100)
})

var listener = function(utterance) {
  Enumerate(function() {
    var world = worldPrior(2, function(w) {return 1}) //use vacuous meaning to avoid any guide...
    //    var world = worldPrior(2, meaning(utterance)) //guide by literal meaning
    var S = speaker(world)
    factor(S.score([], utterance))
    return isall(world)
  }, 100)
}

listener('some of the blond people are nice')
back to top