Giter Site home page Giter Site logo

racketscript's Introduction

RacketScript

MIT licensed Tests ESLint Coverage Status Try Online

Racket Discourse users Racket Discord

RacketScript is an experimental lightweight Racket to JavaScript (ECMAScript 6) compiler. RacketScript aims to leverage both JavaScript and Racket's ecosystem, and make interoperability between them clean and smooth.

RacketScript takes in Racket source files, uses Racket's macro expander to produce Fully Expanded Programs, and then compile these fully expanded programs to JavaScript. RacketScript currently supports only a subset of Racket.

Try RacketScript

You can try RacketScript in your browser at RacketScript Playground.

You may alo be interested in Rackt - An ultrasmall (~70 loc) React wrapper written in RacketScript.

Disclaimer

RacketScript is work-in-progress and is not mature and stable. Several Racket features and libraries are not yet implemented (eg. number pyramid, contracts, proper tail calls, continuations). There are also quite a few missing primitive functions. That said, we encourage experimentation, user feedback, discussions, bug reports and pull requests.

Installation

Following system packages are required:

  • Racket 6.12 or higher
  • NodeJS (14.0 or higher) and NPM
  • Make

Quick Install

RacketScript can be installed using the Racket package manager raco:

raco pkg install racketscript

See Basic Usage to get started.

Install from Github

# Clone RacketScript
git clone [email protected]:racketscript/racketscript.git`
cd racketscript

# Build and install
make setup

If you do not wish to pollute your root NPM directory, you can set a custom global location by changing your npmrc (eg. echo "prefix = $HOME/.npm-packages" >> ~/.npmrc. Then add /prefix/path/above/bin to your PATH.

Basic Usage

RacketScript compiler is named racks.

racks -h # show help

To compile a Racket source file:

# Installs all NPM dependencies and compile file.rkt
racks /path/to/file.rkt

The above command will create a output build directory named js-build, copy RacketScript runtime, copy other support files, install NPM dependencies, compile file.rkt and its dependencies.

The compiled JavaScript modules typically goto one of following three folders:

  • "modules": The normal Racket files.
  • "collects": Racket collects source files.
  • "links": Other third party packages.
  • "dist": Contains sources compiled to ES6 or bundled JavaScript ready for distribution.

Here are few other examples that would come in handy:

# To skip `npm install` step. Useful when building
# for second time.
racks -n /path/to/module-name.rkt

# Run the assembled JavaScript module.
node js-build/modules/module-name.rkt.js

# Use `-b` to format the assembled JavaScript code use `-b`. Assumes
# `js-beautify` is available in `$PATH`.
racks -b /path/to/module-name.rkt

# Override default output directory
racks -d /path/to/output/dir /path/to/module-name.rkt

# Print JavaScript output to stdout
racks --js --js-beautify /path/to/module-name.rkt

By default tail call optimization is turned off. To enable translation of self recursive tail calls to loop, pass --enable-self-tail flag.

racks --enable-self-tail /path/to/source.rkt

Browser

Most browsers can load RacketScript modules directly without any external dependencies <script type="module" src="path/to/module.rkt.js"></script>.

Module Bundler (Webpack)

For deployment, you may want to bundle all generated modules into single JavaScript file. RacketScript can generate some boiler-plate for using Webpack/Babel, however we recommend you to use your own configuration.

# Use `--target` or `-t` flag.
racks --target webpack /path/to/source.rkt

# Call webpack to bundle in `js-build` directory. Will produce
# single JavaScript bundle in `js-build/dist` directory.
npx webpack

Contributing to RacketScript

Please read Contribution Guidelines.

Troubleshooting

Please read the Troubleshooting Wiki.

Related Work

racketscript's People

Contributors

arjunguha avatar darrenn avatar daynin avatar dependabot[bot] avatar freeduck avatar gamburgm avatar glebm avatar kclapper avatar kikudjira avatar leidnedya avatar samth avatar spdegabrielle avatar stchang avatar vishesh 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

racketscript's Issues

Porting forward.scm to RacketScript

I created a minimalist scheme framework for creating web apps in scheme using the BiwaScheme interpreter called forward.scm. Basically, it boils down to the %create-app procedure.

It's similar in principle to redux, it use a virtualdom library called snabbdom (similar in principle to reactjs) and prolly similar in principle to ClojureScript's om (even though I don't have om experience yet).

To make a port to RacketScript (RS) possible I need to know two things:

  • How to call javascript from RS and vice-versa
  • Right now, the framework works with the assumption that XHR calls are not callback based, they are not synchronous tho. This is made possible thanks to BiwaScheme's magic http-request which AFAIU use somekind of call/cc. Now I now RS doesn't implement call/cc (why?). I successfully ported forward.scm to use the new async/await from ES2017. My question is: does RS plan to support somekind of call/cc similar to biwascheme? Does it support async/await?

Thanks!

on-key triggers on shift

In Racket, does on-key trigger when you press shift? I thought it didnt (will test later). Anyhow, RacketScript does and it makes it hard to enter capital letters.

image: deep dense image trees are slow to render

For example following example without freeze would take forever to render. freeze here is a fix to pre-render these images, ensuring image tree remains shallow.

(define (colored-carpet colors)
  (match colors
    [(cons hd '()) (square 1 'solid hd)]
    [(cons hd tl)
     (define c (colored-carpet tl))
     (define i (square (image-width c) 'solid hd))
     (freeze (above (beside c c c)
                    (beside c i c)
                    (beside c c c)))]))

When constructing images, we can consider a depth threshold or some other heuristic to pre-cache these images.

Improve Readme

Describe how to compile and run a hello-world example.

After

 racks foo.rkt

describe how to run the result in Node.

Circular references

Now that we have mutable structs, we should be able to check for circular references. Equal? and display should not get hung up on them.

No arity checks

Arity checks seems to be missing?

#lang racket
(displayln (cons 11 22 33))

(define (foo x y)
  (list x y))

(displayln (foo 11 22 33))
(displayln (foo 44))

How to upgrade?

What's the best way to upgrade the 'racketscript' installation?

`#lang lazy` causes bad error

