Coder Social home page Coder Social logo

mattwparas / steel Goto Github PK

View Code? Open in Web Editor NEW
874.0 13.0 39.0 5.81 MB

An embedded scheme interpreter in Rust

License: Apache License 2.0

Rust 77.98% Racket 4.45% Scheme 17.41% Lua 0.02% Python 0.06% Shell 0.01% Nix 0.06% Dockerfile 0.01%
rust racket transducers scripting-language contracts programming-language

steel's Introduction

Steel

An embeddable and extensible scheme dialect built in Rust.

Actions Status Coverage Status Discord Chat

Try it on the Playground · Read the Steel book (WIP)

Getting Started

This github repository contains a cli interpreter. To try it out on the online playground, go to the Steel playground. To get started using a repl with the crates, make sure you first have rust installed.

Then, clone the repo and run the following command:

cargo run

This will launch a REPL instance that looks something like this:

Packages

If you would like to install and use packages, please set the STEEL_HOME environment variable. This will be the location that packages get installed to. Steel currently does not assume any default.

About

Steel is an embeddable scheme interpreter, with a standalone cli included as well. Inspired largely by Racket, the language seeks to be ergonomic scheme variant helpful for embedding in applications, or to be used on its own with high performance functions implemented in Rust. The language implementation itself contains a fairly powerful macro system based on the syntax-rules style and a bytecode virtual machine. At the moment, it is not explicitly compliant with any individual scheme specification.

Warning The API is unstable with no guarantees, and may change at any time while pre 1.0. There are undoubtedly bugs that exist, and any major bug reports will be addressed quickly. That being said, I do use it as a daily driver for many scripting tasks myself.

Features

  • Limited syntax-rules style macros are supported
  • Easy integration with Rust functions and structs
  • Easily call a script from rust or via a separate file
  • Efficient - common functions and data structures are optimized for performance (map, filter, etc)
  • Higher order Contracts
  • Built in immutable data structures include:
    • lists
    • vectors
    • hashmaps
    • hashsets

Contracts

Inspired by Racket's higher order contracts, Steel implements* higher order contracts to enable design by contract, made easy with a define/contract macro for easier ergonomics. Racket makes use of a concept known as blame which seeks to identify the violating party - Steel does not quite have fully fleshed out blame but that is a work in progress. Here are some examples:

;; Simple flat contracts
(define/contract (test x y)
    (->/c even? even? odd?)
    (+ x y 1))

(test 2 2) ;; => 5

(define/contract (test-violation x y)
    (->/c even? even? odd?)
    (+ x y 1))

(test-violation 1 2) ;; contract violation

Contracts are implemented as values, so they are bound to functions. This enables the use of contract checking on functions themselves since functions can be passed around:

;; Higher order contracts, check on application
(define/contract (higher-order func y)
    (->/c (->/c even? odd?) even? even?)
    (+ 1 (func y)))

(higher-order (lambda (x) (+ x 1)) 2) ;; => 4

(define/contract (higher-order-violation func y)
    (->/c (->/c even? odd?) even? even?)
    (+ 1 (func y)))

(higher-order-violation (lambda (x) (+ x 2)) 2) ;; contract violation

Contracts on functions do not get checked until they are applied, so a function returning a contracted function won't cause a violation until that function is actually used:

;; More higher order contracts, get checked on application
(define/contract (output)
    (->/c (->/c string? int?))
    (lambda (x) 10))

(define/contract (accept func)
    (->/c (->/c string? int?) string?)
    "cool cool cool")

(accept (output)) ;; => "cool cool cool"

;; different contracts on the argument
(define/contract (accept-violation func)
    (->/c (->/c string? string?) string?)
    (func "applesauce")
    "cool cool cool")

(accept-violation (output)) ;; contract violation

;; generates a function
(define/contract (generate-closure)
    (->/c (->/c string? int?))
    (lambda (x) 10))

;; calls generate-closure which should result in a contract violation
(define/contract (accept-violation)
    (->/c (->/c string? string?))
    (generate-closure))

((accept-violation) "test") ;; contract violation

Perhaps a more nuanced case:

(define/contract (output)
    (->/c (->/c string? int?))
    (lambda (x) 10.2))

(define/contract (accept)
    (->/c (->/c string? number?))
    (output))


((accept) "test") ;; contract violation 10.2 satisfies number? but _not_ int?

* Very much a work in progress

Transducers

Inspired by clojure's transducers, Steel has a similar object that is somewhere half way in between transducers and iterators. Consider the following:

(mapping (lambda (x) (+ x 1))) ;; => <#iterator>
(filtering even?) ;; => <#iterator>
(taking 15) ;; => <#iterator>

(compose 
    (mapping add1)
    (filtering odd?)
    (taking 15)) ;; => <#iterator>

Each of these expressions emit an <#iterator> object, which means they're compatible with transduce. transduce takes a transducer (i.e. <#iterator>) and a collection that can be iterated (list, vector, stream, hashset, hashmap, string, struct) and applies the transducer.

;; Accepts lists
(transduce (list 1 2 3 4 5) (mapping (lambda (x) (+ x 1))) (into-list)) ;; => '(2 3 4 5 6)

;; Accepts vectors
(transduce (vector 1 2 3 4 5) (mapping (lambda (x) (+ x 1))) (into-vector)) ;; '#(2 3 4 5 6)

;; Even accepts streams!
(define (integers n)
    (stream-cons n (lambda () (integers (+ 1 n)))))

(transduce (integers 0) (taking 5) (into-list)) ;; => '(0 1 2 3 4)

Transduce accepts a reducer function as well. Above we used into-list and into-vector, but below we can use any arbitrary reducer:

;; (-> transducer reducing-function initial-value iterable)
(transduce (list 0 1 2 3) (mapping (lambda (x) (+ x 1))) (into-reducer + 0)) ;; => 10

Compose just combines the iterator functions and lets us avoid intermediate allocation. The composition works left to right - it chains each value through the functions and then accumulates into the output type. See the following:

(define xf 
    (compose 
        (mapping add1)
        (filtering odd?)
        (taking 5)))

(transduce (range 0 100) xf (into-list)) ;; => '(1 3 5 7 9)

Syntax Choices

Steel is mildly opinionated in that there a few ways to define variables and functions. These choices are fairly arbitrary except for the shorthand function syntax, which I borrowed from Racket. defn and fn were really encouraged by me wanting to type less characters.

;; All of the following are equivalent
(define (foo x) (+ x 1))
(define foo (lambda (x) (+ x 1)))
(defn (foo x) (+ x 1))
(defn foo (lambda (x) (+ x 1)))

;; All of the following are equivalent
(lambda (x) (+ x 1))
(λ (x) (+ x 1))
(fn (x) (+ x 1))

Modules

In order to support a growing codebase, Steel has module support for projects spanning multiple files. Steel files can provide values (with contracts attached) and require modules from other files:

;; main.scm
(require "provide.scm")

(even->odd 10)


;; provide.scm
(provide 
    (contract/out even->odd (->/c even? odd?))
    no-contract
    flat-value)

