{"id":13780876,"url":"https://github.com/andrewbuss/crisp","last_synced_at":"2025-05-11T14:34:12.173Z","repository":{"id":35491104,"uuid":"39760573","full_name":"andrewbuss/crisp","owner":"andrewbuss","description":"A C interpreter for a purely functional language, bolted to a very unsafe FFI","archived":false,"fork":false,"pushed_at":"2016-10-17T01:28:47.000Z","size":98,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-17T16:41:43.369Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andrewbuss.png","metadata":{"files":{"readme":"README","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-27T07:15:07.000Z","updated_at":"2018-11-17T05:23:08.000Z","dependencies_parsed_at":"2022-08-17T15:51:08.301Z","dependency_job_id":null,"html_url":"https://github.com/andrewbuss/crisp","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/andrewbuss%2Fcrisp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewbuss%2Fcrisp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewbuss%2Fcrisp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewbuss%2Fcrisp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrewbuss","download_url":"https://codeload.github.com/andrewbuss/crisp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253580203,"owners_count":21930899,"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-08-03T18:01:20.681Z","updated_at":"2025-05-11T14:34:11.843Z","avatar_url":"https://github.com/andrewbuss.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"crisp: a purely functional language interpreter bolted to a very unsafe FFI\n\nRepo contents:\n\n    crisp.h       : header file with core declarations\n    crisp.c       : the core, including cell allocation and evaluation\n    parse.c       : routines for converting between cells and strings\n    ffi.c         : routines supporting the foreign function interface\n    interpreter.c : REPL\n\n    modules/std     : a module containing important functions which are\n      |               not required to implement the minimal interpreter\n      + std.c       : wrappers around zip, concat, apply, assoc which conform\n      |               to the native function interface, plus hash and ispair,\n      |               plus asc, sum, product, and modulus\n      + std.crisp   : declare the above native functions in the global env\n                      This also contains many useful lambda functions:\n                      nil, not, and, nand, or, nor, neq, dec, inc,\n                      void, makerec, defrec, test, testwith, list-equal,\n                      c{a,d}{a,d}r, reduce, map, all, any, none, filter,\n                      reverse-concat, reverse, reversed-range, range,\n                      repeat, zip, len\n\n    tests.crisp     : an assortment of tests and additional syntax examples\n    libc_demo.crisp : a few examples using the FFI with libc\n    bintree.crisp   : an implementation of a basic persistent binary tree map\n\n    fuzz_parser.c   : a small program to exercise the parser and verify\n                      round-trip stability. Only useful for fuzzing\n\nBuilding:\n\n    git submodule update --init\n    ln -sr libatomic_ops bdwgc\n    cmake .\n    make\n\n    # optionally, install with all modules; read Modules below\n    sudo make install\n\nSome syntax examples and testing code are in the tests file. Run tests:\n\n    crisp \u003c tests.crisp\n\nRun as a REPL:\n\n    crisp\n\nModules:\n\n    crisp.c contains the minimal evaluation logic, but many common functions\n    are implemented in the std module. A module is a crisp script, optionally\n    combined with any number of compiled object files, linked into a shared\n    library. When a module is loaded with the `import` function, the script is\n    executed in an environment with a few extra mappings:\n\n        this            : an FFI_LIBRARY, the module currently being loaded\n        native-function : convert an FFI_SYMBOL into a NATIVE_FUNCTION\n\n    In practice, a module will contain `def`'s to define new functions in the\n    global environment. For example:\n\n        def hash native-function this.hash\n\n    will expose `hash` for use after the module is imported. Private functions\n    need not be declared in this way.\n\n    Modules are loaded at runtime using the `import` function. Modules may\n    import other modules similarly, although crisp does not attempt to detect\n    or resolve circular dependencies.\n\n    Modules are installed by default to /usr/lib/ with filenames of the form\n    `libstd.crisp.so`. In addition to the default library paths, `import`\n    tries to find a library named `std` in:\n\n        $CRISP_MODULE_PATH/modules/libstd.crisp.so\n        $CRISP_MODULE_PATH/std/libstd.crisp.so\n\n    where CRISP_MODULE_PATH is an environment variable that may be provided at\n    runtime, and defaults to `./modules/` if not provided. If imports are\n    failing, check that modules are reachable from the current directory.\n\nSyntax notes:\n\n    Whitespace is generally ignored. An exception, in order to work as a REPL,\n    is that lines are evaluated after each newline, unless there are unclosed\n    parentheses. Example:\n\n        sum 1 2 3  ; result: 6\n        4 5        ; result: 4 5\n\n        (sum 1 2 3\n             4 5)  ; result: 15\n\n    The if function takes two required arguments: a predicate and a clause to\n    be evaluated if the predicate is non-nil. If only two arguments are\n    supplied and the predicate is nil, nil is returned. If the predicate is\n    nil, everything after the second argument is evaluated. This allows an\n    easier syntax for chaining if's. The lambda function works similarly;\n    evaluating everything after the first argument as a referent. Example:\n\n        def fizzbuzz (lambda x\n            with fizz (equal (modulus x 3) 0)\n            with buzz (equal (modulus x 5) 0)\n            if (and fizz buzz)\n                FizzBuzz\n            if fizz\n                Fizz\n            if buzz\n                Buzz\n            x)\n\n        map fizzbuzz (range 20)\n        ; output:\n        ; FizzBuzz 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz\n          11 Fizz 13 14 FizzBuzz 16 17 Fizz 19\n\nEvaluation notes:\n\n    A list is evaluated by examining its head. If the head is callable, and\n    isn't a special function (examples include def, quote, lambda), which\n    holds evaluation for its arguments, the arguments are evaluated and the\n    function is applied. If the head is not callable, each element of the list\n    is independently evaluated and the resulting list is returned.\n\n    The behavior of native functions is unconstrained, although the interface\n    is defined: a native function accepts a cell which may be either NIL or a\n    PAIR, and returns a cell which may be of any type.\n\n    Little is specified about the behavior of FFI functions - the interface\n    is not stable enough to describe yet. Roughly, an FFI_FN can be\n    applied to integer or symbol arguments. Symbol arguments are passed as\n    pointers to the underlying strings, so (libc.puts foo) prints \"foo\\n\".\n    The return value of an FFI_FN is always assumed to be an integer.\n    This means that (libc.malloc 4096) will return an integer pointer which\n    can be passed to libc.gets or libc.free, for example.\n\n    Finally, lambdas are evaluated by evaluating the body of a lambda in a new\n    environment where the names in the lambda args list are mapped to the\n    corresponding values to which the lambda is being applied. This new\n    environment is stacked atop the environment in which the lambda was\n    created, so variables may override other local variables, but global\n    definitions are still accessible.\n\n    For example, to evaluate\n\n        with z 4 (def f lambda (x y) sum y (product z x))\n\n        f 3 7\n\n    the interpreter first pairs x with 3 and y with 7 to yield a new env\n    concatenated with the existing environment:\n\n        (x . 3) (y . 7) (z . 4) ... (f . (lambda (x y) sum y (product z x)))\n\n    Then it evaluates the body of the lambda in this environment, obtaining:\n\n        sum y (product z x) -\u003e\n            y -\u003e 7\n            product z x -\u003e\n                z -\u003e 4\n                x -\u003e 3\n            -\u003e apply product 4 3 -\u003e 12\n        -\u003e apply sum 7 12 -\u003e 19\n\nImplementation notes:\n\n    At one point crisp was implemented in a purely functional style\n    (as far as that's possible in C). Later work, particularly work on\n    optimizing tail calls, has added enough complex control flow that this\n    is no longer really true, but the goal of moving as much functionality\n    from the interpreter into the language remains.\n\n    There is no special \"environment\" struct. The environment is a normal list\n    containing dotted-pair mappings between variable names and their values.\n    Evaluation contexts are stacked by concatenating a list of name/value pairs\n    to the previous environment.\n\n    The intention is to implement only the minimum functionality in C required\n    to implement higher-level functionality in the crisp language, such as\n    recursion, map, filter, defrec, and, via the FFI, libc, and glib, I/O of\n    some sort.\n\n    At present, crisp uses the Boehm-Demers-Weiser (http://www.hboehm.info/gc/)\n    garbage collector rather than including one of its own. A reference counted\n    system is a goal of later work.\n\n    The FFI functionality is exciting because it allows reuse of libraries from\n    many other projects with very little extra C code in crisp. Using libc from\n    crisp allows all sorts of low-level I/O. \n\n    Symbols are interned when they are parsed.\n\nFuzzing:\n\n    Automated fuzzing is a fun way to catch bugs. CMake targets are included\n    building crisp without FFI or GC. FFI yields a lot of false positives when\n    a fuzzer discovers it can pass a garbage pointer to libc.free for example.\n    Allowing the fuzzer to call arbitrary external code is also simply a\n    worrying idea. GC tends to clog up the fuzzer with extra logic and control\n    flow, and isn't needed for short executions.\n\n    Build the slimmer crisp:\n\n        cd fuzzing\n        cmake .. -DCMAKE_C_COMPILER=afl-gcc\n        make crisp_fuzz\n\n    Prepare directories for afl:\n\n        mkdir inputs afl_state\n        echo \"(lambda (x y) y x) 4 2\" \u003e inputs/simple\n\n    Run afl with a 200 MB memory limit:\n\n        afl-fuzz -m 200 -x afl_dict.txt -i inputs -o afl_state ./crisp_fuzz\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewbuss%2Fcrisp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrewbuss%2Fcrisp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewbuss%2Fcrisp/lists"}