Compiling the following program:

#lang lazy

(define 1s (cons 1 1s))

produced an awful error:

Console Log: 
[error] {"message":"http://rapture.twistedplane.com:8080/modules/index_inline_script_1.js:5:5: Unexpected token 1\nhttp:/ [snip]

lazy lang not found

#lang lazy

(define (odd? n)
  (if (zero? n) false (even? (sub1 n))))

(define (even? n)
  (if (zero? n) true (odd? (sub1 n))))

(odd? 100)

produces

Console Log: 
[error] Error: File not found 'http://rapture.twistedplane.com:8080/links/lazy/lazy.rkt.js'
Specified as ../links/lazy/lazy.rkt.js.
Normalizes to ../links/lazy/lazy.rkt.js
locate resolved against base 'http://rapture.twistedplane.com:8080/modules/index.html'

    at InternalLoader.handleCodeUnitLoadError (https://google.github.io/traceur-compiler/bin/BrowserSystem.js:33360:40)
    at https://google.github.io/traceur-compiler/bin/BrowserSystem.js:33222:20 

error with 'make integration-test': tests/fixture.rkt:5:9: collection not found

RACKETSCRIPT INTEGRATION TEST
racket --version
Welcome to Racket v6.9.

racketscript version:

git describe --tags
v0.2-alpha-4-g3d221e2
git rev-parse HEAD
3d221e2

make integration-test
++++++++++++++++++++++++++++++++
raco test -t tests/fixture.rkt
tests/fixture.rkt:5:9: collection not found
for module path: glob
collection: "glob"
in collection directories:
/home/bkb/.racket/6.9/collects
/usr/local/share/racket/collects
... [175 additional linked and package directories]
context...:
show-collection-err
standard-module-name-resolver
[repeats 1 more time]
/usr/local/share/racket/pkgs/compiler-lib/compiler/commands/test.rkt:550:0: test-files79
/usr/local/share/racket/pkgs/compiler-lib/compiler/commands/test.rkt:433:0: map/parallel49
...cket/cmdline.rkt:179:51
/usr/local/share/racket/pkgs/compiler-lib/compiler/commands/test.rkt: [running body]
/usr/local/share/racket/collects/raco/raco.rkt: [running body]
/usr/local/share/racket/collects/raco/main.rkt: [running body]
Makefile:75: recipe for target 'integration-test' failed
make: *** [integration-test] Error 1

Name given to (super) struct guards

#lang racket

(struct posn (x y)     #:guard (λ (x y name)   (displayln (list '2d x y name))   (values x y)))
(struct posn3 posn (z) #:guard (λ (x y z name) (displayln (list '3d x y z name)) (values x y z)))
(define p1 (posn3 10 12 14))
(displayln (list (posn-x p1)
                 (posn-y p1)
                 (posn3-z p1)))

In DrRacket the the output is:

(3d 10 12 14 posn3)
(2d 10 12 posn3)
(10 12 14)

In RacketScript the output is:

Console Log: 
[log] (3d 10 12 14 posn3) 
[log] (2d 10 12 posn) 
[log] (10 12 14) 

Import identifiers directly from modules rather than referring

Import identifiers directly from modules and use the original name instead of referring them from modules. We originally did that, but removed that by referring all module objects because due to macro expansion we might have two identifier with same name but different bindings. We can figure this out when expanding and refer to these bindings specifically. The only benefit is more readable output.

`time` can't be used properly?

#lang racket

(define (gen n)
  (if (> n 0)
      (cons n (gen (sub1 n)))
      empty))

(define ls (gen 10000))

(time (length ls))

This program runs fine in DrRacket. But here it produces the error

Console Log: 
[error] TypeError: M0.time_apply is not a function
    at eval (http://rapture.twistedplane.com:8080/modules/index_inline_script_1.js:14:26)
    at Object.exports.call-with-values (http://rapture.twistedplane.com:8080/runtime/kernel.js:25:18)
  ...

Note that writing time on its own produces an error that suggests time is indeed defined.

`big-bang` (maybe `text`) does not display values properly

#lang racketscript/base

(require racketscript/htdp/image
         racketscript/htdp/universe
)

(define (tick1 n)
  (add1 n))

(define (show1 n)
  (text (number->string n) 50 "blue"))

(define (show2 n)
  (text (string-append " " (number->string n) " ") 50 "blue"))

(define a
  (big-bang 0
            [on-tick tick1]
            [to-draw show1]
            [stop-when (lambda (n) (> n 100))]))

This should show the values from 1 to 100. But when the numbers go into double-digits, it has trouble showing everything past the first digit properly.

Note that if I use show2 instead it does a bit better: the second digit is partially visible. Here, e.g, is where it ends (this is "100"):

image

Error(compile):Cannot find module './lib/runTask'

I have a new error when compiling with babel:

racks --target babel tests/basic/fact.rkt

Error:

npm WARN [email protected] No license field.
module.js:328
throw err;
^

Error: Cannot find module './lib/runTask'
at Function.Module._resolveFilename (module.js:326:15)
at Function.Module._load (module.js:277:25)
at Module.require (module.js:354:17)
at require (internal/module.js:12:17)
at Object. (/home/...racketscript/js-build/node_modules/orchestrator/index.js:8:15)
at Module._compile (module.js:410:26)
at Object.Module._extensions..js (module.js:417:10)
at Module.load (module.js:344:32)
at Function.Module._load (module.js:301:12)
at Module.require (module.js:354:17)
[info] Finished.

'() seems to print as ""

This is pretty minor, but here's an example program:

#lang racketscript/base

(require racketscript/htdp/universe
         racketscript/htdp/image 
         racketscript/interop
         racketscript/browser)

(displayln 3)
(displayln '(1 2 3))
(displayln '())

and console log (I thought the third line would be ()):

Console Log: 
[log] 3 
[log] (1 2 3) 
[log]  

Simple (factorial) Typed/Racket program fails

I tried the following Typed/Racket program on http://rapture.twistedplane.com:8080/.

#lang typed/racket/base

(define (factorial [n : Integer])
  (let loop : Integer ([n : Integer n]
             [a : Integer 1])
    (if (zero? n)
        a
        (loop (sub1 n) (* n a)))))

(displayln (factorial 7))

I get the following error:

Console Log: 
[error] TypeError: M0.variable_reference__gt_module_source_by_submod is not a function
    at eval (http://rapture.twistedplane.com:8080/modules/index_inline_script_1.js:5:40)
    at Object.getAnonymousModule (https://google.github.io/traceur-compiler/bin/BrowserSystem.js:358:25)
    at eval (http://rapture.twistedplane.com:8080/modules/index_inline_script_1.js:1:29)
    at eval (<anonymous>)
    at LoaderCompiler.evaluateCodeUnit (https://google.github.io/traceur-compiler/bin/BrowserSystem.js:32970:38)
    at EvalCodeUnit.evaluate (https://google.github.io/traceur-compiler/bin/BrowserSystem.js:33130:36)
    at InternalLoader.evaluate (https://google.github.io/traceur-compiler/bin/BrowserSystem.js:33432:31)
    at InternalLoader.handleCodeUnitLoaded (https://google.github.io/traceur-compiler/bin/BrowserSystem.js:33345:18)
    at https://google.github.io/traceur-compiler/bin/BrowserSystem.js:33216:18 

The produced JS code (in the lower left pane) is:

import * as $rjs_core from '../runtime/core.js';
import * as M0 from "../runtime/kernel.rkt.js";
var blame1 = M0.module_name_fixup(M0.variable_reference__gt_module_source_by_submod(M0.__variable_reference), M0.list());
var factorial = function(n5) {
  var loop6 = function(_n74, _a85) {
    lambda_start3: while (true) {
      let n7 = _n74;
      let a8 = _a85;
      if (M0.zero_p(n7)) {
        return a8;
      } else {
        _n74 = M0.sub1(n7);
        _a85 = n7 * a8;
        continue lambda_start3;
      }
    }
  };
  return loop6(n5, 1);
};
M0.call_with_values(function() {
  return M0.displayln(factorial(7));
}, M0.print_values);
M0.rvoid();

Support babel in fixtures.rkt

We currently use Traceur, but we can just replace that command with babel. Because of no runtime, and smaller code, tests may run faster.

RacketScript may get confused with symlinks

For example, currently we have some primitive set of module whose import we ignore in kernel.rkt to avoid circular links and plus because they are not actually used at first place. When our last phase checks for these ignored modules, although they refer to same file they may have different path because of use of symlinks.

Steps:

  • Setup RacketScript
  • Symlink RacketScript root dir to some other place and use that as the main directory to use.
  • Compile a file (I've tested in fixtures) and we will end up with unresolved cycle. (transform.rkt:+94)

Support `proc-spec` property in structures

As of now required for keyword arguments. racket-core/list.rkt test fails as sort uses keyword arguments. Two ways to support applicable structures:

  • Use lam.call convention
  • Or, produce an actual function with structure object as its property.

(runtime) error: ReferenceError: $traceurRuntime is not defined

I've uninstalled the last racketscript installation because '--target babel' always gave me runtime errors. Now racketscript is installed again from source with:

  • 'make setup'
  • 'npm install -g traceur gulp'

Compiling to *.js seems to work

> racks for.rkt 
[...]
npm WARN [email protected] No license field.
[11:42:33] Using gulpfile ~/local/racketscript/git/racketscript/js-build/gulpfile.js
[...]
[info] Finished.

But I can't resolve this runtime error:

node js-build/dist/compiled.js

ReferenceError: $traceurRuntime is not defined

Don't recompute identifier graph and recompiled unchanged modules

We compute identifier and module dependency graph each time we start rapture. Also, each module is recompiled even though it is not changed. Keep some metadata around previous compilation history so that we don't do all this again and again and make rapture execution time a bit faster.

If modules are changed, we would want to recompute identifier graph, or atleast the nodes specific to changed modules.

Stack depth very limited

I find that when running a tight non-tail loop, the stack exhausts in around 12,000 frames on my desktop browser, which is just a second or two of execution time. This makes it difficult to run any interesting recursive computations. When will the project add stack management?

Improvements to compiler driver

  • Don't recompile files that are not changed
  • Don't copy runtime files again and again
  • Make runtime a separate npm pacakage?
  • Cache export graph and other data structures used
  • Persist package.json, Gulpfile across builds. Currently we copy it again and again. Practically, users would want to add their own JS dependencies. Overall we need some notion of workspace.
  • Option to just compile and update complete runtime.
  • Option to compile just single file
  • #308
  • A build system for RacketScript which also watches and automatically compiles them.
  • Improvements (caching?) global identifier graph and other computed data.
  • Parallelize compilation.

bouquet, not a brickbat

This is an anti-issue, but I wasn't sure how else to communicate it since I don't have all your emails. I had time to try out RacketScript on some tiny #lang-style examples, and I was impressed that it worked out of the box. Since I've criticized RacketScript for the things it doesn't do, I thought I should also publicly praise you for the things it does. Nice job!

call/cc does not work

(call/cc 3) produces

Console Log: 
[error] TypeError: M0.call_by_cc is not a function
    at eval (http://rapture.twistedplane.com:8080/modules/index_inline_script_1.js:6:15)
    at Object.exports.call-with-values (http://rapture.twistedplane.com:8080/runtime/kernel.js:25:18)
blah

This isn't Racket.

Compile functions in all phases

Compile functions in all phases (or at-least ones that can be used outside current module by looking into provides):

Possible solutions:

  • 1 Module file/phase
  • Append phase number to binding name

Find macro introduced bindings while expanding

Currently we compile in topological order to track used identifiers. We can parse define-syntax to find such bindings and save some time and dependencies b/w these modules which lead to increased test/compile time.

call/ec doesn't seem to work, either

I can understand that continuations don't work, but even this program

(+ 1 (call/ec (lambda (k) (k 2))))

doesn't work ("TypeError: M0.call_by_ec is not a function").

Migrate kernel.js to RacketScript

Try to write as much runtime in RacketScript as possible.

https://github.com/vishesh/racketscript/compare/task/rjs-kernel
https://github.com/vishesh/racketscript/tree/task/rjs-kernel

  • Add a minimal #lang racketscript/boot for writing kernel.
  • Migrate basic primitives to kernel.rkt
  • Handle cyclic dependency for compiling kernel
  • Add checks by implementing a minimal contract system
  • Migrate all kernel.js to RacketScript
  • Optimization to use native operator doesn't work in kernel.rkt

mutual tail-recursion doesn't work in Playground

#lang racket

(define (odd? n)
  (if (zero? n) false (even? (sub1 n))))

(define (even? n)
  (if (zero? n) true (odd? (sub1 n))))

(odd? 100001)

produces a stack depth error. I saw that the docs said you could build the system with tail-call support; perhaps the Playground was not built that way? But this program has no problem:

(define (loop n)
  (if (zero? n) "done" (loop (sub1 n))))

(loop 10000000)

`find-relative-path` err when trying to run `rapture`

Output is below. It doesnt seem to like the .. in the path.

I believe it's the same error as here:

https://www.mail-archive.com/racket-users%40googlegroups.com/msg29170.html

with fix here: 97jaz/tzinfo@a8038e4

~/NEU_Research/racketscript/tests/simple$ ../../bin/rapture  --skip-gulp-build arithmatic.rkt 
[expand] /home/stchang/NEU_Research/racketscript/tests/simple/arithmatic.rkt
[absyn] arithmatic
[rename] arithmatic
[il] arithmatic
find-relative-path: contract violation
  expected: (and/c path-for-some-system? simple-form?)
  given: #<path:/home/stchang/NEU_Research/racketscript/tests/simple/js-build/modules/../../../../plt/racket/collects/racket/base.rkt.js>
  context...:
   /home/stchang/plt/racket/collects/racket/path.rkt:118:2: for-loop
   /home/stchang/plt/racket/collects/racket/path.rkt:116:0: do-explode-path
   /home/stchang/plt/racket/collects/racket/path.rkt:126:0: find-relative-path5
   /home/stchang/NEU_Research/racketscript/src/util.rkt:146:0: module->relative-import
   /home/stchang/NEU_Research/racketscript/src/transform.rkt:33:0: absyn-module->il
   /home/stchang/NEU_Research/racketscript/src/transform.rkt:23:0: absyn-top-level->il
   /home/stchang/NEU_Research/racketscript/src/main.rkt:120:2: loop
   (submod /home/stchang/NEU_Research/racketscript/src/main.rkt main): [running body]

Better indentifier names after renaming

If we can keep track of used identifier in current scope, we can produce better identifier names when renaming and make code more readable. For in a function scope we may not have to rename a var declaration which isn't shadowed at first place.

Edit: This should also help us fix the name clash issue after normalizing ids.

does not prevent "Page Unresponsive" popup

Sometimes a computation has a long-running sub-computation. For instance, when doing physical simulations, there can be a sequence of complicated computations.

However, if the computation runs for the while, the browser becomes unresponsive, and after a while the "Page Unresponsive" link pops up. If you kill the computation (what else can you do if you had a bug?), you get "Aw, Snap!" from Chrome and you've lost your page.

I'm not sure what one is supposed to do if one has an accidental infinite loop or, worse, an intentional long-running computation (e.g., updating the values of numerous pixels/particles/simulated physical objects/etc.).

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.