#ifndef __REPL_REPLENVIRONMENT_H #define __REPL_REPLENVIRONMENT_H #include #include "Evaluators/FFI" #include "Evaluators/Builtins" #include "Evaluators/Evaluators" #include "FFIs/FFIs" #include "AST/AST" #include "AST/Keyword" #include "FFIs/UI" #include "FFIs/RecordPacker" #include "Evaluators/ModuleLoader" #include "Evaluators/BuiltinSelector" #include "FFIs/ProcessInfos" #include "REPL/ExtREPL" namespace GUI { }; namespace REPLX { struct REPL; typedef AST::Node EnvEntry; /* actually Application, but we don't want the client to assume silly things */ using namespace GUI; /* [ ->!] [ ->!] [ ->!] ^ ^ ^ fTailEnvironment fTailUserEnvironment fTailUserEnvironmentFrontier entry = ((\x B) a) ((\+ (\* B (M +)) (\x nil) is used as a tail marker for where the user code should go. ) P) env-entry = application(abstraction(\+, xxx), P) xxx = application(abstraction(...), ...) or nil or junk there is a dummy entry at the beginnning, called fTailEnvironment. fTailUserEnvironment is the last entry before the user environment, it can be used to smuggle in new builtins. fTailUserEnvironmentFrontier is the last entry of the user environment. Its tail will be the code to execute. */ static bool envEntry_P(AST::NodeT node) { return(application_P(node) && abstraction_P(get_application_operator(node))); } static void envEntrySetTail(AST::NodeT node, AST::NodeT value) { // continue at the abstraction body embedded in the application (all other slots are used up) assert(envEntry_P(node)); AST::NodeT op = get_application_operator(node); assert(abstraction_P(op)); ((AST::Abstraction*)op)->body = value; // shoot me now } static AST::NodeT envEntryGetTail(AST::NodeT node) { // continue at the abstraction body embedded in the application (all other slots are used up) assert(envEntry_P(node)); AST::NodeT op = get_application_operator(node); assert(abstraction_P(op)); return(get_abstraction_body(op)); } static AST::NodeT makeEnvEntry(AST::NodeT /* symbol */ name, AST::NodeT body, AST::NodeT next) { return(makeApplication(makeAbstraction(name, next), body)); } static void getEnvEntry(AST::NodeT entry, AST::NodeT& name, AST::NodeT& body, AST::NodeT& next) { assert(application_P(entry)); AST::NodeT abstraction = get_application_operator(entry); assert(abstraction_P(abstraction)); name = get_abstraction_parameter(abstraction); next = get_abstraction_body(abstraction); body = get_application_operand(entry); } AST::NodeT REPL_get_definition_backwards(struct REPL* self, AST::NodeT /*Symbol*/ name, size_t backOffset) { // envEntrySetTail(self->fTailUserEnvironmentFrontier, NULL); AST::NodeT x_name; AST::NodeT x_body; AST::NodeT x_next; std::deque matches; for(AST::NodeT entry = envEntryGetTail(self->fTailEnvironment); getEnvEntry(entry, x_name, x_body, x_next), true; entry = x_next) { if(x_name == name) { matches.push_front(x_body); } if(entry == self->fTailUserEnvironmentFrontier) break; } if(backOffset >= 0 && backOffset < matches.size()) return(matches[backOffset]); else return(NULL); } AST::NodeT REPL_get_definition(struct REPL* self, size_t offset) { // envEntrySetTail(self->fTailUserEnvironmentFrontier, NULL); AST::NodeT x_name; AST::NodeT x_body; AST::NodeT x_next; std::deque matches; for(AST::NodeT entry = envEntryGetTail(self->fTailEnvironment); getEnvEntry(entry, x_name, x_body, x_next), true; entry = x_next, --offset) { if(offset == 0) return(x_body); if(entry == self->fTailUserEnvironmentFrontier) break; } return(NULL); } bool exit_P(const char* text) { return(strncmp(text, "#exit", strlen("#exit")) == 0); } AST::NodeT REPL_prepare(struct REPL* self, AST::NodeT input); Scanners::OperatorPrecedenceList* REPL_ensure_operator_precedence_list(struct REPL* self) { Scanners::OperatorPrecedenceList* result; result = new Scanners::OperatorPrecedenceList; // FIXME add the operators, reading the environment return(result); } template AST::NodeT REPL_parse(struct REPL* self, const char* command, int commandLen, T destination/*for errors*/) { // TODO just call the builtin! RStrMathParser /* is not allowed to both print stuff AND return non-null, except when it updates the destination iter */ Scanners::MathParser parser; FILE* input_file = fmemopen((void*) command, commandLen, "r"); if(input_file) { try { parser.push(input_file, 0, ""); AST::NodeT result = NULL; result = parser.parse(REPL_ensure_operator_precedence_list(self), Symbols::SlessEOFgreater); fclose(input_file); return(result); } catch(...) { fclose(input_file); throw; } } // REMOVE REPL_queue_scroll_down(self); return(NULL); } template bool REPL_execute(struct REPL* self, AST::NodeT input, T destination) { bool B_ok = false; try { AST::NodeT result = input; Evaluators::resetWorld(); result = REPL_prepare(self, result); /*std::string v = result ? Evaluators::str(result) : "OK"; v = " => " + v + "\n"; REPL_insert_into_output_buffer(self, destination, v.c_str());*/ result = Evaluators::reduce(result); REPL_enqueue_LATEX(self, result, destination); B_ok = true; } catch(Evaluators::EvaluationException e) { std::string v = e.what() ? e.what() : "error"; v = " => " + v + "\n"; REPL_insert_into_output_buffer(self, destination, v.c_str()); } REPL_set_file_modified(self, true); return(B_ok); } #define DECLARE_REPL_OPERATION(P) \ DECLARE_FULL_OPERATION(P) #define DEFINE_REPL_OPERATION(P, B) \ DEFINE_FULL_OPERATION(P, B) static AST::NodeT REPL_import(AST::NodeT options, AST::NodeT argument); DEFINE_REPL_OPERATION(RImporter, { return(REPL_import(fn, argument)); }) static AST::HashTable setFromList(AST::NodeT listNode) { AST::HashTable result; for(AST::Cons* node = Evaluators::evaluateToCons(listNode); node; node = Evaluators::evaluateToCons(node->tail)) { const char* name = AST::get_symbol1_name(AST::get_cons_head(node)); // TODO error handling if(name) result[GCx_strdup(name)] = NULL; } return(result); } static AST::NodeT REPL_import(AST::NodeT options, AST::NodeT argument) { Evaluators::CXXArguments arguments = Evaluators::CXXfromArguments(options, argument); Evaluators::CXXArguments::const_iterator iter = arguments.begin(); struct REPL* self = (struct REPL*)(Evaluators::get_pointer(iter->second)); assert(self); ++iter; assert(iter != arguments.end()); AST::NodeT filename = iter->second; if(!str_P(filename)) throw Evaluators::EvaluationException("import: expected Str filename"); AST::NodeT body = Evaluators::import_module(options/*FIXME*/, filename); //body = Evaluators::annotate(body); //REPL_prepare(self, body); //REPL_add_to_environment(self, name, body); // TODO make it possible to limit imports (use whitelist and blacklist) AST::NodeT whitelistN = Evaluators::CXXgetKeywordArgumentValue(arguments, AST::keywordFromStr("whitelist:")); AST::HashTable whitelist = setFromList(whitelistN); AST::NodeT blacklistN = Evaluators::CXXgetKeywordArgumentValue(arguments, AST::keywordFromStr("blacklist:")); AST::HashTable blacklist = setFromList(blacklistN); AST::NodeT prefixN = Evaluators::CXXgetKeywordArgumentValue(arguments, AST::keywordFromStr("prefix:")); char* prefix = prefixN ? Evaluators::get_string(prefixN) : NULL; AST::NodeT exports = Evaluators::reduce(AST::makeApplication(body, Symbols::Sexports)); AST::NodeT usedExports = NULL; blacklist["exports"] = NULL; // Sexports for(AST::Cons* node = Evaluators::evaluateToCons(exports); node; node = Evaluators::evaluateToCons(node->tail)) { AST::NodeT key = get_cons_head(node); const char* xname = AST::get_symbol1_name(key); if(xname && (whitelist.empty() || whitelist.find(xname) != whitelist.end()) && blacklist.find(xname) == blacklist.end()) { AST::NodeT xbody = AST::makeApplication(body, Evaluators::quote(key)); //REPL_prepare(self, xbody); if(prefix && prefix[0]) { std::stringstream sst; sst << prefix << xname; std::string v = sst.str(); key = AST::symbolFromStr(v.c_str()); } REPL_add_to_environment(self, key, xbody); usedExports = AST::makeCons(key, usedExports); } } return(usedExports); } static AST::NodeT REPL_define(AST::NodeT options, AST::NodeT argument) { Evaluators::CXXArguments arguments = Evaluators::CXXfromArguments(options, argument); Evaluators::CXXArguments::const_iterator iter = arguments.begin(); assert(iter != arguments.end()); struct REPL* self = (struct REPL*)(Evaluators::get_pointer(iter->second)); assert(self); ++iter; assert(iter != arguments.end()); AST::NodeT name = iter->second; ++iter; assert(iter != arguments.end()); AST::NodeT body = iter->second; /* make sure it would actually work... (otherwise would throw exception) */ REPL_prepare(self, body); REPL_add_to_environment(self, name, body); return(body); } static AST::NodeT REPL_describe(AST::NodeT options, AST::NodeT argument) { Evaluators::CXXArguments arguments = Evaluators::CXXfromArguments(options, argument); struct REPL* self = (struct REPL*)(Evaluators::get_pointer(arguments.front().second)); assert(self); Evaluators::CXXArguments::const_iterator iter = arguments.begin(); assert(iter != arguments.end()); ++iter; assert(iter != arguments.end()); argument = iter->second; AST::NodeT backOffsetNode = Evaluators::CXXgetKeywordArgumentValue(arguments, AST::keywordFromStr("@backOffset:")); int backOffset = backOffsetNode ? Evaluators::get_int(backOffsetNode) : 0; if(AST::get_symbol1_name(argument) != NULL) { AST::NodeT body = REPL_get_definition_backwards(self, argument, backOffset); return(body); } else return(argument); } static AST::NodeT REPL_purge(AST::NodeT options, AST::NodeT argument) { Evaluators::CXXArguments arguments = Evaluators::CXXfromArguments(options, argument); Evaluators::CXXArguments::const_iterator iter = arguments.begin(); assert(iter != arguments.end()); struct REPL* self = (struct REPL*)(Evaluators::get_pointer(iter->second)); assert(self); // TODO actually purge return(NULL); } static AST::NodeT REPL_getOperatorPrecedenceList(AST::NodeT options, AST::NodeT argument) { Evaluators::CXXArguments arguments = Evaluators::CXXfromArguments(options, argument); Evaluators::CXXArguments::const_iterator iter = arguments.begin(); assert(iter != arguments.end()); struct REPL* self = (struct REPL*)(Evaluators::get_pointer(iter->second)); std::string v = Evaluators::str(iter->second); assert(self); return(AST::makeBox(REPL_ensure_operator_precedence_list(self), NULL/*TODO*/)); } using namespace GUI; static AST::NodeT REPL_execute1(AST::NodeT options, AST::NodeT argument) { Evaluators::CXXArguments arguments = Evaluators::CXXfromArguments(options, argument); Evaluators::CXXArguments::const_iterator iter = arguments.begin(); assert(iter != arguments.end()); struct REPL* self = (struct REPL*)(Evaluators::get_pointer(iter->second)); assert(self); ++iter; assert(iter != arguments.end()); AST::NodeT expression = iter->second; { FILL_END_ITER return(Evaluators::internNative(REPL_execute(self, expression, END_ITER))); } } //fTailEnvironment //fTailUserEnvironment /* =fTailBuiltinEnvironmentFrontier */ //fTailUserEnvironmentFrontier void REPL_add_to_environment_simple(struct REPL* self, AST::NodeT name, AST::NodeT value); static void REPL_add_static_builtin_binding(struct REPL* self, AST::NodeT name, AST::NodeT value) { REPL_add_to_environment_simple(self, name, value); } using namespace Evaluators; #define DECLARE_REPL_OPERATION(P) \ DECLARE_FULL_OPERATION(P) #define DEFINE_REPL_OPERATION(P, B) \ DEFINE_FULL_OPERATION(P, B) DEFINE_REPL_OPERATION(RInformant, { return(REPL_describe(fn, argument)); }) DEFINE_REPL_OPERATION(RDefiner, { return(REPL_define(fn, argument)); }) DEFINE_REPL_OPERATION(ROperatorPrecedenceListGetter, { return(REPL_getOperatorPrecedenceList(fn, argument)); }) DEFINE_REPL_OPERATION(RPurger, { return(REPL_purge(fn, argument)); }) DEFINE_REPL_OPERATION(RExecutor, { return(REPL_execute1(fn, argument)); }) //#define REPL_add_builtin_method(s, n, v) REPL_add_to_environment_simple(s, n, uncurried((AST::NodeT) v, (AST::NodeT) AST::makeApplication(&Evaluators::Quoter, n))) #define REPL_add_builtin_method(s, n, v) REPL_add_to_environment_simple(s, n, v) static void REPL_init_builtins(struct REPL* self) { if(self->fTailUserEnvironment && envEntryGetTail(self->fTailUserEnvironment)) { fprintf(stderr, "warning: There is a user environment, so not clobbering it by builtins.\n"); return; } if(!self->fTailEnvironment) { /* first init */ self->fTailEnvironment = makeApplication(makeAbstraction(Symbols::Snil, NULL/*mutable*/), NULL); self->fTailUserEnvironment = self->fTailEnvironment; self->fTailUserEnvironmentFrontier = self->fTailUserEnvironment; } Evaluators::BuiltinSelector_init(); REPL_add_static_builtin_binding(self, Symbols::Squote, &Evaluators::Quoter); /* keep at the beginning */ REPL_add_static_builtin_binding(self, Symbols::Sdot, AST::makeAbstraction(Symbols::Sa, AST::makeAbstraction(Symbols::Sb, AST::makeApplication(Symbols::Sa, Symbols::Sb)))); // TODO close REPL_add_static_builtin_binding(self, Symbols::Slet, &Evaluators::Quoter); // dummy // ??? REPL_add_static_builtin_binding(self, Symbols::Sinline, &Evaluators::Reducer); REPL_add_static_builtin_binding(self, AST::symbolFromStr("REPL"), AST::makeAbstraction(Symbols::Sa, AST::makeApplication(AST::makeApplication( &REPLX::REPLMethodGetter, // AST::makeApplication(&Evaluators::BuiltinGetter, AST::symbolFromStr("REPLMethods")), Symbols::Sa), AST::makeBox(self, AST::symbolFromStr("REPL"))))); REPL_add_static_builtin_binding(self, Symbols::SrequireModule, &ModuleLoader); REPL_add_builtin_method(self, Symbols::SBuiltins, &Evaluators::BuiltinGetter); REPL_add_builtin_method(self, Symbols::Scolon, &Evaluators::Conser); // module dispatch list needs that anyway //REPL_add_builtin_method(self, Symbols::Sreturnexclam, get_module_entry_accessor("IO", Symbols::Sreturnexclam)); self->fTailUserEnvironment = self->fTailUserEnvironmentFrontier; /*self->fTailUserEnvironmentFrontier = self->fTailUserEnvironmentFrontier;*/ } //int REPL_add_to_environment_simple_GUI(struct REPL* self, AST::NodeT name, AST::NodeT value); /* returns: whether we just inserted something after endIter, ostensibly moving stuff */ bool REPL_add_to_environment_simple_end(struct REPL* self, AST::NodeT name, AST::NodeT value, AST::NodeT endIter) { using namespace AST; using namespace Evaluators; int index = REPL_add_to_environment_simple_GUI(self, name, value); assert(index >= 0); EnvEntry* prevNode = self->fTailEnvironment; EnvEntry* shiftedNode = NULL; EnvEntry* newNode; // there is a sentinel at the beginning. for(; index > 0 && prevNode != endIter; --index) prevNode = envEntryGetTail(prevNode); if(!prevNode) { abort(); } shiftedNode = envEntryGetTail(prevNode); newNode = makeEnvEntry(name, value, /*next*/shiftedNode); envEntrySetTail(prevNode, newNode); return(prevNode == endIter); } void REPL_add_to_environment_simple(struct REPL* self, AST::NodeT name, AST::NodeT value) { if(REPL_add_to_environment_simple_end(self, name, value, self->fTailUserEnvironmentFrontier)) self->fTailUserEnvironmentFrontier = envEntryGetTail(self->fTailUserEnvironmentFrontier); } static AST::NodeT REPL_close_environment(struct REPL* self, AST::NodeT node) { if(self->fTailUserEnvironmentFrontier) { if(increaseGeneration() == 1) { /* overflow */ mapTree(NULL, AST::uncacheNodeResult, Evaluators::evaluateToCons(self->fTailUserEnvironment)->tail); } envEntrySetTail(self->fTailUserEnvironmentFrontier, node); return(envEntryGetTail(self->fTailEnvironment)); } else return(node); } static void REPL_unfilter_environment(struct REPL* self, AST::NodeT environment) { /* filters out Builtins */ using namespace AST; AST::NodeT name; AST::NodeT body; AST::NodeT next; if(environment == Symbols::Snil) { self->fTailUserEnvironmentFrontier = self->fTailUserEnvironment; return; } for(; environment; environment = next) { getEnvEntry(environment, name, body, next); REPL_add_to_environment_simple_GUI(self, name, body); if(next == NULL || next == Symbols::Snil) { // used as a tail marker self->fTailUserEnvironmentFrontier = environment; envEntrySetTail(self->fTailUserEnvironmentFrontier, NULL); break; } } } void REPL_set_environment(struct REPL* self, EnvEntry* environment) { envEntrySetTail(self->fTailUserEnvironment, NULL); envEntrySetTail(self->fTailUserEnvironmentFrontier, NULL); // help GC self->fTailUserEnvironmentFrontier = NULL; envEntrySetTail(self->fTailUserEnvironment, environment); REPL_unfilter_environment(self, environment); assert(self->fTailUserEnvironmentFrontier != NULL); } AST::NodeT REPL_get_user_environment(struct REPL* self) { if(self->fTailUserEnvironmentFrontier) envEntrySetTail(self->fTailUserEnvironmentFrontier, NULL); /* clear the last command from the environment, nobody cares. */ return(self->fTailUserEnvironment ? envEntryGetTail(self->fTailUserEnvironment) : NULL); } AST::NodeT REPL_prepare(struct REPL* self, AST::NodeT input) { return(prepare_module(REPL_close_environment(self, input))); } /* TODO defrec! is defrec 'a 5 let a := rec\f 5 in ... */ REGISTER_BUILTIN(RImporter, (-2), 1, AST::symbolFromStr("import!")) REGISTER_BUILTIN(RInformant, (-2), 1, AST::symbolFromStr("describe")) REGISTER_BUILTIN(RDefiner, (-3), 1, AST::symbolFromStr("define")) REGISTER_BUILTIN(ROperatorPrecedenceListGetter, (-1), 1, AST::symbolFromStr("getOperatorPrecedenceList")) REGISTER_BUILTIN(RPurger, (-1), 1, AST::symbolFromStr("purge")) REGISTER_BUILTIN(RExecutor, (-2), 1, AST::symbolFromStr("execute")) REGISTER_STR(REPL, return("REPL");) }; /* end namespace */ #endif /* __REPL_REPLENVIRONMENT_H */