{"id":19312485,"url":"https://github.com/exaexa/escm","last_synced_at":"2025-02-24T03:43:28.157Z","repository":{"id":11307508,"uuid":"13724733","full_name":"exaexa/escm","owner":"exaexa","description":"exa scheme, the C++-connected scheme interpreter.","archived":false,"fork":false,"pushed_at":"2013-10-20T18:58:34.000Z","size":352,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-06T02:40:57.836Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exaexa.png","metadata":{"files":{"readme":"README","changelog":"ChangeLog","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-10-20T18:58:04.000Z","updated_at":"2017-01-30T17:46:43.000Z","dependencies_parsed_at":"2022-07-12T15:04:32.014Z","dependency_job_id":null,"html_url":"https://github.com/exaexa/escm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exaexa%2Fescm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exaexa%2Fescm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exaexa%2Fescm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exaexa%2Fescm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exaexa","download_url":"https://codeload.github.com/exaexa/escm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240415212,"owners_count":19797599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T00:34:58.085Z","updated_at":"2025-02-24T03:43:28.120Z","avatar_url":"https://github.com/exaexa.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n .-----.-----.----.--------.\n |  -__|__ --|  __|        |\n |_____|_____|____|__|__|__|   [exa] embeddable scheme\n\n\nMinimalistic, lightweight scheme machine written in C++. Primarily focused for\nuse as embedded scripting language in any application, extensible to any level\nof scheme impossibility.\n\nBefore any work with this program, please see the license in LICENSE file.\nIf you didn't receive your copy of LICENSE, please download it from web:\n\nhttp://www.gnu.org/licenses/gpl-3.0.txt\n\n\nESCM was written by Mirek [exa] Kratochvil for some higher purposes of computer\nscience in 2007-2008. You can contact me at \u003cexa.exa@gmail.com\u003e.\n\n   ___              \n .'  _|.---.-.-----.\n |   _||  _  |  _  |\n |__|  |___._|__   |  frequently answered questions\n                |__|\n\n\nQ:\n\tWhy should I use Scheme as my scripting language, and why should I use\n\tESCM interpreter for that?\nA:\n\tScheme is a wonderful scrpiting language, to observe it, learn it.\n\tYou can look up a very good scheme tutorial called \"Teach yourself\n\tscheme in fixnum days\" by Dorai Sitaram.\n\n\tESCM aims to be the best scheme that can be used for embedding and\n\tscripting, with cleanly and readably written internals and rich\n\tprogrammer/user interface.\n\n\nQ: \n\tIs there any tutorial for embedding ESCM in my application?\nA: \n\tLook at `src/interp.cpp' in this package, there's an implementation of\n\tcomplete interpreter using libreadline (which gives bash-like features).\n\tIt should come well-commented so that any experienced-enough programmer\n\tunderstands it.\n\n\nQ:\n\tHow can I add functions and/or variables to my ESCM environment?\nA:\n\tLook at `include/macros.h', there are some macros that could possibly\n\thelp you. File `src/builtins.cpp' contains all the scheme internals\n\timplemented, so you can gain inspiration there. Basically, writing a\n\tlambda is a very simple process:\n\n\tYou want to call it this way:\n\n\t\t(write-teh-numz)\n\n\tSo you write the function, with use of one macro:\n\t\t\n\t\t#include \"path_to_escm/macros.h\"\n\n\t\tstatic escm_func_handler(thenums) {\n\t\t\tfor(int i=0;i\u003c1000;++i)printf(\"%d\\n\",i);\n\t\t\treturn_scm(NULL);\n\t\t}\n\n\treturn_scm(NULL) means that you don't return anything, in scheme that\n\twill be seen as returning the () empty list. `static' keyword is not\n\tnecessary here, but it can be usuable when writing larger projects.\n\n\tThen, if you have your environment\n\t\t\n\t\tscm_env* e;\n\t\t//....\n\t\n\tyou have to \"register\" the function, or, put the reference to the global\n\tvariable frame.\n\n\t\tescm_add_func_handler(e,\"write-teh-numz\",thenums);\n\n\t...and then you can use it!\n\n\tAny other object can be added to environment using the add_global method\n\tof scm_env:\n\n\t\te-\u003eadd_global(\"variable\",new_scm(e,number,5)\n\t\t\t\t-\u003ecollectable\u003cscm\u003e())\n\t\n\tin a result, scheme will evaluate correctly:\n\n\t\t(* variable 2) =\u003e 10\n\n\nQ:\n\tHow can I guess which environment was my C++ function called from?\nA:\n\tEvery function declared with escm_func_handler macro has the caller\n\tenvironment accesible via escm_environment local variable. This can be\n\tused for memory allocation (which you will most probably need.)\n\n\tThis is an example of function that always returns #t:\n\n\t\tescm_func_handler(foo) {\n\t\t\treturn new_scm(escm_environment,boolean,true)\n\t\t\t\t-\u003ecollectable\u003cscm\u003e();\n\t\t}\n\n\tWhen writing larger handlers, this will most probably help:\n\t\t\n\t\t#define e escm_environment   // :D\n\n\nQ: \t\n\tHow do I manipulate internal scheme data?\n\tHow do I create scheme data type of my own?\nA:\n\tEvery scheme-related data in escm is represented as a class derived from\n\tthe very basic scm class. Generally, the data is created this way:\n\n\t\tscm_env*e; \n\t\t//...\n\n\t\tscm*a=new_scm(e,string,\"foobar!\");\n\n\tThis creates new object on scheme heap, with a type of string, filled\n\twith foobar. As all scheme objects are garbage-collected, this object is\n\tinitially protected, so it doesn't get deleted accidentally before you\n\tbind it somewhere, so the garbage collector can \"reach\" it (and\n\ttherefore decide that it won't be deleted).\n\n\tYou may want to mark the object as collectable after you set some\n\tassignment to it:\n\t\t\n\t\te-\u003eadd_global(\"aaa\",a); \n\t\t/*now, that the object is referenced by the global frame,\n\t\t  it is reachable for the collector and won't be deleted by\n\t\t  accident. */\n\n\t\ta-\u003emark_collectable();\n\t\t/* If the string gets out somehow, gc can delete it. */\n\n\tFor user convenience, expression-like collectable-marking was added:\n\t\t\n\t\treturn a-\u003ecollectable\u003cstring\u003e();\n\n\tor you can combine it even like this:\n\t\t\n\t\treturn new_scm(e,string,\"returned string\") -\u003e\n\t\t\t\t\t\tcollectable\u003cstring\u003e();\n\n\tPlease note that collectable-marking is recursive, so when you possibly\n\thad scheme object like this:\n\n\t\t(1 . (2 . (3 . (4 . 0))))\n\t\n\tYou don't have to mark all 4 pairs, but only the first one (with '1'),\n\tand other get marked properly because they're children of the first\n\tpair.\n\t\n\tIn ESCM we have several such string- and pair-like \"basic\" types:\n\t\t\n\t\t-- C++ class\t-- scheme expression\n\n\t\tnumber\t\t123\n\t\tstring\t\t\"hello\"\n\t\tcharacter\t#\\a\n\t\tboolean\t\t#t #f\n\t\tpair\t\t(1 . 2) (1 2 3 4)\n\t\tclosure\t\t(lambda (foo) foo)\n\t\tNULL\t\t()\n\t\n\tPlease note that we work only with pointers to these classes, never\n\tdirectly. There are several more classes to handle scheme internals,\n\tthose include class frame, continuation, lambda, macro, and derivees.\n\n\tNOTE: we will describe work with macros later.\n\n\tYou may work with classes through pointers, you can see file \n\t`include/types.h' to see what every class contains. You can work with\n\tnumber class this way:\n\t\t\n\t\tnumber *a;\n\t\t//.....\n\t\ta-\u003en=23;\n\t\n\tConstructors of the classes are closely related to the new_scm macro we\n\thave seen earlier. For example using\n\n\t\tnew_scm(e,type,foo,bar)\n\t\n\twill allocate the memory for `type' in heap that belongs to scm_env e, \n\tand then will call a constructor:\n\t\t\n\t\ttype(foo,bar)\n\n\tDestructors are properly called, too, but the time of object deletion is\n\trather unpredictable - so for example having some `printf's in ~type\n\tdestructor will make the program throw the lines on garbage collection,\n\twhich can be years after the objects were really used. On the other\n\thand, this can be interesting to observe. (measure how many % of your\n\tobjects get redundant, or just to do some statistics)\n\n\n\tPlease note that because of principle of garbage collection, when your\n\tcustom scheme object refers to another garbage-collected scheme objects,\n\tit has to be able to mark those objects as it's children. This is done\n\twith the scm* get_child(int) function. Every scheme object has to have\n\tone, and every such function has to behave in this way:\n\n\t1] if it's called with a number n, it returns pointer to n-th child.\n\t2] if it's called with a higher number, it returns constant\n\t   escm_no_more_children.\n\t\n\tUsually the function gets called sequentially with values increasing\n\tfrom zero. You must not return any pointers that point on anything other\n\tthan the class scm's derivee, but you can return NULL.\n\n\tYou can see some examples of get_child() functions in `include/types.h'\n\tfile.\n\n\tIf you need some memory allocation in the scheme machine, you can use\n\tclassical C/C++ memory allocation methods, and if you need the data on\n\tthe escm's heap (so it can get stored to a file or something), you can\n\tuse the data scheme this way:\n\n\t\ta=new_data_scm(scm_env_pointer,size)\n\n\tand access allocated data with dataof() macro, for example:\n\n\t\tmemcpy(dataof(a),pointer,size);\n\n\n\tWith all of this in mind, creating a garbage-collectable class is pretty\n\tsimple. Look, for example, at header of class for letters.\n\n\t\tclass character: public scm\n\t\t{\n\t\tpublic:\n\t\t\tchar c;\n\t\t\tinline character (scm_env*e, char a) : scm (e)\n\t\t\t{\n\t\t\t\tc = a;\n\t\t\t}\n\n\t\t\tvirtual std::string display_internal (int style);\n\t\t};\n\n\tscm_env*e is passed to the constructor because of need for more\n\tconstruct-time allocations.\n\t\n\tNotice the display_internal() function - it formats an object to some\n\thuman-readable form and returns it in std::string from STL. When style\n\tis 0, then it's formatted like scheme source code, otherwise it's\n\tformated for standart output. For example:\n\n\t\t style --\t-- type\t\t-- output string\n\t\t\n\t\t\t0\tcharacter\t#\\a\n\t\t\t0\tstring\t\t\\\"oh hai\"\n\t\t\t1\tcharacter\ta\n\t\t\t1\tstring\t\toh hai\n\t\n\tTo get garbage-collection working well, you have to code correct\n\tget_child function. For example, class pair could look like this:\n\n\t\tclass pair : public scm {\n\t\tpublic:\n\t\t\tscm *a, *d;\n\n\t\t\tvirtual scm* get_child(int i) {\n\t\t\t\tswitch (i) {\n\t\t\t\tcase 0: return a;\n\t\t\t\tcase 1: return d;\n\t\t\t\tdefault: return escm_no_more_children;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// ...\n\n\t\t};\n\n\nQ:\n\tHow do I get data from/to a functions?\n\tHow do I get function arguments which were passed to C++ code?\n\tHow do I return value from a C++ function to scheme?\nA:\n\tArguments which the function was called with can be retrieved with\n\tmacros defined in `include/macros.h'. Those are:\n\n\t\t-- macro\t\t-- usage\n\n\t\targ_count\t\tcount of arguments, without tail \n\t\t\t\t\targument\n\n\t\thas_arg\t\t\ttrue if there's one or more \"classical\"\n\t\t\t\t\targument available\n\t\t\n\t\tpop_arg()\t\tpops that argument\n\n\t\thas_tail_arg\t\ttrue if the next argument is \"tail\"\n\n\t\tpop_tail_arg()\t\tpops it\n\n\t\tpop_arg_type(T)\t\tpops the corresponding argument, but\n\t\tpop_tail_arg_type(T)\treturns NULL if it's not of type T.\n\n\tNote, classical and tail arguments mean roughly this in scheme:\n\n\t\t(function classical-arg classical-arg . tail-arg)\n\t\n\n\tTo return a value from the function, use return_scm macro:\n\n\t\treturn_scm(new_scm(e,boolean,true));\n\n\tThis is not a \"real return\", function code may continue even below the\n\treturn_scm macro, but do not try to return any other value. (because you\n\twould most probably cause the next function in call-stack to return that\n\tvalue, which could bring not-really-interesting results)\n\n\nQ:\n\tHow do I use macros?\nA:\n\tMacros differ from functions in some ways:\n\n\t1] when you call a macro, the arguments don't get evaluated.\n\t2] macro gets the arguments in one list, does something with them,\n\t   evaluates to a value, and this value is then evaluated again.\n\t\n\tSimple explanation for non-schemers: Macro is a code generator. It gets\n\tsomething, creates a scheme code, and that code then gets evaluated.\n\n\tIn ESCM, the code can be generated by another scheme code, or by some\n\tC++ function.\n\n\tMacro in scheme code looks like this:\n\n\t\t(macro (a form) (cdr form))\n\t\t(a + 1 2 3) =\u003e 6\n\n\t\t; this is equivalent definition:\n\n\t\t(define a (macro (form) (cdr form)))\n\n\tNote that macro is called with its whole syntax, so this would create an\n\tendless loop:\n\t\t\n\t\t(macro (a form) form)\n\t\t(a)\n\n\t\t; =\u003e (a) =\u003e (a) =\u003e (a) =\u003e ...\n\n\t\n\tIn C++, macro can be defined just like the function. We can implement\n\tmacro from above:\n\n\t\tescm_macro_handler(a,code)\n\t\t{\n\t\t\treturn_macro_code(code-\u003ed);\n\t\t}\n\n\tYou might NOT want the interpreter to evaluate things you created, and\n\tuse it as a result of whole operation - then you need to play with the\n\tcontinuations a little. There are several macros defined that way:\n\n\t\t#define e escm_environment\n\n\t\tescm_macro_handler(lambda,code)\n\t\t{\n\t\t\tpair* c = pair_p (code-\u003ed);\n\t\t\tif (!c) e-\u003ethrow_exception (code);\n\t\t\treturn_scm (new_scm (e, closure,\n\t\t\t\tc-\u003ea, pair_p (c-\u003ed), e-\u003econt-\u003eenv)\n\t\t\t\t-\u003ecollectable\u003cscm\u003e() );\n\t\t}\n\n\t\tescm_macro_handler(begin,code)\n\t\t{\n\t\t\te-\u003ereplace_cont (new_scm \n\t\t\t\t(e, codevector_continuation, pair_p (code-\u003ed) )\n\t\t\t\t\t -\u003ecollectable\u003ccontinuation\u003e() );\n\t\t}\n\n\t\tescm_macro_handler(quote,code)\n\t\t{\n\t\t\tif(pair_p (code-\u003ed) ) \n\t\t\t\treturn_scm (((pair*)(code-\u003ed))-\u003ea);\n\t\t}\n\n\nQ:\n\tHow can I see what error occured?\nA:\n\tESCM has classical scheme error mechanism. You may define *error-hook*\n\tto set desired error-handling behavior.\n\n\tFor example, when using the interpreter:\n\n\t\t\u003e (macro)\n\t\t =\u003e ()\n\n\t\t\u003e (define (*error-hook* . x)\n\t\t | (display \"error: \")\n\t\t | (display x)\n\t\t | (newline))\n\t\t =\u003e *ERROR-HOOK*\n\n\t\t\u003e (macro)\n\t\terror: bad macro syntax\n\t\t =\u003e ()\n\n\t\t\u003e (define 123 4356)\n\t\terror: (\"invalid define symbol format\" 123)\n\t\t =\u003e ()\n\n\tR5RS scheme features like catch/throw and error stack should be defined\n\tin scheme initialization file.\n\nQ:\n\tHow do I catch escm errors?\n\tHow are the exceptions implemented in escm?\nA:\n\tSimply, as in every scheme. There should be *globally* defined variable\n\t*ERROR-HOOK* (with asterisks), which is lambda. Every exception\n\tgenerated by internal escm code, embedded code, or (error) call is then\n\tfed to it. For example you might have noticed that \"clean\" interpreter\n\tdoesn't really print out error messages. Fix is simple:\n\n\t\t(define (*error-hook* . x)\n\t\t\t(display \"error: \")\n\t\t\t(display x)\n\t\t\t(newline))\n\t\n\tNotice that argument has variable length, so anything can be passed to\n\tthis.\n\n\tImplementing some other error-handling stacks depends on your choice.\n\tDefault escm interpreter should come with some solution.\n\n\nQ:\n\tHow do I generate an exception?\nA:\n\tFrom scheme code, lambda (error) is predefined in default builtins, you\n\tmay use it this way:\n\n\t\t(error 'any 'description 'here)\n\n\tNote that error is lambda, so arguments get evaluated before throwing.\n\n\tWhen implementing some builtin C++ function, you may use any of scm_env\n\tprovided member error generators:\n\t\t\n\t\tvoid scm_env::throw_exception (scm* s)\n\t\tvoid scm_env::throw_string_exception (char* s)\n\t\tvoid scm_env::throw_desc_exception (char*desc, scm* s)\n\t\n\tAll those do the same thing - throw an exception which is then usually\n\tpassed to *error-hook*. You can safely emulate their behavior using\n\tC++ exception mechanism, mostly because escm exceptions are built upon\n\tthat one. You should also be careful if you try to write some code that\n\tcatches (scm*) - if it calls _any_ escm code, it must be prepared to\n\tcatch its exceptions and probably forward them by another throw.\n\n\tSo, generally, example definition of builtin (error) should be\n\tsufficient to explain all of this:\n\n\t\t\n\t\tvoid op_error (scm_env*e, scm*arglist)\n\t\t{\n\t\t\te-\u003ethrow_exception (arglist);\n\t\t}\n\n\tMacros for exception handling similar to the parameter handling macros\n\tfound above should be implemented as well, self-explainable from file\n\t`include/macros.h'\n\t\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexaexa%2Fescm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexaexa%2Fescm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexaexa%2Fescm/lists"}