(define (even->odd x) 
    (+ x 1))

(define (accept-number x) (+ x 10))

(define (no-contract) "cool cool cool")
(define flat-value 15)

(displayln "Calling even->odd with some bad inputs but its okay")
(displayln (even->odd 1))

Here we can see if we were to run main that it would include the contents of provide, and only provided values would be accessible from main. The contract is attached at the contract boundary, so inside the provide module, you can violate the contract, but outside the module the contract will be applied.

A few notes on modules:

  • Cyclical dependencies are not allowed
  • Modules will be only compiled once and used across multiple files. If A requires B and C, and B requires C, C will be compiled once and shared between A and B.
  • Modules will be recompiled when changed, and any dependent files will also be recompiled as necessary

Performance

Preliminary benchmarks show the following on my machine:

Benchmark Steel Python
(fib 28) 63.383ms 65.10 ms
(ack 3 3) 0.303 ms 0.195 ms

Examples of embedding Rust values in the virtual machine

Rust values, types, and functions are easily embedded into Steel. Using the register_fn call, you can embed functions easily:

use steel_vm::engine::Engine;
use steel_vm::register_fn::RegisterFn;

fn external_function(arg1: usize, arg2: usize) -> usize {
    arg1 + arg2
}

fn option_function(arg1: Option<String>) -> Option<String> {
    arg1
}

fn result_function(arg1: Option<String>) -> Result<String, String> {
    if let Some(inner) = arg1 {
        Ok(inner)
    } else {
        Err("Got a none".to_string())
    }
}

pub fn main() {
    let mut vm = Engine::new();

    // Here we can register functions
    // Any function can accept parameters that implement `FromSteelVal` and
    // return values that implement `IntoSteelVal`
    vm.register_fn("external-function", external_function);

    // See the docs for more information about `FromSteelVal` and `IntoSteelVal`
    // but we can see even functions that accept/return Option<T> or Result<T,E>
    // can be registered
    vm.register_fn("option-function", option_function);

    // Result values will map directly to errors in the VM and bubble back up
    vm.register_fn("result-function", result_function);

    vm.run(
        r#"
        (define foo (external-function 10 25))
        (define bar (option-function "applesauce"))
        (define baz (result-function "bananas"))
    "#,
    )
    .unwrap();

    let foo = vm.extract::<usize>("foo").unwrap();
    println!("foo: {}", foo);
    assert_eq!(35, foo);

    // Can also extract a value by specifying the type on the variable
    let bar: String = vm.extract("bar").unwrap();
    println!("bar: {}", bar);
    assert_eq!("applesauce".to_string(), bar);

    let baz: String = vm.extract("baz").unwrap();
    println!("baz: {}", baz);
    assert_eq!("bananas".to_string(), baz);
}

We can also embed structs themselves:

use steel_vm::engine::Engine;
use steel_vm::register_fn::RegisterFn;

use steel_derive::Steel;

// In order to register a type with Steel,
// it must implement Clone, Debug, and Steel
#[derive(Clone, Debug, Steel, PartialEq)]
pub struct ExternalStruct {
    foo: usize,
    bar: String,
    baz: f64,
}

impl ExternalStruct {
    pub fn new(foo: usize, bar: String, baz: f64) -> Self {
        ExternalStruct { foo, bar, baz }
    }

    // Embedding functions that take self by value
    pub fn method_by_value(self) -> usize {
        self.foo
    }

    pub fn method_by_reference(&self) -> usize {
        self.foo
    }

    // Setters should update the value and return a new instance (functional set)
    pub fn set_foo(mut self, foo: usize) -> Self {
        self.foo = foo;
        self
    }
}

pub fn main() {
    let mut vm = Engine::new();

    // Registering a type gives access to a predicate for the type
    vm.register_type::<ExternalStruct>("ExternalStruct?");

    // Structs in steel typically have a constructor that is the name of the struct
    vm.register_fn("ExternalStruct", ExternalStruct::new);

    // register_fn can be chained
    vm.register_fn("method-by-value", ExternalStruct::method_by_value)
        .register_fn("method-by-reference", ExternalStruct::method_by_reference)
        .register_fn("set-foo", ExternalStruct::set_foo);

    let external_struct = ExternalStruct::new(1, "foo".to_string(), 12.4);

    // Registering an external value is fallible if the conversion fails for some reason
    // For instance, registering an Err(T) is fallible. However, most implementation outside of manual
    // ones should not fail
    vm.register_external_value("external-struct", external_struct)
        .unwrap();

    let output = vm
        .run(
            r#"
            (define new-external-struct (set-foo external-struct 100))
            (define get-output (method-by-value external-struct))
            (define second-new-external-struct (ExternalStruct 50 "bananas" 72.6))
            "last-result"
        "#,
        )
        .unwrap();

    let new_external_struct = vm.extract::<ExternalStruct>("new-external-struct").unwrap();
    println!("new_external_struct: {:?}", new_external_struct);
    assert_eq!(
        ExternalStruct::new(100, "foo".to_string(), 12.4),
        new_external_struct
    );

    // Can also extract a value by specifying the type on the variable
    let get_output: usize = vm.extract("get-output").unwrap();
    println!("get_output: {}", get_output);
    assert_eq!(1, get_output);

    let second_new_external_struct: ExternalStruct =
        vm.extract("second-new-external-struct").unwrap();
    println!(
        "second_new_external_struct: {:?}",
        second_new_external_struct
    );
    assert_eq!(
        ExternalStruct::new(50, "bananas".to_string(), 72.6),
        second_new_external_struct
    );

    // We also get the output of the VM as the value of every expression run
    // we can inspect the results just by printing like so
    println!("{:?}", output);
}

See the examples folder for more examples on embedding values and interacting with the outside world.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

See CONTRIBUTING.md.

steel's People

Contributors

aadamandersson avatar angelochecked avatar cgahr avatar dlip avatar duttakapil avatar gabydd avatar glenn-m avatar jasonmliunu avatar kekamui avatar kskarthik avatar mattwparas avatar mhjasonliu avatar partisani avatar rktjmp avatar sirius94 avatar wmedrano avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

steel's Issues

installer prototype

Being a somewhat lazy user of programming languages, I must admit that I'm not entirely at ease with the idea of having to navigate the nuances of installation, locating the binary, and confirming its build version...

I've been thinking about making a standard installer using a popular package manager. I'm mostly focused on myself, and right now, I'm exploring how to add something to Brew and Nixpkgs, which I use the most.

In this context, I find the Helix project on repology quite intriguing, and I'm pondering the idea of setting up an automated release pipeline for it.

@mattwparas, have you already considered this? Do you have any guidelines, concerns, or considerations about it?

(require "something") not working on windows

On windows, I can install steel and it's cogs fine, but I can't require modules that are in current path, or giving a module via absolute path like is the default on your fork of helix with steel support showin in the image bellow.
image

Next is an example of using steel directly with using a module that is in the same directory, but I get an error that it can't be found.

image

