-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Deploying to gh-pages from @ 61f53a7 🚀
- Loading branch information
Showing
9 changed files
with
1,746 additions
and
16 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
# R | ||
|
||
_An experimental implementation of R, with embellishments_ | ||
|
||
Check out the [live demo](https://dgkf.github.io/R/) | ||
|
||
## What can it do? | ||
|
||
```sh | ||
cargo run | ||
``` | ||
```r | ||
# R version 0.3.1 -- "Art Smock" | ||
|
||
x <- function(a = 1, ...) { a + c(...) } | ||
# function(a = 1, ...) { | ||
# a + c(...) | ||
# } | ||
# <environment 0x6000005b8e28> | ||
|
||
y <- function(...) x(...) | ||
# function(...) x(...) | ||
# <environment 0x6000005b8e28> | ||
|
||
y(4, 3, 2, 1) | ||
# [1] 7 6 5 | ||
``` | ||
|
||
This amounts to (most) of R's grammar parsing, basic primitives, scope | ||
management and ellipsis argument passing. | ||
|
||
## What's different? | ||
|
||
This project is not just a rewrite of R, but a playground for features and | ||
reinterpretations. It is not meant to reimplement a compatible R layer, but | ||
to rethink some of R's assumptions. | ||
|
||
### Syntax | ||
|
||
To start, there are a few superficial changes: | ||
|
||
```r | ||
# 'fn' keyword | ||
f <- fn(a, b, c) { | ||
a + b + c | ||
} | ||
|
||
# vector syntax | ||
v <- [1, 2, 3, 4] | ||
|
||
# list syntax | ||
l <- (a = 1, b = 2, c = 3) | ||
|
||
# lowercase keywords | ||
kws <- (na, null, inf, true, false) | ||
|
||
# destructuring assignment | ||
(a, b) <- (1, 2) | ||
``` | ||
|
||
There are plenty of more substantial [changes being considered](https://github.com/dgkf/R/issues?q=is%3Aissue+is%3Aopen+label%3Ameta-proposal). | ||
If you enjoy mulling over the direction of syntax and features, feel | ||
free to join the conversation. | ||
|
||
### Experiments | ||
|
||
All experiments are feature-gated and enabled by running (or building) with | ||
|
||
```sh | ||
cargo run -- --experiments "<experiment>" | ||
``` | ||
|
||
Please try them out and share your thoughts in the corresponding issues! | ||
|
||
#### Ellipsis packing and unpacking | ||
|
||
> [!NOTE] | ||
> `--experiments rest-args` (discussed in [#48](https://github.com/dgkf/R/issues/48), [#49](https://github.com/dgkf/R/issues/49)) | ||
Current work is focused on `..args` named ellipsis arguments and `..args` | ||
unpacking in function calls. However, due to the experimental nature of this | ||
syntax it is currently behind a feature gate. | ||
|
||
```r | ||
f <- function(..args) { | ||
args | ||
} | ||
|
||
f(1, 2, 3) # collect ellipsis args into a named variable | ||
# (1, 2, 3) | ||
``` | ||
|
||
```r | ||
args <- (a = 1, b = 2, c = 3) | ||
f <- function(a, b, c) { | ||
a + b + c | ||
} | ||
|
||
f(..args) # unpack lists into arguments | ||
# [1] 6 | ||
|
||
more_args <- (c = 10) | ||
f(..args, ..more_args) # duplicate names okay, last instance takes priority | ||
# [1] 13 | ||
``` | ||
|
||
#### Tail Recursion | ||
|
||
> [!NOTE] | ||
> `--experiments tail-calls` (discussed in [#60](https://github.com/dgkf/R/issues/60)) | ||
Tail recursion allows for arbitrarily recursive call stacks - or, more | ||
accurately, it discards frames from the call stack in this special case | ||
allowing for recursion without overflowing of the call stack. | ||
|
||
```r | ||
f <- function(n) if (n > 0) f(n - 1) else "done" | ||
f(10000) | ||
# [1] "done" | ||
``` | ||
|
||
The details of how this is achieves requires the tail call's arguments to be | ||
executed _eagerly_ instead of R's typical _lazy_ argument evaluation. This | ||
change can result in some unexpected behaviors that need discussion before | ||
the feature can be fully introduced. | ||
|
||
### Performance | ||
|
||
You might be thinking `rust` is fast, and therefore this project must be | ||
fast. Well, unfortunately you'd be wrong. That's probably more of a | ||
reflection on me than `rust`. To get the basic skeleton in place, | ||
my focus has been on getting things working, not on getting them working | ||
_well_. For now, expect this interpreter to be about ***1000x*** slower | ||
than R. | ||
|
||
I'm feeling good about the general structure of the internals, but there | ||
have been plenty of quick proofs of concept that involve excess copies, | ||
extra loops, panics and probably less-than-ideal data structures. | ||
If you're an optimization fiend and you want to help narrow the gap with | ||
R, your help would be very much appreciated! | ||
|
||
## Why | ||
|
||
This project is primarily a personal exploration into language design. | ||
|
||
At the outset, many of the choices are researched one-by-one and are almost | ||
certainly naive implementations. My goal is to learn and explore, and in | ||
that way the project is already a success in my eyes. Beyond advancing my own | ||
understanding of language internals, I'd love to see the project garner enough | ||
interest to become self-sustaining. | ||
|
||
If you see value in the project for anything beyond prototyping ideas, then | ||
pushing the project toward something practical is contingent on your support. | ||
Contributions, suggestions, feedback and testing are all appreciated. | ||
|
||
### Values | ||
|
||
Being primarily a one-person project, the values currently map closely to my | ||
own. Somethings I want to aim for: | ||
|
||
- A reasonably approachable language for R users (possibly with the ability to | ||
interpret R code). | ||
- Improved R constructs for complex calls, including argument packing and | ||
unpacking, partial function calls, destructuring assignment | ||
- Guardrails on non-standard-evaluation, allowing for user-facing | ||
domain-specific-languages, while allowing a more rigid evaluation scheme | ||
internally. | ||
- Lean into the things that `rust` does well, such as threading, arguably | ||
async evaluation, first-class data structures and algebraic error types. | ||
- Learn from more general languages like `TypeScript` to better understand | ||
how static typing can be comfortably embedded in a high-level language. | ||
|
||
## Contribution Guide | ||
|
||
If you also want to learn some `rust` or want to explore language design with | ||
me, I'm happy to have you along for the ride. There are plenty of ways to | ||
contribute. In order of increasing complexity, this might include: | ||
|
||
- Documenting internals | ||
- Improving documentation throughout | ||
- Helping to improve the demo page hosted on GitHub pages | ||
- Implementing new language concepts | ||
- Providing feedback on internals | ||
|
||
Any and all contributions are appreciated, and you'll earn yourself a mention | ||
in release notes! | ||
|
||
## License | ||
|
||
I welcome other contributors, but also have not thoughtfully selected a long- | ||
term license yet. For now there's a CLA in place so that the license can | ||
be altered later on. I don't intend to keep it around forever. If you have | ||
suggestions or considerations for selecting an appropriate license, your | ||
feedback would be much appreciated. | ||
|
||
My current preference is toward a copyleft license like GPL as opposed to a | ||
permissive license like MIT, as I believe that languages are a best-case | ||
candidate for such licenses and it fits well with the ethos of the R community | ||
as being scientific-community first. If you disagree strongly with that | ||
decision, now is your time to let me know. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "r", | ||
"type": "module", | ||
"collaborators": [ | ||
"Doug Kelkhoff", | ||
"Sebastian Fischer" | ||
], | ||
"description": "An experimental reimaginging of R", | ||
"version": "0.3.3", | ||
"license": "SEE LICENSE IN LICENSE.md", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/dgkf/R/" | ||
}, | ||
"files": [ | ||
"r_bg.wasm", | ||
"r.js", | ||
"r.d.ts" | ||
], | ||
"main": "r.js", | ||
"types": "r.d.ts", | ||
"sideEffects": [ | ||
"./snippets/*" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* tslint:disable */ | ||
/* eslint-disable */ | ||
/** | ||
* @param {any} args | ||
* @returns {string} | ||
*/ | ||
export function wasm_session_header(args: any): string; | ||
/** | ||
* @param {any} args | ||
* @returns {any} | ||
*/ | ||
export function wasm_runtime(args: any): any; | ||
/** | ||
* Check whether an input produces parse errors | ||
* | ||
* Returns Option::None if no errors are found, or | ||
* Option::Some((start, end, message)) when an error is produced. | ||
* @param {any} args | ||
* @param {string} input | ||
* @returns {(ParseError)[]} | ||
*/ | ||
export function wasm_parse_errors(args: any, input: string): (ParseError)[]; | ||
/** | ||
* returns a stream of strings. Each pair represents a style and text | ||
* @param {any} args | ||
* @param {string} input | ||
* @returns {any[]} | ||
*/ | ||
export function wasm_highlight(args: any, input: string): any[]; | ||
/** | ||
*/ | ||
export enum Localization { | ||
En = 0, | ||
Es = 1, | ||
Zh = 2, | ||
De = 3, | ||
Pirate = 4, | ||
Emoji = 5, | ||
} | ||
/** | ||
*/ | ||
export enum Experiment { | ||
TailCalls = 0, | ||
RestArgs = 1, | ||
} | ||
/** | ||
* Run the R REPL | ||
*/ | ||
export class Cli { | ||
free(): void; | ||
/** | ||
* Enable experimental language features | ||
*/ | ||
experiments: any[]; | ||
/** | ||
* Localization to use for runtime | ||
*/ | ||
locale: Localization; | ||
/** | ||
* Show the extended warranty information at startup | ||
*/ | ||
warranty: boolean; | ||
} | ||
/** | ||
*/ | ||
export class ParseError { | ||
free(): void; | ||
/** | ||
* @returns {number} | ||
*/ | ||
start(): number; | ||
/** | ||
* @returns {number} | ||
*/ | ||
end(): number; | ||
/** | ||
* @returns {string} | ||
*/ | ||
message(): string; | ||
} | ||
|
||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; | ||
|
||
export interface InitOutput { | ||
readonly memory: WebAssembly.Memory; | ||
readonly __wbg_cli_free: (a: number) => void; | ||
readonly __wbg_get_cli_locale: (a: number) => number; | ||
readonly __wbg_set_cli_locale: (a: number, b: number) => void; | ||
readonly __wbg_get_cli_warranty: (a: number) => number; | ||
readonly __wbg_set_cli_warranty: (a: number, b: number) => void; | ||
readonly __wbg_get_cli_experiments: (a: number, b: number) => void; | ||
readonly __wbg_set_cli_experiments: (a: number, b: number, c: number) => void; | ||
readonly __wbg_parseerror_free: (a: number) => void; | ||
readonly parseerror_start: (a: number) => number; | ||
readonly parseerror_end: (a: number) => number; | ||
readonly parseerror_message: (a: number, b: number) => void; | ||
readonly wasm_session_header: (a: number, b: number) => void; | ||
readonly wasm_runtime: (a: number) => number; | ||
readonly wasm_parse_errors: (a: number, b: number, c: number, d: number) => void; | ||
readonly wasm_highlight: (a: number, b: number, c: number, d: number) => void; | ||
readonly __wbindgen_malloc: (a: number, b: number) => number; | ||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; | ||
readonly __wbindgen_export_2: WebAssembly.Table; | ||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number; | ||
readonly _dyn_core__ops__function__Fn__A_B___Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h138f2b7c4a5e2241: (a: number, b: number, c: number, d: number, e: number, f: number) => void; | ||
readonly __wbindgen_free: (a: number, b: number, c: number) => void; | ||
readonly __wbindgen_exn_store: (a: number) => void; | ||
} | ||
|
||
export type SyncInitInput = BufferSource | WebAssembly.Module; | ||
/** | ||
* Instantiates the given `module`, which can either be bytes or | ||
* a precompiled `WebAssembly.Module`. | ||
* | ||
* @param {SyncInitInput} module | ||
* | ||
* @returns {InitOutput} | ||
*/ | ||
export function initSync(module: SyncInitInput): InitOutput; | ||
|
||
/** | ||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and | ||
* for everything else, calls `WebAssembly.instantiate` directly. | ||
* | ||
* @param {InitInput | Promise<InitInput>} module_or_path | ||
* | ||
* @returns {Promise<InitOutput>} | ||
*/ | ||
export default function __wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>; |
Oops, something went wrong.