mrscheme
Version:
Scheme didactic subset interpreter in JavaScript.
850 lines (678 loc) • 28.5 kB
JavaScript
import { SchemeNumber } from "./schemeNumber101.mjs";
import { M$ } from './message101.mjs';
import { TypeUnit, TypeInt, TypeString, TypeVector, TypeList, TypeVariable,
TypeOption, makeTypeBool, TypeFun } from "./type101.mjs";
import { BoolValue, VectorValue, StringValue, NilValue, PairValue,
IntegerValue }
from './values.mjs';
import { PrimitiveError } from './primerror.mjs';
/* primitives functions */
function PrimsEnv() {
this.env = new Array();
this.register = function(name,prim) {
if(this.env["___"+name]!=null) {
throw "Primitive '" + name +"' already defined";
}
this.env["___"+name] = prim;
prim.type = "primitive";
prim.call = function(evaluator,lexenv,expr,args) { return checkAndCall(prim,evaluator,lexenv,expr,args); }
prim.callable = true;
prim.name = name;
prim.toString = function() {
return name+":"+prim.typeRepr.toString();
}
prim.toHTML = function() {
return '<span class="value">primitive:'+name+'<span class="tooltip">type <strong>'+prim.typeRepr.show()+'</strong></span></span>';
}
}
this.fetch = function(name) {
return this.env["___"+name];
}
this.lookupType = function(name,mvars) {
var prim = this.env["___"+name];
if(prim==null) {
return null; // not found
}
var ntypeRepr = prim.typeRepr.updateVars(mvars,{});
return ntypeRepr;
}
}
export function defaultPrimsEnv() {
var penv = new PrimsEnv();
// remark: numerical primitives are in numericaltower101.js
// misc.
penv.register("boolean?", new primitiveTypePredicate("boolean?","bool"));
penv.register("string?", new primitiveTypePredicate("string?","string"));
penv.register("symbol?", new primitiveTypePredicate("symbol?","symbol"));
penv.register("equal?",new primitiveEqual());
penv.register("not", new primitiveNot());
penv.register(M$("error").toString(), new primitiveError());
// strings
penv.register("string-length", new primitiveStringLength());
penv.register("string-append", new primitiveStringAppend());
penv.register("substring", new primitiveSubString());
penv.register("string>?", new primitiveCompareString());
// cons cells
penv.register("cons", new primitiveCons());
penv.register("list", new primitiveList());
penv.register("pair?", new primitivePairP());
penv.register("empty?", new primitiveNullP());//JPRoy
penv.register("list?", new primitiveListP());
penv.register("car", new primitiveCar());
penv.register("first", new primitiveCar());//JPRoy
penv.register("cdr", new primitiveCdr());
penv.register("rest", new primitiveCdr());//JPRoy
penv.register("caar", new primitiveListAccess(new Array("a","a")));
penv.register("cadr", new primitiveListAccess(new Array("a","d")));
penv.register("second", new primitiveListAccess(new Array("a","d")));//JPRoy
penv.register("cdar", new primitiveListAccess(new Array("d","a")));
penv.register("cddr", new primitiveListAccess(new Array("d","d")));
penv.register("caaar", new primitiveListAccess(new Array("a","a","a")));
penv.register("cdaar", new primitiveListAccess(new Array("d","a","a")));
penv.register("cadar", new primitiveListAccess(new Array("a","d","a")));
penv.register("caadr", new primitiveListAccess(new Array("a","a","d")));
penv.register("caddr", new primitiveListAccess(new Array("a","d","d")));
penv.register("third", new primitiveListAccess(new Array("a","d","d")));//JPRoy
penv.register("cdadr", new primitiveListAccess(new Array("d","a","d")));
penv.register("cddar", new primitiveListAccess(new Array("d","d","a")));
penv.register("cdddr", new primitiveListAccess(new Array("d","d","d")));
penv.register("caaaar", new primitiveListAccess(new Array("a","a","a","a")));
penv.register("caaadr", new primitiveListAccess(new Array("a","a","a","d")));
penv.register("caadar", new primitiveListAccess(new Array("a","a","d","a")));
penv.register("cadaar", new primitiveListAccess(new Array("a","d","a","a")));
penv.register("cdaaar", new primitiveListAccess(new Array("d","a","a","a")));
penv.register("caaddr", new primitiveListAccess(new Array("a","a","d","d")));
penv.register("caddar", new primitiveListAccess(new Array("a","d","d","a")));
penv.register("cdaadr", new primitiveListAccess(new Array("d","a","a","d")));
penv.register("cddaar", new primitiveListAccess(new Array("d","d","a","a")));
penv.register("cadddr", new primitiveListAccess(new Array("a","d","d","d")));
penv.register("fourth", new primitiveListAccess(new Array("a","d","d","d")));//JPRoy
penv.register("cdaddr", new primitiveListAccess(new Array("d","a","d","d")));
penv.register("cddadr", new primitiveListAccess(new Array("d","d","a","d")));
penv.register("cdddar", new primitiveListAccess(new Array("d","d","d","a")));
penv.register("cddddr", new primitiveListAccess(new Array("d","d","d","d")));
penv.register("append", new primitiveAppend());
penv.register("map", new primitiveMap());
penv.register("filter", new primitiveFilter());
penv.register("filtre", new primitiveFilter());
penv.register("reduce", new primitiveReduce());
penv.register("assoc", new primitiveAssoc());
penv.register("make-vector", new primitiveVectorMake());
penv.register("vector-ref", new primitiveVectorRef());
penv.register("vector-set!", new primitiveVectorSet());
penv.register("vector-length", new primitiveVectorLength());
penv.register("vector?", new primitiveTypePredicate("vector?","vector"));
return penv;
}
function checkArity(expr,name,arity,nary,args) {
if(args.length<arity) {
return new PrimitiveError(expr,name,args,M$("Not enough arguments: expected $0 given $1",new Array(arity,args.length)));
}
if(nary) {
return { type: "ok" };
}
if(args.length>arity) {
return new PrimitiveError(expr,name,args,M$("Too many arguments: expected $0 given $1", new Array(arity,args.length)));
}
return { type: "ok" };
}
function checkAndCall(prim,evaluator,lexenv,expr,args) {
var check = checkArity(expr,prim.name,prim.arity,prim.nary,args);
if(check.type=="evalError") {
return check;
}
return prim.exec(evaluator,lexenv,expr,args)
}
/*********** MISC. **********/
export function primitiveTypePredicate(name,type) {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0)),makeTypeBool());
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
var arg = args[0];
if(arg.type==type) {
return new BoolValue(true);
} else {
return new BoolValue(false);
}
}
}
function primitiveEqual() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0), new TypeVariable(1)),makeTypeBool());
this.arity = 2;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
var left = args[0];
var right = args[1];
if(left.type=="function" || right.type=="function") {
return new PrimitiveError(expr,"equal?",new Array(left,right),M$("No equality for functions"));
} else if(left.type=="primitive" || right.type=="primitive") {
return new PrimitiveError(expr,"equal?",new Array(left,right),M$("No equality for primitives"));
}
return new BoolValue(left.equal(right));
}
}
function primitiveNot() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0)),makeTypeBool());
this.arity = 1;
this.nary = 0;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type=='bool' && !args[0].value) {
return new BoolValue(true);
}
return new BoolValue(false);
}
}
function primitiveError() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0)),new TypeUnit(), true); // n-ary
this.arity = 1;
this.nary = true;
this.exec = function(evaluator,lexenv,expr,args) {
var str = "";
for(var i=0;i<args.length;i++) {
if(args[i].type=="string") {
str += args[i].value;
} else {
str += args[i].toString();
}
if(i<args.length-1) {
str += " ";
}
}
var err = new PrimitiveError(expr,M$("error").toString(),args,str);
err.toHTML = function() {
return '<span class="error"><strong>'+M$("Error").toString()+'</strong>: '+this.message+'</span>';
}
return err;
}
}
/********** Vectors *************/
function primitiveVectorMake() {
this.typeRepr = new TypeFun(new Array(new TypeInt()), new TypeVector());
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="int") {
return new PrimitiveError(expr.get(1),"make-vector",args,M$("Expecting an int"));
}
var size = args[0].value;
if (size < 0 ) {
return new PrimitiveError(expr.get(1),"make-vector",args,M$("Expecting a natural number"));
}
var array = new Array(size);
for (var i=0; i<size; i++ ) {
array[i] = new BoolValue(false);
}
return new VectorValue(array);
}
}
function primitiveVectorRef() {
this.typeRepr = new TypeFun(new Array(new TypeVector(), new TypeInt()), new TypeVariable(0));
this.arity = 2;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="vector") {
return new PrimitiveError(expr.get(1),"vector-ref",args,M$("Expecting a vector"));
}
if(args[1].type!="int") {
return new PrimitiveError(expr.get(2),"vector-ref",args,M$("Expecting a natural number"));
}
var array = args[0].value;
var index = args[1].value;
if ( 0 <= index && index < array.length ) {
return array[index];
} else {
return new PrimitiveError(expr,"vector-ref",args,M$("Index out of bounds"));
}
}
}
function primitiveVectorSet() {
this.typeRepr = new TypeFun(new Array(new TypeVector(), new TypeInt(), new TypeVariable(0)), new TypeVariable(1));
this.arity = 3;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="vector") {
return new PrimitiveError(expr.get(1),"vector-set!",args,M$("Expecting a vector"));
}
if(args[1].type!="int") {
return new PrimitiveError(expr.get(2),"vector-set!",args,M$("Expecting a natural number"));
}
var array = args[0].value;
var index = args[1].value;
if ( 0 <= index && index < array.length ) {
var value = args[2];
var oldvalue = array[index];
array[index] = value;
return oldvalue;
} else {
return new PrimitiveError(expr,"vector-set!",args,M$("Index out of bounds"));
}
}
}
function primitiveVectorLength() {
this.typeRepr = new TypeFun(new Array(new TypeVector()), new TypeInt());
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="vector") {
return new PrimitiveError(expr.get(1),"vector-set!",args,M$("Expecting a vector"));
}
return new IntegerValue(SchemeNumber.fn["string->number"](""+args[0].value.length));
}
}
/********** STRINGS *************/
function primitiveStringLength() {
this.typeRepr = new TypeFun(new Array(new TypeString()), new TypeInt());
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="string") {
return new PrimitiveError(expr.get(1),"string-length",args,M$("Expecting a string"));
}
return new IntegerValue(SchemeNumber.fn["string->number"](""+args[0].value.length));
}
}
function primitiveStringAppend() {
this.typeRepr = new TypeFun(new Array(new TypeString(),new TypeString()), new TypeString());
this.arity = 2;
this.nary = true;
this.exec = function(evaluator,lexenv,expr,args) {
var str = "";
for(var i=0;i<args.length;i++) {
if(args[i].type!="string") {
return new PrimitiveError(expr.get(i+1),"string-append",args,M$("Expecting a string"));
}
str += args[i].value
}
return new StringValue(str);
}
}
function primitiveCompareString() {
// C'est string>? (qui passe mieux en html que string<?)
this.typeRepr = new TypeFun(new Array(new TypeString(),new TypeString()), makeTypeBool());
this.arity = 2;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="string") {
return new PrimitiveError(expr.get(1),"string>?",args,M$("Expecting a string"));
}
if(args[1].type!="string") {
return new PrimitiveError(expr.get(1),"string>?",args,M$("Expecting a string"));
}
if ( args[0].value > args[1].value ) {
return new BoolValue(true);
} else {
return new BoolValue(false);
}
}
}
function primitiveSubString() {
this.typeRepr = new TypeFun(new Array(new TypeString(),new TypeInt(), new TypeInt()), new TypeString());
this.arity = 3;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="string") {
return new PrimitiveError(expr.get(1),"substring",args,M$("Expecting a string"));
}
if(args[1].type!="int") {
return new PrimitiveError(expr.get(2),"substring",args,M$("Expecting an integer"));
}
if(args[2].type!="int") {
return new PrimitiveError(expr.get(3),"substring",args,M$("Expecting an integer"));
}
var str = args[0].value;
var start = args[1].value;
var end = args[2].value;
if(start<0) {
return new PrimitiveError(expr.get(2),"substring",args,M$("The start index must be positive"));
}
if(start>str.length) {
return new PrimitiveError(expr.get(2),"substring",args,M$("The start index is outside the string"));
}
if(end<start) {
return new PrimitiveError(expr.get(3),"substring",args,M$("The end index must be after start"));
}
if(end>str.length) {
return new PrimitiveError(expr.get(3),"substring",args,M$("The end index is outside the string"));
}
return new StringValue(str.substring(start,end));
}
}
/********** CONS CELLS ***********/
function primitiveCons() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0), new TypeList(new TypeVariable(0))),new TypeList(new TypeVariable(0)));
this.arity = 2;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
return new PairValue(args[0],args[1]);
}
}
function primitiveList() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0)), new TypeList(new TypeVariable(0)), true);
this.toString = function() {
return "list:'a*...->LIST['a]";
}
this.arity = 0;
this.nary = true;
this.exec = function(evaluator,lexenv,expr,args) {
var last = new NilValue();
for(var i=args.length-1;i>=0;i--) {
var next = new PairValue(args[i],last);
last = next;
}
return last;
}
}
function primitivePairP() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0)), makeTypeBool());
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type=="pair") {
return new BoolValue(true);
}
return new BoolValue(false);
}
}
function primitiveNullP() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0)), makeTypeBool());
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
var nil = new NilValue();
if(args[0].equal(nil)) {
return new BoolValue(true);
}
return new BoolValue(false);
}
}
function primitiveListP() {
this.toString = function() {
return "list?:'a->bool";
}
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type=="pair" || args[0].type=="nil") {
return new BoolValue(true);
}
return new BoolValue(false);
}
}
function primitiveCar() {
this.typeRepr = new TypeFun(new Array(new TypeList(new TypeVariable(0))), new TypeVariable(0));
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="pair") {
return new PrimitiveError(expr.get(1), "car", args,M$("Not a pair"));
}
return args[0].car;
}
}
function primitiveCdr() {
this.typeRepr = new TypeFun(new Array(new TypeList(new TypeVariable(0))), new TypeList(new TypeVariable(0)));
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="pair") {
return new PrimitiveError(expr.get(1), "cdr", args,M$("Not a pair"));
}
return args[0].cdr;
}
}
function primitiveListAccess(dirs) {
if(dirs[0]=="a") {
this.typeRepr = new TypeFun(new Array(new TypeList(new TypeVariable(0))), new TypeVariable(0));
} else {
this.typeRepr = new TypeFun(new Array(new TypeList(new TypeVariable(0))), new TypeList(new TypeVariable(0)));
}
this.arity = 1;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
var arg = args[0];
if(arg.type!="pair") {
return new PrimitiveError(expr.get(1), this.name, args,M$("Not a pair"));
}
var tempName = "";
for(var i = dirs.length-1;i>=0;i--) {
if(arg.type!="pair") {
return new PrimitiveError(expr.get(1), this.name, args,M$("$0 is not a pair","c"+tempName+"r"));
}
var dir = dirs[i];
if(dir=="a") {
arg = arg.car;
} else {
arg = arg.cdr;
}
tempName = dir + tempName;
}
return arg;
}
}
function primitiveAppend() {
this.typeRepr = new TypeFun(new Array(new TypeList(new TypeVariable(0)),new TypeList(new TypeVariable(0))), new TypeList(new TypeVariable(0)), true);
this.arity = 2;
this.nary = true;
this.lastNode = null;
this.copyList = function(expr,nb,args,head) {
if(head.type=="nil") {
return new NilValue();
} else if(head.type=="pair") {
var node = new PairValue(head.car,null);
this.lastNode = node;
var rest = this.copyList(expr,nb,args,head.cdr);
node.cdr = rest;
return node;
} else {
return new PrimitiveError(expr.get(nb),"append",args,M$("Expecting a proper list"));
}
}
this.exec = function(evaluator,lexenv,expr,args) {
var head = null;
var last = null;
this.lastNode = null;
for(var i=0;i<args.length-1;i++) {
var arg = args[i];
if(arg.type=="pair") {
var copy = this.copyList(expr,i+1,args,arg);
if(copy.type=="error") {
return copy;
}
if(head==null) {
head = copy;
}
if(last!=null) {
last.cdr = copy;
}
last = this.lastNode;
} else if(arg.type=="nil") {
// do nothing
} else {
return new PrimitiveError(expr.get(i+1),"append",args,M$("Expecting a list"));
}
}
var lastArg = args[args.length-1];
if(lastArg.type=="pair" || lastArg.type=="nil") {
if(head!=null) {
last.cdr = lastArg;
return head;
} else {
return lastArg;
}
} else {
return new PrimitiveError(expr.get(args.length),"append",args,M$("Expecting a list"));
}
};
}
function primitiveMap() {
this.typeRepr = new TypeFun(new Array(new TypeFun(new Array(new TypeVariable(0)),new TypeVariable(1)), new TypeList(new TypeVariable(0))), new TypeList(new TypeVariable(1)));
this.arity = 2;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="function" && args[0].type!="primitive") {
return new PrimitiveError(expr.get(1), "map", args,M$("A function or primitive is expected"));
}
if(args[0].arity!=1) {
if(args[0].nary) {
return new PrimitiveError(expr.get(1), "map", args,M$("The function or primitive should be unary but its arity is (at least) $0",""+args[0].arity));
} else {
return new PrimitiveError(expr.get(1), "map", args,M$("The function or primitive should be unary but its arity is $0",""+args[0].arity));
}
}
// now the map algorithm
var pair = args[1];
var mpair = null;
var prevPair = null;
var firstPair = null;
do {
if(pair.type=="pair") {
mpair = new PairValue(null,null);
// the new car
var mcar = args[0].call(evaluator,lexenv,expr.get(1),new Array(pair.car));
if(mcar.type=="error") {
return mcar;
}
mpair.car = mcar;
pair = pair.cdr; // advance
} else if(pair.type=="nil") {
mpair = new NilValue();
} else { // neither a pair nor a nil
return new PrimitiveError(expr.get(2), "map", args,M$("Expecting a proper list"));
}
if(firstPair==null) {
firstPair = mpair;
}
if(prevPair!=null) {
prevPair.cdr = mpair;
}
prevPair = mpair;
} while(mpair.type!="nil");
return firstPair;
};
}
function primitiveFilter() {
this.typeRepr = new TypeFun(new Array(new TypeFun(new Array(new TypeVariable(0)),makeTypeBool()), new TypeList(new TypeVariable(1))), new TypeList(new TypeVariable(1)));
this.arity = 2;
this.nary = false;
this.exec = function(evaluator,lexenv,expr,args) {
if(args[0].type!="function" && args[0].type!="primitive") {
return new PrimitiveError(expr.get(1), "filter", args,M$("A functional or primitive predicate is expected"));
}
if(args[0].arity!=1) {
if(args[0].nary) {
return new PrimitiveError(expr.get(1), "filter", args,M$("The function or primitive should by unary but its arity is (at least) $0",""+args[0].arity));
} else {
return new PrimitiveError(expr.get(1), "filter", args,M$("The function or primitive should by unary but its arity is $0",""+args[0].arity));
}
}
// now the filter algorithm
var pair = args[1];
var prevPair = null;
var firstPair = null;
do {
var mpair = null;
if(pair.type=="pair") {
mpair = new PairValue(null,null);
// the test
var test = args[0].call(evaluator,lexenv,expr.get(1),new Array(pair.car));
if(test=="error") {
return test;
} else if(test.type!="bool") {
return new PrimitiveError(expr.get(1),"filter",args,M$("The filter predicate should return a boolean, not a $0",M$(test.type).toString()));
} else if(test.value==true) {
mpair.car = pair.car;
pair = pair.cdr;
} else { // filtered
pair = pair.cdr;
continue; // skip the update
}
} else if(pair.type=="nil") {
mpair = new NilValue();
} else { // neither a pair nor a nil
return new PrimitiveError(expr.get(2), "filter", args,M$("Expecting a proper list"));
}
if(firstPair==null) {
firstPair = mpair;
}
if(prevPair!=null) {
prevPair.cdr = mpair;
}
prevPair = mpair;
} while(mpair.type!="nil");
return firstPair;
};
}
function primitiveReduce() {
this.typeRepr = new TypeFun(new Array(new TypeFun(new Array(new TypeVariable(0),new TypeVariable(1)), new TypeVariable(1)),
new TypeVariable(1),
new TypeList(new TypeVariable(0))),
new TypeVariable(1));
this.exec = function(evaluator,lexenv,expr,args) {
// check the function
if(args[0].type!="function" && args[0].type!="primitive") {
return new PrimitiveError(expr.get(1), "reduce", args,M$("A function or primitive is expected"));
}
if(args[0].arity!=2) {
if(args[0].nary) {
if(args[0].arity>2) { // XXX: no error is arity <= 2 (because the function is nary)
return new PrimitiveError(expr.get(1), "reduce", args,M$("The function or primitive should be binary but its arity is (at least) $0",""+args[0].arity));
}
} else {
return new PrimitiveError(expr.get(1), "reduce", args,M$("The function or primitive should be binary but its arity is $0",""+args[0].arity));
}
}
// go the the last pair and store the values
var values = new Array();
var pair = args[2];
var last = pair;
do {
last = pair;
if(pair.type=="pair") {
values.push(pair.car);
pair = pair.cdr;
} else if(pair.type!="nil") {
return new PrimitiveError(expr.get(2),"reduce",args,M$("Expecting a proper list"));
}
} while(last.type!="nil");
// now apply the reduction
var acc = args[1];
for(var i=values.length-1;i>=0;i--) {
acc = args[0].call(evaluator,lexenv,expr.get(1),new Array(values[i],acc));
if(acc.type=="error") {
return acc;
}
}
return acc;
}
}
function primitiveAssoc() {
this.typeRepr = new TypeFun(new Array(new TypeVariable(0),new TypeList(new TypeList(new TypeVariable(1)))), new TypeOption(new TypeVariable(1))); // XXX: need a TUPLE type !
this.exec = function(evaluator,lexenv,expr,args) {
if(args[1].type!="pair" && args[1].type!="nil") {
return new PrimitiveError(expr.get(2), "assoc", args,M$("Expecting a list"));
}
if(args[1].type=="nil") {
return new BoolValue(false);
}
var pair = args[1];
while(pair.type!="nil") {
var binding = pair.car;
if(pair.car.type!="pair") {
return new PrimitiveError(expr.get(2), "assoc", args,M$("Expecting an association list"));
}
if(args[0].equal==null) {
return new PrimitiveError(expr.get(1), "assoc", args,M$("Equality not supported for type: '$0'",args[0].type));
}
if(pair.car.car.equal==null) {
return new PrimitiveError(expr.get(2), "assoc", args,M$("Equality not supported for type: '$0'",pair.car.car.type));
}
var eq = args[0].equal(pair.car.car);
if(eq) {
return pair.car;
}
pair = pair.cdr;
}
return new BoolValue(false);
}
}