Make dylib loading lazy

At the moment, all of the dylibs are loaded at interpreter startup - we probably want to only load the dylib once there is actually a call to require-builtin that references a dylib.

Another concern is that compilation requires loading the dylib, which we also don't want. This is fine for use cases when compilation happens directly before running the actual code, but if we're compiling ahead of time or checking the bytecode or ast dump, we don't want to have to load the dylib. We'll need some kind of primitive to expose the expected API of the dylib rather than load the dylib to inspect the module API within it to allow compilation to succeed in an environment where the dylib doesn't exist, perhaps something like

;; Contains the functions, "foo" and "bar"
(require-builtin external-dylib)

(declare "foo")
(declare "bar")

declare should just reserve a slot in the global env for the value, but otherwise shouldn't do anything.

Support native threads properly

Tracking issue for native threads

Right now, threads are created by creating a new runtime instance on another thread, where the entire runtime environment is copied and moved over, which is error prone since some values cannot be moved.

Error reporting is also poor on threads - they can error on creation and during runtime, and it will be difficult to know.

It also isn't particularly documented - so this is at least somewhere where progress on it can be documented.

Allow #[steel] attribute to be used on Enums

Tagging enums with the #[steel] should result in constructors and predicates (potentially, getters and setters) for the enum variants. For example, this enum:

#[steel]
pub enum Foo {
        Bar,
        Baz(usize)
}

Should result in the following scheme functions:

Foo?
Foo-Bar?
Foo-Baz?
Foo-Bar
Foo-Baz
...
(Foo-Bar) ;; <#Foo>
(Foo-Baz 15) ;; <#Foo>
...

Work still needs to be done to decide how to get and set structs/enums with unnamed fields.

WebAssembly

Does the compiler run on WebAssembly (wasm)? Is such feature planned?

Roadmap

Thanks for this project, its exciting to see a scheme in Rust, I think it they compliment each other well (with regards to development vs run time, experimental vs standardized).

Although I can see the value in keeping it the future open, I was wondering what your goals for this project were?

Specifically, your thoughts on compatibly with:

  • no_std
  • async
  • wasm
  • non-lisp syntax extensions

REPL prompt is incorrectly fed a path name even though the loading has failed

Issue encountered using Steel v 0.5.0 1fb88e5

The REPL prompt, initially defined like so λ >, is none the less fed the newly added path name despite the loading failed.
That seems to me like unintended behaviour because of the visual noise and a possible misinterpretation leading to the belief that some symbols are actually imported in the current environment.

  1. Run the Steel REPL:
    steel

  2. Load a wrongly named or inexistant file/path name:
    :load utilities.scm

  3. An non-blocking error occurs:
    No such file or directory (os error 2)

  4. The prompt is however fed with a non-loaded path name:
    λ > (utilities.scm) >

Here is the actual result on my machine:
steel_repl_prompt_error

EDIT:
The same issue occurs when the file tentatively loaded actually exists.
Despite failing to load that said file due to a whatever compiling error, the prompt is still fed the path name, which again is an undesirable consequence.

:load <existing_file.scm> produces the following result:

λ > :load let02.scm
error[E09]: Parse
   ┌─ let02.scm:15:2
   │
15 │ (let ([x 3]
   │  ^^^ Parse: Syntax Error: let expects an expression, found none

λ (let02.scm) >

On a side note:

  • It could possibly be tagged as a "good first issue".
  • It could be subject to a specific colored nomenclatur, alongside Mismatch brackets (e.g. could be called with bright().cyan()) to determine a "notice" kind of messages to convey feed-back regarding to criticality to the user.

Could be extended to the point where we would land a full nomenclatur:
notice < warning < error < fatal error.

Documentation generator

There is a proof of concept implementation that extracts doc comments from modules and formats into a markdown document, but this should be extended for native modules as well.

Proper numeric tower

It would be nice to have a proper numeric tower, right now there are integers that promote to floats at overflow - but otherwise bigints are not implemented - historically this was to avoid having to box all integers.

It might be worth implementing now, and just wrap up this crate https://docs.rs/num/latest/num/

testing system and enahnce command `steel test`

I attempted to understand the inner workings of run-tests.scm here.

Today, we could consider placing a default copy of run-tests.scm in the project directory, allowing steel test to run that file as one possible option.

I believe the purpose of the steel test command is to simplify test execution for users, abstracting away the details of the testing system implementation. This implies that we would need to reimplement run-test.scm within the command line. However, this approach has a tradeoff – it makes 'steel' responsible for the testing strategy. I'm not entirely sure how other programming languages handle different testing frameworks (like JUnit in Java, Mocha and Jest in JavaScript, or HSpec, HUnit, QuickCheck... in Haskell). They either have standalone command-line interfaces or provide a test driver of some kind. I think Golang go test and Rust cargo test are very opinionated here. But I admit I'm not well-informed on this topic.

as an initial proof of concept I implemented it by replicating the behavior of run-tests.scm as follows:

rust example
fn test_files(dir: &PathBuf, paths: &mut Vec<PathBuf>) -> Result<(), Box<dyn Error>> {
    if dir.is_dir() {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                test_files(&path, paths)?;
            } else {
                if path.is_file()
                    && path
                        .file_name()
                        .map(|name| name.to_str().map_or(false, |n| n.ends_with(".scm")))
                        .unwrap_or(false)
                {
                    paths.push(path);
                }
            }
        }
    }
    Ok(())
}

fn run_folder_tests(project_root: &PathBuf) -> Result<(), Box<dyn Error>> {
    let mut vm = Engine::new();
    vm.register_value("std::env::args", steel::SteelVal::ListV(vec![].into()));
    vm.run("(set-test-mode!)")?;

    let mut files = Vec::new();
    if project_root.is_file() {
        files.push(project_root.clone());
    } else {
        test_files(project_root, &mut files)?;
    }

    for file in &files {
        let contents = fs::read_to_string(&file)?;
        if let Err(e) = vm.compile_and_run_raw_program_with_path(&contents, file.clone()) {
            e.emit_result(file.to_str().unwrap(), &contents);
            return Err(Box::new(e));
        }
    }
    let get_test_stats = r#"(require "steel/tests/unit-test.scm") (get-test-stats)"#;
    if let Err(e) = vm.run(get_test_stats) {
        e.emit_result(project_root.to_str().unwrap(), &get_test_stats);
        return Err(Box::new(e));
    }
    Ok(())
}

I had to reimplement the 'walk-dir' function and make some adjustments to the execution path, but it appears to be working well.

I'm super open to your feedback and thoughts. Please let me know if there are alternative approaches or if the current implementation is satisfactory, and I can proceed with opening a pull request.

We could also improve by ignoring hidden files and patterns from .gitignore, but that's not our top priority for now.

Escaping backquotes

Is escaping backquotes done this way, or is it still unimplemented?

