Coder Social home page Coder Social logo

mattwparas / steel Goto Github PK

View Code? Open in Web Editor NEW
899.0 13.0 40.0 6.08 MB

An embedded scheme interpreter in Rust

License: Apache License 2.0

Rust 78.07% Racket 4.43% Scheme 17.33% Lua 0.02% Python 0.06% Shell 0.01% Nix 0.08% Dockerfile 0.01%
rust racket transducers scripting-language contracts programming-language

steel's Issues

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.

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.

WebAssembly

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

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.

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.

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)"

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

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/

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

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.

(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

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)

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

`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)

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 ?

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.

`integer?` behavior

Hello, I've noticed a behaviour incompatible with Scheme and I'm not sure whether it's intended or not:

steel> (integer? 5.0)
=> #f

racket> (integer? 5.0)
=> #t

Hence exact-integer? makes no sense anymore

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.

Escaping backquotes

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

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

[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!

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))

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.)

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.

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)

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

`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)

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'.

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" 

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.

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!

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?

`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.

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.