// Read a JKLSequence, checking that it starts and terminates correctly. // Named read_list to follow the ref, but has been genericized to handle vectors as well. static public JKLSeqBase read_list(TokenQueue TQ, JKLSeqBase sequence, char start, char end) { // Check that we are in fact at the start of a list. string token = TQ.Next(); if (token[0] != start) { // Parse error - probably internal if the list code is correct. throw new JKLInternalError("Sequence expected '" + start + "' but got: " + token); } // Use read_form to get the list's contents, accumulating them into the list. while (true) { token = TQ.Peek(); if (token != null) { // We are in the list or at the end. if (token[0] == end) { // Reached valid end of list. Consume the end char. TQ.Next(); // And we are done. break; } // Mutually recurse to read the next list element. JKLVal newVal = read_form(TQ); sequence.Add(newVal); } else { // The input has finished but the list hasn't. Try to get more input. TQ.LoadMoreTokens(start, end); } } return(sequence); }
// Support the built-in '=' function. False until explicitly proven otherwise. // Should be used in preference to == for internal JKLVal equality checks. public static JKLVal EQ(JKLVal a, JKLVal b) { if (a.GetType() != b.GetType()) { // TODO - allow equality comparisons between ints and floats. // TODO - allow equality comparisons between empty lists and vectors. return(jklFalse); } // If here, they are of the same Mal type. Do they have the same value? switch ((object)a) { case JKLSym aSym: if (aSym.getName() == ((JKLSym)b).getName()) { return(jklTrue); } break; case JKLNum aNumk: if (aNumk.Unbox() == ((JKLNum)b).Unbox()) { return(jklTrue); } break; case JKLString aString: if (aString.unbox() == ((JKLString)b).unbox()) { return(jklTrue); } break; case JKLKeyword aKeyWord: if (aKeyWord.unbox() == ((JKLKeyword)b).unbox()) { return(jklTrue); } break; case JKLNil aNil: if (aNil == ((JKLNil)b)) { return(jklTrue); } break; case JKLTrue aTrue: if (aTrue == ((JKLTrue)b)) { return(jklTrue); } break; case JKLFalse aFalse: if (aFalse == ((JKLFalse)b)) { return(jklTrue); } break; case JKLHashMap ahashMap: if (_innerHashMapEQ(ahashMap, (JKLHashMap)b)) { return(jklTrue); } else { return(jklFalse); } case JKLSeqBase aSeq: // Sequences must be the same length, and each element must be the same. JKLSeqBase bSeq = (JKLSeqBase)b; if (aSeq.Count() != bSeq.Count()) { // They are not of equal length. return(jklFalse); } for (var i = 0; i < aSeq.Count(); i++) { // Now check that the elements are equal. if (EQ(aSeq[i], bSeq[i]) == jklFalse) { // At least one of the elements is not equal. return(jklFalse); } } return(jklTrue); case JKLAtom aAtom: // The atoms must be the same object. Two atoms containing the same value are not equal. // TODO check whether this should be true if dereferencing them should be used instead. This isn't specified in the tests. if (aAtom == ((JKLAtom)b)) { return(jklTrue); } break; default: throw new JKLInternalError("Can't yet compare '" + a.GetType() + "' with '" + b.GetType() + "'"); } return(jklFalse); }