54 │       [else '(you can\'t go that way.)])))
   │                      ^ Parse: Unexpected character: '\\'

Spec feedback

Thanks for your spec. This is a good start, and I like where you’re going with it. I have just a few thoughts/suggestions/questions:

  • I’d like to see a bit of API sketch in the spec, or maybe you could point me to what you have. You probably needs not just marshaling traits, but a type for the interpreter itself and types for handling Scheme values on the Rust side.

  • What is “Partial Tail-call Optimization”? Why not full TCO?

  • In addition to wrapping Rust functions as Scheme functions, you probably need to be able to wrap other Rust values as well. It should possible to make it easy to turn, say, a mutex into a Scheme value, along with a new predicate that recognizes it.

  • Have you thought about reentrancy? For example, if you call into Scheme and the from Scheme you call a Rust function, can that Rust function then call back into Scheme (and so on)? What happens when you have an error on the Scheme side? (Maybe you want a method for entering Scheme that returns a Result<SchemeValue, SchemeError> back to Rust.) The harder question is what happens if Rust code called from Scheme code panics. Does the Scheme context get to see that?

  • Instead of having records defined in Scheme, it might make more sense to make it easy to embed Rust structs. Maybe the programmer could write,

    #[derive(Scheme)]
    struct Wib {
        xam: u32,
        sqo: String,
    }

    and that would not only derive the trait for turning a Wib into a Scheme value and back, but also Scheme functions: a constructor wib, a predicate wib?, getters wib-xam and wib-sqo, and setters set-wib-xam! and set-wib-sqo!.

  • Your public API may need a notion of a Scheme environment that can be manipulated from Rust.

  • Have you thought about whether it’s possible to include borrowed values in Scheme values? Can you check lifetimes dynamically? (Yes, sort of.)

`cargo run -- install.scm` fails with `os error 2`: no such file or directory

currently nix build and nix shell does not work.

Running either command errors with

> error: package `clap_builder v4.5.2` cannot be built because it requires rustc 1.74 or newer, while the currently active rustc version is 1.72.1
> Either upgrade to rustc 1.74 or newer, or use
> cargo update -p [email protected] --precise ver
> where `ver` is the latest version of `clap_builder` supporting rustc 1.72.1

This is easily fixable by updating flake.lock (i.e. calling nix flake update).

After updating, nix build and nix shell now fails with a SteelErr with message

"Struct getter expected Ok, found Gc(UserDefinedStruct { fields: [Error: Io: No such file or directory (os error 2)], type_descriptor: StructTypeDescriptor(1) }), (Err Error: Io: No such file or directory (os error 2))"
full output
@nix { "action": "setPhase", "phase": "unpackPhase" }
Running phase: unpackPhase
unpacking source archive /nix/store/ry52wmz97b9il1ixg60gk1zgmjr5pqfw-source
source root is source
Executing cargoSetupPostUnpackHook
Finished cargoSetupPostUnpackHook
@nix { "action": "setPhase", "phase": "patchPhase" }
Running phase: patchPhase
Executing cargoSetupPostPatchHook
Validating consistency between /build/source/Cargo.lock and /build/cargo-vendor-dir/Cargo.lock
Finished cargoSetupPostPatchHook
@nix { "action": "setPhase", "phase": "updateAutotoolsGnuConfigScriptsPhase" }
Running phase: updateAutotoolsGnuConfigScriptsPhase
@nix { "action": "setPhase", "phase": "configurePhase" }
Running phase: configurePhase
@nix { "action": "setPhase", "phase": "buildPhase" }
Running phase: buildPhase
Executing cargoBuildHook
++ env CC_X86_64_UNKNOWN_LINUX_GNU=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/cc CXX_X86_64_UNKNOWN_LINUX_GNU=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/c++ CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/cc CC_X86_64_UNKNOWN_LINUX_GNU=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/cc CXX_X86_64_UNKNOWN_LINUX_GNU=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/c++ CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/cc CARGO_BUILD_TARGET=x86_64-unknown-linux-gnu HOST_CC=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/cc HOST_CXX=/nix/store/qhpw32pz39y6i30b3vrbw5fw6zv5549f-gcc-wrapper-13.2.0/bin/c++ cargo build -j 8 --target x86_64-unknown-linux-gnu --frozen --profile release
�[1m�[32m   Compiling�[0m autocfg v1.1.0
�[1m�[32m   Compiling�[0m proc-macro2 v1.0.78
�[1m�[32m   Compiling�[0m unicode-ident v1.0.12
�[1m�[32m   Compiling�[0m libc v0.2.153
�[1m�[32m   Compiling�[0m cfg-if v1.0.0
�[1m�[32m   Compiling�[0m smallvec v1.13.1
�[1m�[32m   Compiling�[0m serde v1.0.197
�[1m�[32m   Compiling�[0m parking_lot_core v0.9.9
�[1m�[32m   Compiling�[0m scopeguard v1.2.0
�[1m�[32m   Compiling�[0m crossbeam-utils v0.8.19
�[1m�[32m   Compiling�[0m version_check v0.9.4
�[1m�[32m   Compiling�[0m log v0.4.21
�[1m�[32m   Compiling�[0m once_cell v1.19.0
�[1m�[32m   Compiling�[0m syn v1.0.109
�[1m�[32m   Compiling�[0m indexmap v1.9.3
�[1m�[32m   Compiling�[0m lock_api v0.4.11
�[1m�[32m   Compiling�[0m num-traits v0.2.18
�[1m�[32m   Compiling�[0m cc v1.0.90
�[1m�[32m   Compiling�[0m signal-hook v0.3.17
�[1m�[32m   Compiling�[0m ahash v0.8.11
�[1m�[32m   Compiling�[0m num-bigint v0.4.4
�[1m�[32m   Compiling�[0m quote v1.0.35
�[1m�[32m   Compiling�[0m hashbrown v0.12.3
�[1m�[32m   Compiling�[0m typenum v1.17.0
�[1m�[32m   Compiling�[0m syn v2.0.52
�[1m�[32m   Compiling�[0m core_extensions v1.5.3
�[1m�[32m   Compiling�[0m unicode-width v0.1.11
�[1m�[32m   Compiling�[0m rustix v0.38.31
�[1m�[32m   Compiling�[0m bitflags v1.3.2
�[1m�[32m   Compiling�[0m getrandom v0.2.12
�[1m�[32m   Compiling�[0m num-integer v0.1.46
�[1m�[32m   Compiling�[0m parking_lot v0.12.1
�[1m�[32m   Compiling�[0m signal-hook-registry v1.4.1
�[1m�[32m   Compiling�[0m rand_core v0.6.4
�[1m�[32m   Compiling�[0m mio v0.8.11
�[1m�[32m   Compiling�[0m crossbeam-channel v0.5.12
�[1m�[32m   Compiling�[0m slab v0.4.9
�[1m�[32m   Compiling�[0m num-rational v0.4.1
�[1m�[32m   Compiling�[0m zerocopy v0.7.32
�[1m�[32m   Compiling�[0m core_extensions_proc_macros v1.5.3
�[1m�[32m   Compiling�[0m tstr_proc_macros v0.2.2
�[1m�[32m   Compiling�[0m linux-raw-sys v0.4.13
�[1m�[32m   Compiling�[0m paste v1.0.14
�[1m�[32m   Compiling�[0m bitflags v2.4.2
�[1m�[32m   Compiling�[0m serde_json v1.0.114
�[1m�[32m   Compiling�[0m tstr v0.2.3
�[1m�[32m   Compiling�[0m signal-hook-mio v0.2.3
�[1m�[32m   Compiling�[0m psm v0.1.21
�[1m�[32m   Compiling�[0m codegen v0.2.0
�[1m�[32m   Compiling�[0m abi_stable_shared v0.11.0
�[1m�[32m   Compiling�[0m crossbeam-epoch v0.9.18
�[1m�[32m   Compiling�[0m typed-arena v2.0.2
�[1m�[32m   Compiling�[0m hashbrown v0.14.3
�[1m�[32m   Compiling�[0m thiserror v1.0.57
�[1m�[32m   Compiling�[0m memchr v2.7.1
�[1m�[32m   Compiling�[0m itoa v1.0.10
�[1m�[32m   Compiling�[0m ryu v1.0.17
�[1m�[32m   Compiling�[0m utf8parse v0.2.1
�[1m�[32m   Compiling�[0m bitmaps v2.1.0
�[1m�[32m   Compiling�[0m crossbeam-deque v0.8.5
�[1m�[32m   Compiling�[0m crossterm v0.23.2
�[1m�[32m   Compiling�[0m repr_offset v0.2.2
�[1m�[32m   Compiling�[0m dashmap v5.5.3
�[1m�[32m   Compiling�[0m as_derive_utils v0.11.0
�[1m�[32m   Compiling�[0m num-iter v0.1.44
�[1m�[32m   Compiling�[0m stacker v0.1.15
�[1m�[32m   Compiling�[0m crossbeam-queue v0.3.11
�[1m�[32m   Compiling�[0m im-rc v15.1.0
�[1m�[32m   Compiling�[0m generational-arena v0.2.9
�[1m�[32m   Compiling�[0m libloading v0.7.4
�[1m�[32m   Compiling�[0m termcolor v1.4.1
�[1m�[32m   Compiling�[0m byteorder v1.5.0
�[1m�[32m   Compiling�[0m option-ext v0.2.0
�[1m�[32m   Compiling�[0m const_panic v0.2.8
�[1m�[32m   Compiling�[0m futures-task v0.3.30
�[1m�[32m   Compiling�[0m pin-utils v0.1.0
�[1m�[32m   Compiling�[0m ppv-lite86 v0.2.17
�[1m�[32m   Compiling�[0m arrayvec v0.5.2
�[1m�[32m   Compiling�[0m strsim v0.11.0
�[1m�[32m   Compiling�[0m futures-core v0.3.30
�[1m�[32m   Compiling�[0m pin-project-lite v0.2.13
�[1m�[32m   Compiling�[0m fxhash v0.2.1
�[1m�[32m   Compiling�[0m dirs-sys v0.4.1
�[1m�[32m   Compiling�[0m pretty v0.12.3
�[1m�[32m   Compiling�[0m crossbeam v0.8.4
�[1m�[32m   Compiling�[0m rand_chacha v0.3.1
�[1m�[32m   Compiling�[0m coolor v0.5.0
�[1m�[32m   Compiling�[0m sized-chunks v0.6.5
�[1m�[32m   Compiling�[0m serde_derive v1.0.197
�[1m�[32m   Compiling�[0m thiserror-impl v1.0.57
�[1m�[32m   Compiling�[0m futures-macro v0.3.30
�[1m�[32m   Compiling�[0m rand_xoshiro v0.6.0
�[1m�[32m   Compiling�[0m minimad v0.10.0
�[1m�[32m   Compiling�[0m iana-time-zone v0.1.60
�[1m�[32m   Compiling�[0m either v1.10.0
�[1m�[32m   Compiling�[0m home v0.5.9
�[1m�[32m   Compiling�[0m chrono v0.4.35
�[1m�[32m   Compiling�[0m which v4.4.2
�[1m�[32m   Compiling�[0m futures-util v0.3.30
�[1m�[32m   Compiling�[0m rand v0.8.5
�[1m�[32m   Compiling�[0m termimad v0.21.1
�[1m�[32m   Compiling�[0m steel-derive v0.5.0 (/build/source/crates/steel-derive)
�[1m�[32m   Compiling�[0m abi_stable_derive v0.11.3
�[1m�[32m   Compiling�[0m dirs v5.0.1
�[1m�[32m   Compiling�[0m codespan-reporting v0.11.1
�[1m�[32m   Compiling�[0m aho-corasick v1.1.2
�[1m�[32m   Compiling�[0m anstyle-parse v0.2.3
�[1m�[32m   Compiling�[0m quickscope v0.2.0
�[1m�[32m   Compiling�[0m futures-executor v0.3.30
�[1m�[32m   Compiling�[0m dirs-sys-next v0.1.2
�[1m�[32m   Compiling�[0m im-lists v0.8.0
�[1m�[32m   Compiling�[0m nibble_vec v0.1.0
�[1m�[32m   Compiling�[0m anstyle-query v1.0.2
�[1m�[32m   Compiling�[0m radix_fmt v1.0.0
�[1m�[32m   Compiling�[0m weak-table v0.3.2
�[1m�[32m   Compiling�[0m anstyle v1.0.6
�[1m�[32m   Compiling�[0m endian-type v0.1.2
�[1m�[32m   Compiling�[0m regex-syntax v0.8.2
�[1m�[32m   Compiling�[0m colorchoice v1.0.0
�[1m�[32m   Compiling�[0m anstream v0.6.13
�[1m�[32m   Compiling�[0m radix_trie v0.2.1
�[1m�[32m   Compiling�[0m dirs-next v2.0.0
�[1m�[32m   Compiling�[0m fd-lock v3.0.13
�[1m�[32m   Compiling�[0m nix v0.25.1
�[1m�[32m   Compiling�[0m lazy_static v1.4.0
�[1m�[32m   Compiling�[0m clap_lex v0.7.0
�[1m�[32m   Compiling�[0m unicode-segmentation v1.11.0
�[1m�[32m   Compiling�[0m heck v0.4.1
�[1m�[32m   Compiling�[0m clap_derive v4.5.0
�[1m�[32m   Compiling�[0m regex-automata v0.4.6
�[1m�[32m   Compiling�[0m rustyline v10.1.1
�[1m�[32m   Compiling�[0m clap_builder v4.5.2
�[1m�[32m   Compiling�[0m steel-gen v0.2.0 (/build/source/crates/steel-gen)
�[1m�[32m   Compiling�[0m regex v1.10.3
�[1m�[32m   Compiling�[0m num-complex v0.4.5
�[1m�[32m   Compiling�[0m hashbrown v0.13.2
�[1m�[32m   Compiling�[0m steel-core v0.6.0 (/build/source/crates/steel-core)
�[1m�[32m   Compiling�[0m bincode v1.3.3
�[1m�[32m   Compiling�[0m lasso v0.7.2
�[1m�[32m   Compiling�[0m abi_stable v0.11.3
�[1m�[32m   Compiling�[0m num v0.4.1
�[1m�[32m   Compiling�[0m colored v2.1.0
�[1m�[32m   Compiling�[0m steel-parser v0.6.0 (/build/source/crates/steel-parser)
�[1m�[32m   Compiling�[0m rustyline-derive v0.7.0
�[1m�[32m   Compiling�[0m is-terminal v0.4.12
�[1m�[32m   Compiling�[0m humantime v2.1.0
�[1m�[32m   Compiling�[0m clap v4.5.2
�[1m�[32m   Compiling�[0m env_logger v0.10.2
�[1m�[32m   Compiling�[0m async-ffi v0.5.0
�[1m�[32m   Compiling�[0m steel-repl v0.6.0 (/build/source/crates/steel-repl)
�[1m�[32m   Compiling�[0m steel-doc v0.6.0 (/build/source/crates/steel-doc)
�[1m�[32m   Compiling�[0m steel-interpreter v0.6.0 (/build/source)
�[1m�[32m    Finished�[0m release [optimized] target(s) in 3m 49s
Executing cargoInstallPostBuildHook
Finished cargoInstallPostBuildHook
Finished cargoBuildHook
buildPhase completed in 3 minutes 50 seconds
@nix { "action": "setPhase", "phase": "installPhase" }
Running phase: installPhase
Executing cargoInstallHook
/build/source/cogs /build/source
cogs directory does not exist, creating now...
Package is not currently installed.
=> Installing:  '#hash((package-name . steel/lists) (path . "/build/source/cogs/lists") (version . "0.1.0") (dependencies . ())) 
=> Copied package over to:  /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/lists 
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/lists

Package is not currently installed.
=> Installing: '#hash((path . "/build/source/cogs/command-line") (version . "0.1.0") (package-name . steel/command-line) (dependencies . ()))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/command-line
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/command-line

Package is not currently installed.
=> Installing: '#hash((package-name . steel/sorting) (version . "0.1.0") (dependencies . ()) (path . "/build/source/cogs/sorting"))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/sorting
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/sorting

Package is not currently installed.
=> Installing: '#hash((dependencies . ()) (package-name . steel/collections) (path . "/build/source/cogs/collections") (version . "0.1.0"))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/collections
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/collections

Package is not currently installed.
=> Installing: '#hash((package-name . steel/tests) (dependencies . ()) (version . "0.1.0") (path . "/build/source/cogs/tests"))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/tests
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/tests

Package is not currently installed.
=> Installing: '#hash((version . "0.1.0") (path . "/build/source/cogs/srfi") (package-name . srfi) (dependencies . ()))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/srfi
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/srfi

Package is not currently installed.
=> Installing: '#hash((dependencies . ()) (package-name . steel/fs) (version . "0.1.0") (path . "/build/source/cogs/fs"))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/fs
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/fs

Package is not currently installed.
=> Installing: '#hash((version . "0.1.0") (package-name . steel/time) (path . "/build/source/cogs/time") (dependencies . ()))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/time
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel/time

Package is not currently installed.
=> Installing: '#hash((path . "/build/source/cogs/installer") (dependencies . ()) (version . "0.1.0") (package-name . installer))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/installer
✅ Installed package to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/installer

Package is not currently installed.
=> Installing: '#hash((path . "/build/source/cogs/slack") (version . "0.1.0") (dependencies . ('#hash((#:path . "../libs/steel-websockets") (#:name . steel-websockets)) '#hash((#:path . "../libs/steel-webrequests") (#:name . steel-webrequests)))) (package-name . slack/websocket))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/slack/websocket
=> Installing: '#hash((version . "0.1.0") (package-name . steel-websockets) (dependencies . ()) (path . "../libs/steel-websockets") (dylibs . ('#hash((#:name . "steel-websockets") (#:workspace-root . "../..") (#:subdir . "libs/steel-websockets")))))
=> Copied package over to: /nix/store/lh4y0sjnnqqmwmrzn91lbhfqim1md3ap-steel-interpreter-0.6.0/lib/cogs/steel-websockets
Attempting to install: '#hash((#:name . "steel-websockets") (#:workspace-root . "../..") (#:subdir . "libs/steel-websockets"))
Running dylib build in: ../libs/steel-websockets/../../libs/steel-websockets
�[0m�[1m�[38;5;9merror[E03]�[0m�[1m: TypeMismatch�[0m
�[0m�[34m┌─�[0m install.scm:11:1
�[0m�[34m│�[0m
�[0m�[34m11�[0m �[0m�[34m│�[0m
�[0m�[34m│�[0m �[0m�[31mStruct getter expected Ok, found Gc(UserDefinedStruct { fields: [Error: Io: No such file or directory (os error 2)], type_descriptor: StructTypeDescriptor(1) }), (Err Error: Io: No such file or directory (os error 2))�[0m

Error: SteelErr { repr: Repr { kind: TypeMismatch, message: "Struct getter expected Ok, found Gc(UserDefinedStruct { fields: [Error: Io: No such file or directory (os error 2)], type_descriptor: StructTypeDescriptor(1) }), (Err Error: Io: No such file or directory (os error 2))", span: Some(2535..2544), stack_trace: Some(DehydratedStackTrace { stack_trace: [DehydratedCallContext { span: Some(0..0) }, DehydratedCallContext { span: Some(0..0) }, DehydratedCallContext { span: None }, DehydratedCallContext { span: Some(0..0) }, DehydratedCallContext { span: Some(2161..2172) }, DehydratedCallContext { span: Some(0..0) }, DehydratedCallContext { span: Some(1555..1563) }, DehydratedCallContext { span: Some(0..0) }, DehydratedCallContext { span: Some(2161..2172) }, DehydratedCallContext { span: Some(1555..1563) }, DehydratedCallContext { span: Some(2182..2186) }, DehydratedCallContext { span: Some(2426..2452) }] }) } }
/nix/store/v099hqvw5z87423p4hz1vfhzaqa07dii-stdenv-linux/setup: line 131: pop_var_context: head of shell_variables not a function context

PS: the popd in line 41 in flake.nix seems to throw an error too, see the last line of the log

EDIT: I investigated this further. This is not a nix flake error! Instead this seems to originate from running cargo run -- install.scm from contributing.scm too! To me it looks like steel-websockets or steel-regex fails to install (depending on which machine I use)

[feature] self-contained steel program executables

This is a great feature to have! Many scheme implementations do not have this.

This feature makes it easy to deploy steel apps.

Basically, The approach could be steel runtime + script + dependencies -> ELF binary

I am open to more thoughts on this!

get started with steel for curious

Hello there!

I'm really curious about learning more about steel. I've already begun reading the code, though I'm not sure if I'm good enough to contribute.

For now, my main focus is to grasp the overall structure and objectives of the project.

I've started build the project and run tests, but I'm open to guidance and feedback to ensure I'm on the right path.

cargo run works perfectly and displays the REPL
but in order to run tests i spied the CI: https://github.com/mattwparas/steel/blob/master/.github/workflows/rust.yml

so i do this:

# run-tests.sh
cd cogs
export STEEL_HOME=$HOME/.steel
echo $STEEL_HOME
mkdir -p $STEEL_HOME
cargo run -- install.scm
cd ..
cargo test --all

Everything seems to be working fine. However, I noticed some errors in the logs:
image

Did I miss something or forget to do anything specific?

thanks you!

Module system

Add as special forms a way to provide and require files from a script

For example:

;; sort.scm
(provide sort)
(define (sort lst) ...)
(require "sort.scm")
(sort (list 6 3 5 4 1 2)) ;; '(1 2 3 4 5 6)

Only functions provided by one file should be available in a file that requires them.

Support path expansion

I would like to be able to specify paths starting from ~ for example: ~/.local/ and it'll expand to /home/user/.local
This could be useful for interacting with other CLI tools or saving some state/data to a specific folder in the user directory.

Maybe the usage could be something like this(sorry my Scheme is nonexistant)

(expand-path "~/.local/state/helix") ;; evals to "/home/user/.local/state/helix" 

Document the process/command API

See here for the module

It is a little opaque - the functions that return Result return a struct to rust that is Ok or Err, which makes it non obvious how to use. On top of that, some of the functions are builders, and some act via mutation. First is to probably add some documentation to these functions, and then after that we should implement a module in the cogs directory that wraps up some of the more low level functions into something a bit more idiomatic.

For example, capturing std out into a string is a little non obvious:

(define (with-stdout-piped command)
  (set-piped-stdout! command)
  command)

(~> (command "git" (list "check-ignore" "target"))
    (with-stdout-piped)
    (spawn-process)
    (Ok->value)
    (wait->stdout)
    (Ok->value))

`register_value_with_doc` doesn't utilize the doc part

I was looking at the doc and how the doc are generated.

It appears that various built-in functions are missing in the generated documentation (the book) and the built-in (help indent), such as cons, reverse...

All these are missing, (help cons) doesn't print CONS_DOC

.register_value_with_doc("cdr", crate::primitives::lists::CDR, CDR_DOC)
.register_value_with_doc("rest", crate::primitives::lists::REST, REST_DOC)
.register_value_with_doc("append", crate::primitives::lists::APPEND, APPEND_DOC)
.register_value_with_doc("reverse", crate::primitives::lists::REVERSE, REVERSE_DOC)

Add support for pairwise rest args in syntax rules

The syntax-rules implementation is not done in scheme, but instead is done natively in Rust. There is quite a bit of room for improvement here. For example, the ability to deconstruct pairs on patterns is not supported, like so for this let implementation https://www.scheme.com/tspl2d/syntax.html:

(define-syntax let*
  (syntax-rules ()
    ((_ () e1 e2 ...) (let () e1 e2 ...))
    ((_ ((i1 v1) (i2 v2) ...) e1 e2 ...)
     (let ((i1 v1))
       (let* ((i2 v2) ...) e1 e2 ...)))))

The issue here is the (i2 v2) ... - The current implementation only supports ... on single values, not on a pair.

interpreter panic

found a panic in the interpreter while playing around, and minimized it to this sequence:

(define (x) (- x 2)) ; define a weird function
(x)                  ; function is called (TypeMismatch reported)
(define (x) (- x 2)) ; panic when redefining the function

run with ./target/release/steel < crash.scm

thread 'main' panicked at 'range end index 9 out of range for slice of length 5', crates/steel-core/src/steel_vm/vm.rs:2349:17
stack backtrace:
   0: rust_begin_unwind
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/panicking.rs:578:5
   1: core::panicking::panic_fmt
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/panicking.rs:67:14
   2: core::slice::index::slice_end_index_len_fail_rt
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/slice/index.rs:78:5
   3: core::slice::index::slice_end_index_len_fail
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/slice/index.rs:70:9
   4: steel::steel_vm::vm::VmCore::handle_pure_function
   5: steel::steel_vm::vm::VmCore::vm
             at ./crates/steel-core/src/steel_vm/vm.rs:1993:22
   6: steel::steel_vm::vm::SteelThread::execute
             at ./crates/steel-core/src/steel_vm/vm.rs:457:26
   7: steel::steel_vm::vm::SteelThread::run_executable::{{closure}}
             at ./crates/steel-core/src/steel_vm/vm.rs:361:22
   8: core::iter::adapters::map::map_try_fold::{{closure}}
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/adapters/map.rs:91:28
   9: core::iter::traits::iterator::Iterator::try_fold
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/traits/iterator.rs:2304:21
  10: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/adapters/map.rs:117:9
  11: <core::iter::adapters::GenericShunt<I,R> as core::iter::traits::iterator::Iterator>::try_fold
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/adapters/mod.rs:195:9
  12: core::iter::traits::iterator::Iterator::try_for_each
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/traits/iterator.rs:2366:9
  13: <core::iter::adapters::GenericShunt<I,R> as core::iter::traits::iterator::Iterator>::next
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/adapters/mod.rs:178:14
  14: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter_nested::SpecFromIterNested<T,I>>::from_iter
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/alloc/src/vec/spec_from_iter_nested.rs:26:32
  15: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/alloc/src/vec/spec_from_iter.rs:33:9
  16: <alloc::vec::Vec<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/alloc/src/vec/mod.rs:2712:9
  17: core::iter::traits::iterator::Iterator::collect
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/traits/iterator.rs:1896:9
  18: <core::result::Result<V,E> as core::iter::traits::collect::FromIterator<core::result::Result<A,E>>>::from_iter::{{closure}}
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/result.rs:1969:51
  19: core::iter::adapters::try_process
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/adapters/mod.rs:164:17
  20: <core::result::Result<V,E> as core::iter::traits::collect::FromIterator<core::result::Result<A,E>>>::from_iter
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/result.rs:1969:9
  21: core::iter::traits::iterator::Iterator::collect
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/iter/traits/iterator.rs:1896:9
  22: steel::steel_vm::vm::SteelThread::run_executable
             at ./crates/steel-core/src/steel_vm/vm.rs:362:14
  23: steel::steel_vm::engine::Engine::run_raw_program
             at ./crates/steel-core/src/steel_vm/engine.rs:1051:9
  24: steel::steel_vm::engine::Engine::run_raw_program_from_exprs
             at ./crates/steel-core/src/steel_vm/engine.rs:1015:9
  25: steel::parser::kernel::Kernel::load_program_for_comptime
             at ./crates/steel-core/src/parser/kernel.rs:147:9
  26: steel::compiler::compiler::Compiler::apply_const_evaluation
             at ./crates/steel-core/src/compiler/compiler.rs:685:17
  27: steel::compiler::compiler::Compiler::compile_raw_program
             at ./crates/steel-core/src/compiler/compiler.rs:642:13
  28: steel::compiler::compiler::Compiler::compile_executable
             at ./crates/steel-core/src/compiler/compiler.rs:391:9
  29: steel::steel_vm::engine::Engine::compile_and_run_raw_program
             at ./crates/steel-core/src/steel_vm/engine.rs:1020:23
  30: steel_repl::repl::finish_or_interrupt
             at ./crates/steel-repl/src/repl.rs:54:15
  31: steel_repl::repl::repl_base
             at ./crates/steel-repl/src/repl.rs:176:25
  32: steel_interpreter::run
             at ./src/lib.rs:59:13
  33: steel::main
             at ./src/main.rs:21:5
  34: core::ops::function::FnOnce::call_once
             at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/ops/function.rs:250:5

( I set debug = true for this build so line numbers are present)

Installation error

I want to install to try helix with steel. I successfully installed your helix fork. But as I understand to config I need steel. So I couldn't install steel through flake.nix.

error: builder for '/nix/store/fwshviria0fbbhbmmw27gsdh35df3qil-steel-interpreter-0.6.0.drv' failed with exit code 1;
       last 10 log lines:
       > Attempting to install:  '#hash((#:name . "steel-websockets") (#:subdir . "libs/steel-websockets") (#:workspace-root . "../.."))
       > Running dylib build in:  ../libs/steel-websockets/../../libs/steel-websockets
       > error[E03]: TypeMismatch
       >    ┌─ install.scm:11:1
       >    │
       > 11 │
       >    │   Struct getter expected Ok, found Gc(UserDefinedStruct { fields: [Error: Io: No such file or directory (os error 2)], type_descriptor: StructTypeDescriptor(1) }), (Err Error: Io: No such file or direc
tory (os error 2))
       >
       > Error: SteelErr { repr: Repr { kind: TypeMismatch, message: "Struct getter expected Ok, found Gc(UserDefinedStruct { fields: [Error: Io: No such file or directory (os error 2)], type_descriptor: StructTypeD
escriptor(1) }), (Err Error: Io: No such file or directory (os error 2))", span: Some(2535..2544), stack_trace: Some(DehydratedStackTrace { stack_trace: [DehydratedCallContext { span: Some(0..0) }, DehydratedCallCon
text { span: Some(7212..7218) }, DehydratedCallContext { span: None }, DehydratedCallContext { span: Some(7212..7218) }, DehydratedCallContext { span: Some(2161..2172) }, DehydratedCallContext { span: Some(0..0) }, 
DehydratedCallContext { span: Some(1555..1563) }, DehydratedCallContext { span: Some(7212..7218) }, DehydratedCallContext { span: Some(2161..2172) }, DehydratedCallContext { span: Some(1555..1563) }, DehydratedCallC
ontext { span: Some(2182..2186) }, DehydratedCallContext { span: Some(2426..2452) }] }) } }
       > /nix/store/v5irq7wvkr7kih0hhnch5nnv2dcq8c4f-stdenv-linux/setup: line 131: pop_var_context: head of shell_variables not a function context
       For full logs, run 'nix log /nix/store/fwshviria0fbbhbmmw27gsdh35df3qil-steel-interpreter-0.6.0.drv'.

Ports

Have native IO functions via ports, like so:

  • open-input-file
  • open-output-file
  • close-input-port
  • close-output-port
  • input-port?
  • output-port?
  • current-input-port
  • current-output-port
  • call-with-input-file
  • call-with-output-file
  • with-input-from-file(optional)
  • with-output-to-file(optional)

Deregister exeternal value

There are some (unsafe) values that I need to insert into the engine that have the lifetime of a single script execution. In order to maintain my invariants, I would like to be able to deregister an external value, so that I can remove it from the engine after my script completes.

Successful redefinition of functions is inconsistent.

Affected target

steel-interpreter REPL

Problem

Redefinition of any Steel function, or standard functions such as Ok->value replaces the function definition.
Redefining + or * does nothing.

Expected Behavior

Either:

  1. Error thrown when disallowed redefinition
  2. Allow redefinition

add steel-language-server to nix flake

I just learned that there is a language server for steel. This is so great!

However, it would be even nicer if this language server would be installable using nix flakes. If this is of interest for this project, I could take care of it.

`panic!` when a rational number's denominator is exact 0

As of right now in steel, 1/0 will panic!

λ > 1/0

In racket, 1/0 is a syntax error.

The panic appears to be in the num package.

I think here should throw a bad syntax error like in racket too, instead of letting num to panic.

(Ok(n), Ok(d)) => Rational32::new(n, d).into_steelval(),

https://github.com/rust-num/num-rational/blob/e580ced0602ae8a5e06a8dea8623d70be8c5f3bc/src/lib.rs#L129-L135

Note that in racket, (/ 1 0) is a runtime error while (/ 1 0.0) is inf.

In steel, both (/ 1 0) and (/ 1 0.0) is inf.

Distinguish integers versus floats

For example 1.0 should be parsed as a float, whereas 1 should be parsed as an integer.

Operations between the two should then default to use a float

Implement a macro expansion step

Currently the process is as follows:

  • Tokenize
  • Parse
  • Evaluate

Ideally the scheme would allow extension of syntax by the implementation of a macro system. This would add a step in between parsing and evaluating, known as the "expansion" phase.

This phase could also be added inside the evaluation step. Macros could be identified and expanded just in time at evaluation time.

The expansion phase would expand macros into primitive operations. For example:

(and a b c)

Would translate to:

(if a
    (if b 
        c 
        #f)
    #f)

Other built in macros that should exist:

  • unless
  • and
  • or
  • cond

Issues building steel inside docker

Using rust:slim as base img

At the following Dockerfile steps:

RUN mkdir -p /lib/steel && \
export STEEL_HOME="/lib/steel"

RUN cargo build

RUN target/debug/steel cogs/install.scm

I get this below err:

error[E11]: Generic
   ┌─ cogs/install.scm:11:1
   │
11 │ 
   │   Error: Generic: environment variable not found

Error: SteelErr { repr: Repr { kind: Generic, message: "Error: Generic: environment v
ariable not found", span: Some(1085..1092), stack_trace: Some(DehydratedStackTrace { 
stack_trace: [] }) } }
The command '/bin/sh -c ls target/** && target/debug/steel cogs/install.scm' returned
 a non-zero code: 1

Did i miss setting any ENV ?

filter does not consider lists as true

In scheme list values are accepted as true in boolean context. For example:

λ > (if (list 1 2) 1 2)
=> 1

But, filter only accepts #true as true value:

λ > (filter (lambda (n) (list 1 2)) (list 1 2))
=> '()

The result of the last expression should be:

λ > (filter (lambda (n) (list 1 2)) (list 1 2))
=> '(1 2)

A workaround is to use if explicitly:

λ > (filter (lambda (n) (if (list 1 2) #true #false)) (list 1 2))
=> '(1 2)

I have tested with commit 881262e,
on arch linux with rust version "rustc 1.74.0 (79e9716c9 2023-11-13) (Arch Linux rust 1:1.74.0-1)"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.