/* * File: cexpr.cpp * Purpose: Prolog-like file I/O * Author: Julian Smart * Created: 1993 * Updated: * Copyright: (c) 1993 */ static const char sccsid[] = "%W% %G%"; #include "stdafx.h" #include #include #include #include "expr.h" #include "cexpr.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif extern "C" void add_expr(char *); extern "C" void LexFromFile(FILE *fd); extern "C" void LexFromString(char *buf); CExprDatabase *theCExprDatabase = NULL; CExprErrorHandler currentCExprErrorHandler; IMPLEMENT_DYNAMIC(CExprDatabase, wxList) CExpr::CExpr(const CString& functor) { type = CExprList; next = NULL; last = NULL; value.first = NULL; CExpr *pfunctor = new CExpr(CExprWord, functor); Append(pfunctor); client_data = NULL; } CExpr::CExpr(CExprType the_type, const CString& word_or_string) { type = the_type; switch (the_type) { case CExprWord: value.word = copystring((const char *)word_or_string); break; case CExprString: value.string = copystring((const char *)word_or_string); break; case CExprList: last = NULL; value.first = NULL; break; case CExprReal: case CExprInteger: case CExprNull: break; } client_data = NULL; next = NULL; } CExpr::CExpr(CExprType the_type, char *word_or_string, BOOL allocate) { type = the_type; switch (the_type) { case CExprWord: value.word = allocate ? copystring(word_or_string) : word_or_string; break; case CExprString: value.string = allocate ? copystring(word_or_string) : word_or_string; break; case CExprList: last = NULL; value.first = NULL; break; case CExprReal: case CExprInteger: case CExprNull: break; } client_data = NULL; next = NULL; } CExpr::CExpr(long the_integer) { type = CExprInteger; value.integer = the_integer; client_data = NULL; next = NULL; } CExpr::CExpr(float the_real) { type = CExprReal; value.real = the_real; client_data = NULL; next = NULL; } CExpr::CExpr(wxList *the_list) { type = CExprList; client_data = NULL; last = NULL; value.first = NULL; CExpr *listExpr = new CExpr(CExprList); wxNode *node = the_list->First(); while (node) { CExpr *expr = (CExpr *)node->Data(); listExpr->Append(expr); node = node->Next(); } Append(listExpr); delete the_list; } CExpr::~CExpr(void) { switch (type) { case CExprInteger: case CExprReal: { break; } case CExprString: { delete[] value.string; break; } case CExprWord: { delete[] value.word; break; } case CExprList: { CExpr *expr = value.first; while (expr) { CExpr *expr1 = expr->next; delete expr; expr = expr1; } break; } case CExprNull: break; } } void CExpr::Append(CExpr *expr) { if (!value.first) value.first = expr; if (last) last->next = expr; last = expr; } void CExpr::Insert(CExpr *expr) { expr->next = value.first; value.first = expr; if (!last) last = expr; } CExpr *CExpr::Copy(void) { // This seems to get round an optimizer bug when // using Watcom C++ 10a in WIN32 compilation mode. // If these lines not present, the type seems to be // interpreted wrongly as an integer. // I don't want to turn optimization off since it's needed // for reading in files quickly. #if defined(__WATCOMC__) char buf[2]; sprintf(buf, ""); #endif switch (type) { case CExprInteger: return new CExpr(value.integer); case CExprReal: return new CExpr(value.real); case CExprString: return new CExpr(CExprString, CString(value.string)); case CExprWord: return new CExpr(CExprWord, CString(value.word)); case CExprList: { CExpr *expr = value.first; CExpr *new_list = new CExpr(CExprList); while (expr) { CExpr *expr2 = expr->Copy(); new_list->Append(expr2); expr = expr->next; } return new_list; } case CExprNull: break; } return NULL; } // Get the CExpr (containing (= CExpr Value) form) for the given word // or string, assuming that we have Attribute=Value, ... CExpr *CExpr::GetAttributeValueNode(const CString& word) // Use only for a clause or list { if (type != CExprList) return NULL; CExpr *expr = value.first; while (expr) { if (expr->type == CExprList) { CExpr *firstNode = expr->value.first; if ((firstNode->type == CExprWord) && (firstNode->value.word[0] == '=')) { CExpr *secondNode = firstNode->next; if ((secondNode->type == CExprWord) && (strcmp((const char *)word, secondNode->value.word) == 0)) { return expr; } } } expr = expr->next; } return NULL; } // Get the value (in CExpr form) for the given word or string, assuming // that we have Attribute=Value, ... CExpr *CExpr::AttributeValue(const CString& word) // Use only for a clause or list { if (type != CExprList) return NULL; CExpr *attExpr = GetAttributeValueNode(word); if (attExpr && attExpr->value.first && attExpr->value.first->next) return attExpr->value.first->next->next; else return NULL; } CString CExpr::Functor(void) // Use only for a clause { if ((type != CExprList) || !value.first) return CString(""); if (value.first->type == CExprWord) return CString(value.first->value.word); else return CString(""); } BOOL CExpr::IsFunctor(const CString& f) // Use only for a clause { if ((type != CExprList) || !value.first) return FALSE; return (value.first->type == CExprWord && (strcmp((const char *)f, value.first->value.word) == 0)); } // Return nth argument of a clause (starting from 1) CExpr *CExpr::Arg(CExprType theType, int arg) { CExpr *expr = value.first; int i; for (i = 1; i < arg; i++) if (expr) expr = expr->next; if (expr && (expr->type == theType)) return expr; else return NULL; } // Return nth argument of a list expression (starting from zero) CExpr *CExpr::Nth(int arg) { if (type != CExprList) return NULL; CExpr *expr = value.first; int i; for (i = 0; i < arg; i++) if (expr) expr = expr->next; else return NULL; if (expr) return expr; else return NULL; } // Returns the number of elements in a list expression int CExpr::Number(void) { if (type != CExprList) return 0; int i = 0; CExpr *expr = value.first; while (expr) { expr = expr->next; i ++; } return i; } void CExpr::DeleteAttributeValue(const CString& attribute) { if (type != CExprList) return; CExpr *expr = value.first; CExpr *lastExpr = this; while (expr) { if (expr->type == CExprList) { CExpr *firstNode = expr->value.first; if ((firstNode->type == CExprWord) && (firstNode->value.word[0] == '=')) { CExpr *secondNode = firstNode->next; if ((secondNode->type == CExprWord) && (strcmp((const char *)attribute, secondNode->value.word) == 0)) { CExpr *nextExpr = expr->next; delete expr; lastExpr->next = nextExpr; if (last == expr) last = lastExpr; return; } } } lastExpr = expr; expr = expr->next; } return; } void CExpr::AddAttributeValue(const CString& attribute, CExpr *val) { if (type != CExprList) { // cout << "Error! tried to add an attribute-value pair to a nonlist CExpr expression\n"; return; } // Warning - existing code may assume that any existing value // is deleted first. For efficiency, we leave this to the application. // DeleteAttributeValue(attribute); CExpr *patt = new CExpr(CExprWord, attribute); CExpr *pequals = new CExpr(CExprWord, "="); CExpr *listExpr = new CExpr(CExprList); listExpr->Append(pequals); listExpr->Append(patt); listExpr->Append(val); Append(listExpr); } void CExpr::AddAttributeValue(const CString& attribute, long val) { if (type != CExprList) { // cout << "Error! tried to add an attribute-value pair to a nonlist CExpr expression\n"; return; } // Warning - existing code may assume that any existing value // is deleted first. For efficiency, we leave this to the application. // DeleteAttributeValue(attribute); CExpr *patt = new CExpr(CExprWord, attribute); CExpr *pval = new CExpr(val); CExpr *pequals = new CExpr(CExprWord, "="); CExpr *listExpr = new CExpr(CExprList); listExpr->Append(pequals); listExpr->Append(patt); listExpr->Append(pval); Append(listExpr); } void CExpr::AddAttributeValue(const CString& attribute, float val) { if (type != CExprList) { // cout << "Error! tried to add an attribute-value pair to a nonlist CExpr expression\n"; return; } // DeleteAttributeValue(attribute); CExpr *patt = new CExpr(CExprWord, attribute); CExpr *pval = new CExpr(val); CExpr *pequals = new CExpr(CExprWord, "="); CExpr *listExpr = new CExpr(CExprList); listExpr->Append(pequals); listExpr->Append(patt); listExpr->Append(pval); Append(listExpr); } void CExpr::AddAttributeValueString(const CString& attribute, const CString& val) { if (type != CExprList) { // cout << "Error! tried to add an attribute-value pair to a nonlist CExpr expression\n"; return; } // DeleteAttributeValue(attribute); CExpr *patt = new CExpr(CExprWord, attribute); CExpr *pval = new CExpr(CExprString, val); CExpr *pequals = new CExpr(CExprWord, "="); CExpr *listExpr = new CExpr(CExprList); listExpr->Append(pequals); listExpr->Append(patt); listExpr->Append(pval); Append(listExpr); } void CExpr::AddAttributeValueWord(const CString& attribute, const CString& val) { if (type != CExprList) { // cout << "Error! tried to add an attribute-value pair to a nonlist CExpr expression\n"; return; } // DeleteAttributeValue(attribute); CExpr *patt = new CExpr(CExprWord, attribute); CExpr *pval = new CExpr(CExprWord, val); CExpr *pequals = new CExpr(CExprWord, "="); CExpr *listExpr = new CExpr(CExprList); listExpr->Append(pequals); listExpr->Append(patt); listExpr->Append(pval); Append(listExpr); } void CExpr::AddAttributeValue(const CString& attribute, wxList *val) { if (type != CExprList) { // cout << "Error! tried to add an attribute-value pair to a nonlist CExpr expression\n"; return; } if (!val) return; // DeleteAttributeValue(attribute); CExpr *patt = new CExpr(CExprWord, attribute); CExpr *pval = new CExpr(val); CExpr *pequals = new CExpr(CExprWord, "="); CExpr *listExpr = new CExpr(CExprList); listExpr->Append(pequals); listExpr->Append(patt); listExpr->Append(pval); Append(listExpr); } void CExpr::AddAttributeValueStringList(const CString& attribute, wxList *string_list) { if (type != CExprList) { // cout << "Error! tried to add an attribute-value pair to a nonlist CExpr expression\n"; return; } if (!string_list) return; // DeleteAttributeValue(attribute); // First make a list of CExpr strings CExpr *listExpr = new CExpr(CExprList); wxNode *node = string_list->First(); while (node) { char *string = (char *)node->Data(); CExpr *expr = new CExpr(CExprString, CString(string)); listExpr->Append(expr); node = node->Next(); } // Now make an (=, Att, Value) triple CExpr *patt = new CExpr(CExprWord, attribute); CExpr *pequals = new CExpr(CExprWord, "="); CExpr *listExpr2 = new CExpr(CExprList); listExpr2->Append(pequals); listExpr2->Append(patt); listExpr2->Append(listExpr); Append(listExpr2); } BOOL CExpr::GetAttributeValue(const CString& att, int& var) { CExpr *expr = AttributeValue(att); if (expr && (expr->Type() == CExprInteger || expr->Type() == CExprReal)) { var = (int)(expr->IntegerValue()); return TRUE; } else return FALSE; } BOOL CExpr::GetAttributeValue(const CString& att, long& var) { CExpr *expr = AttributeValue(att); if (expr && (expr->Type() == CExprInteger || expr->Type() == CExprReal)) { var = expr->IntegerValue(); return TRUE; } else return FALSE; } BOOL CExpr::GetAttributeValue(const CString& att, float& var) { CExpr *expr = AttributeValue(att); if (expr && (expr->Type() == CExprInteger || expr->Type() == CExprReal)) { var = expr->RealValue(); return TRUE; } else return FALSE; } BOOL CExpr::GetAttributeValue(const CString& att, CString& var) // Word OR string -> string { CExpr *expr = AttributeValue(att); if (expr && expr->Type() == CExprWord) { var = expr->WordValue(); return TRUE; } else if (expr && expr->Type() == CExprString) { var = expr->StringValue(); return TRUE; } else return FALSE; } BOOL CExpr::GetAttributeValue(const CString& att, CExpr **var) { CExpr *expr = AttributeValue(att); if (expr) { *var = expr; return TRUE; } else return FALSE; } BOOL CExpr::GetAttributeValueStringList(const CString& att, wxList *var) { CExpr *expr = AttributeValue(att); if (expr && expr->Type() == CExprList) { CExpr *string_expr = expr->value.first; while (string_expr) { if (string_expr->Type() == CExprString) var->Append((CObject *)copystring(string_expr->StringValue())); string_expr = string_expr->next; } return TRUE; } else return FALSE; } void CExpr::WriteClause(ostream& stream) // Write this expression as a top-level clause { if (type != CExprList) return; CExpr *node = value.first; if (node) { node->WriteExpr(stream); stream << "("; node = node->next; BOOL first = TRUE; while (node) { if (!first) stream << " "; node->WriteExpr(stream); node = node->next; if (node) stream << ",\n"; first = FALSE; } stream << ").\n\n"; } } void CExpr::WriteExpr(ostream& stream) // Write as any other subexpression { // This seems to get round an optimizer bug when // using Watcom C++ 10a in WIN32 compilation mode. // If these lines not present, the type seems to be // interpreted wrongly as an integer. // I don't want to turn optimization off since it's needed // for reading in files quickly. #if defined(__WATCOMC__) char buf[2]; sprintf(buf, ""); #endif switch (type) { case CExprInteger: { stream << value.integer; break; } case CExprReal: { float f = value.real; /* Now the parser can cope with this. // Prevent printing in 'e' notation. Any better way? if (fabs(f) < 0.00001) f = 0.0; */ char buf[40]; sprintf(buf, "%.6g", f); stream << buf; break; } case CExprString: { stream << "\""; int i; int len = strlen(value.string); for (i = 0; i < len; i++) { char ch = value.string[i]; if (ch == '"' || ch == '\\') stream << "\\"; stream << ch; } stream << "\""; break; } case CExprWord: { BOOL quote_it = FALSE; int len = strlen(value.word); if ((len == 0) || (len > 0 && (value.word[0] > 64 && value.word[0] < 91))) quote_it = TRUE; else { int i; for (i = 0; i < len; i++) if ((!isalpha(value.word[i])) && (!isdigit(value.word[i])) && (value.word[i] != '_')) { quote_it = TRUE; i = len; } } if (quote_it) stream << "'"; stream << value.word; if (quote_it) stream << "'"; break; } case CExprList: { if (!value.first) stream << "[]"; else { CExpr *expr = value.first; if ((expr->Type() == CExprWord) && (strcmp(expr->WordValue(), "=") == 0)) { CExpr *arg1 = expr->next; CExpr *arg2 = arg1->next; arg1->WriteExpr(stream); stream << " = "; arg2->WriteExpr(stream); } else { stream << "["; while (expr) { expr->WriteExpr(stream); expr = expr->next; if (expr) stream << ", "; } stream << "]"; } } break; } case CExprNull: break; } } void CExpr::WriteLispExpr(ostream& stream) { switch (type) { case CExprInteger: { stream << value.integer; break; } case CExprReal: { stream << value.real; break; } case CExprString: { stream << "\"" << value.string << "\""; break; } case CExprWord: { stream << value.word; break; } case CExprList: { CExpr *expr = value.first; stream << "("; while (expr) { expr->WriteLispExpr(stream); expr = expr->next; if (expr) stream << " "; } stream << ")"; break; } case CExprNull: break; } } // CExpr 'database' (list of expressions) CExprDatabase::CExprDatabase(CExprErrorHandler handler) { position = NULL; hash_table = NULL; currentCExprErrorHandler = handler; noErrors = 0; } CExprDatabase::CExprDatabase(CExprType type, const CString& attribute, int size, CExprErrorHandler handler) { position = NULL; attribute_to_hash = attribute; if (type == CExprString) hash_table = new wxHashTable(wxKEY_STRING, size); else if (type == CExprInteger) hash_table = new wxHashTable(wxKEY_INTEGER, size); else hash_table = NULL; currentCExprErrorHandler = handler; noErrors = 0; } CExprDatabase::~CExprDatabase(void) { ClearDatabase(); if (hash_table) delete hash_table; } int CExprDatabase::GetItemCount(void) // get count of possible items { int z = 0; wxNode *node = First(); while (node) { z++; node = node->Next(); } return z; } void CExprDatabase::BeginFind(void) // Initialise a search { position = First(); } CExpr *CExprDatabase::FindClause(long id) // Find a term based on an integer id attribute // e.g. node(id=23, type=rectangle, ....). { CExpr *found = NULL; while (position && !found) { CExpr *term = (CExpr *)position->Data(); if (term->Type() == CExprList) { CExpr *value = term->AttributeValue("id"); if (value->Type() == CExprInteger && value->IntegerValue() == id) found = term; } position = position->Next(); } return found; } // Find on basis of attribute/value pairs, e.g. type=rectangle CExpr *CExprDatabase::FindClause(const CString& word, const CString& val) { CExpr *found = NULL; while (position && !found) { CExpr *term = (CExpr *)position->Data(); if (term->Type() == CExprList) { CExpr *value = term->AttributeValue(word); if ((value->Type() == CExprWord && value->WordValue() == val) || (value->Type() == CExprString && value->StringValue() == val)) found = term; } position = position->Next(); } return found; } CExpr *CExprDatabase::FindClause(const CString& word, long val) { CExpr *found = NULL; while (position && !found) { CExpr *term = (CExpr *)position->Data(); if (term->Type() == CExprList) { CExpr *value = term->AttributeValue(word); if ((value->Type() == CExprInteger) && (value->IntegerValue() == val)) found = term; } position = position->Next(); } return found; } CExpr *CExprDatabase::FindClause(const CString& word, float val) { CExpr *found = NULL; while (position && !found) { CExpr *term = (CExpr *)position->Data(); if (term->Type() == CExprList) { CExpr *value = term->AttributeValue(word); if ((value->Type() == CExprReal) && (value->RealValue() == val)) found = term; } position = position->Next(); } return found; } CExpr *CExprDatabase::FindClauseByFunctor(const CString& functor) { CExpr *found = NULL; while (position && !found) { CExpr *term = (CExpr *)position->Data(); if (term->Type() == CExprList) { if (term->Functor() == functor) found = term; } position = position->Next(); } return found; } // If hashing is on, must store in hash table too void CExprDatabase::Append(CExpr *clause) { wxList::Append((CObject *)clause); if (hash_table) { CString functor(clause->Functor()); CExpr *expr = clause->AttributeValue(attribute_to_hash); if (expr) { long functor_key = hash_table->MakeKey((char *)(const char *)functor); long value_key = 0; if (expr && expr->Type() == CExprString) { value_key = hash_table->MakeKey((char *)(const char *)expr->StringValue()); hash_table->Put(functor_key + value_key, (char *)(const char *)expr->StringValue(), (CObject *)clause); } else if (expr && expr->Type() == CExprInteger) { value_key = expr->IntegerValue(); hash_table->Put(functor_key + value_key, expr->IntegerValue(), (CObject *)clause); } } } } CExpr *CExprDatabase::HashFind(const CString& functor, long value) { long key = hash_table->MakeKey((char *)(const char *)functor) + value; // The key alone isn't guaranteed to be unique: // must supply value too. Let's assume the value of the // id is going to be reasonably unique. return (CExpr *)hash_table->Get(key, value); } CExpr *CExprDatabase::HashFind(const CString& functor, const CString& value) { long key = hash_table->MakeKey((char *)(const char *)functor) + hash_table->MakeKey((char *)(const char *)value); return (CExpr *)hash_table->Get(key, (char *)(const char *)value); } void CExprDatabase::ClearDatabase(void) { noErrors = 0; wxNode *node = First(); while (node) { CExpr *expr = (CExpr *)node->Data(); delete expr; delete node; node = First(); } if (hash_table) hash_table->Clear(); } BOOL CExprDatabase::Read(const CString& filename) { noErrors = 0; FILE *f = fopen((const char *)filename, "r"); if (f) { theCExprDatabase = this; LexFromFile(f); yyparse(); fclose(f); CExprCleanUp(); return (noErrors == 0); } else { return FALSE; } } BOOL CExprDatabase::ReadFromString(const CString& buffer) { noErrors = 0; theCExprDatabase = this; LexFromString((char *)(const char *)buffer); yyparse(); CExprCleanUp(); return (noErrors == 0); } BOOL CExprDatabase::Write(const CString& fileName) { ofstream str((char *)(const char *)fileName); if (str.bad()) return FALSE; return Write(str); } BOOL CExprDatabase::Write(ostream& stream) { noErrors = 0; wxNode *node = First(); while (node) { CExpr *expr = (CExpr *)node->Data(); expr->WriteClause(stream); node = node->Next(); } return (noErrors == 0); } void CExprDatabase::WriteLisp(ostream& stream) { noErrors = 0; wxNode *node = First(); while (node) { CExpr *expr = (CExpr *)node->Data(); expr->WriteLispExpr(stream); stream << "\n\n"; node = node->Next(); } } void add_expr(CExpr * expr) { theCExprDatabase->Append(expr); } // Checks functor BOOL CExprIsFunctor(CExpr *expr, const CString& functor) { if (expr && (expr->Type() == CExprList)) { CExpr *first_expr = expr->value.first; if (first_expr && (first_expr->Type() == CExprWord) && (first_expr->WordValue() == functor)) return TRUE; else return FALSE; } else return FALSE; } /* * Called from parser * */ char *make_integer(char *str) { CExpr *x = new CExpr(atol(str)); return (char *)x; } char *make_real(char *str1, char *str2) { char buf[50]; sprintf(buf, "%s.%s", str1, str2); float f = (float)atof(buf); CExpr *x = new CExpr(f); return (char *)x; } // extern "C" double exp10(double); char *make_exp(char *str1, char *str2) { double mantissa = (double)atoi(str1); double exponent = (double)atoi(str2); double d = mantissa * pow(10.0, exponent); CExpr *x = new CExpr((float)d); return (char *)x; } char *make_exp2(char *str1, char *str2, char *str3) { char buf[50]; sprintf(buf, "%s.%s", str1, str2); double mantissa = (double)atof(buf); double exponent = (double)atoi(str3); double d = mantissa * pow(10.0, exponent); CExpr *x = new CExpr((float)d); return (char *)x; } char *make_word(char *str) { CExpr *x = new CExpr(CExprWord, str); return (char *)x; } char *make_string(char *str) { char *s, *t; int len, i; str++; /* skip leading quote */ len = strlen(str) - 1; /* ignore trailing quote */ s = new char[len + 1]; t = s; for(i=0; iInsert(car); return (char *)cdr; } void process_command(char * cexpr) { CExpr *expr = (CExpr *)cexpr; add_expr(expr); } void syntax_error(char *s) { if (currentCExprErrorHandler) (void)(*(currentCExprErrorHandler))(CEXPR_ERROR_SYNTAX, "syntax error"); if (theCExprDatabase) theCExprDatabase->noErrors += 1; } #ifdef _WINDLL char *__cdecl strdup(const char *s) { int len = strlen(s); char *new_s = (char *)malloc(sizeof(char)*(len+1)); strcpy(new_s, s); return new_s; } #endif