Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/soegaard/urlang
Urlang is JavaScript with a sane syntax
https://github.com/soegaard/urlang
compiler javascript nanopass racket
Last synced: 4 days ago
JSON representation
Urlang is JavaScript with a sane syntax
- Host: GitHub
- URL: https://github.com/soegaard/urlang
- Owner: soegaard
- Created: 2015-08-08T12:27:06.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2025-01-11T14:12:21.000Z (12 days ago)
- Last Synced: 2025-01-12T22:03:26.384Z (11 days ago)
- Topics: compiler, javascript, nanopass, racket
- Language: Racket
- Size: 791 KB
- Stars: 303
- Watchers: 15
- Forks: 16
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-racket - urlang - Write JavaScript with Racket syntax. Bonus: Use Racket to define macros for JavaScript constructs. (Compilers)
- awesome-racket-and-scheme - Urlang
README
# URLANG
> Urlang is JavaScript with a sane syntax
Urlang is a language designed to allow straightforward translation to JavaScript.
Think of Urlang as JavaScript with sane syntax and JavaScript semantics.
JavaScript in this context is short for ECMAScript 5 in strict mode.Although the constructs of Urlang and JavaScript map almost one-to-one,
a little sugar was added:
* function definitions allow default arguments
* let expressionsEven though the syntax of Urlang is Racket-like, remember that the
semantics is standard JavaScript. This means in particular that tail calls
build context.## Examples
The following examples are compiled using the `urlang` form.
(urlang ...)
The urlang form compiles the modules. The result of compiling
a module is saved a file whose path is the module-name with `.js`
added.The `urlang` form is controlled by the following parameters:
(current-urlang-run? #t) ; compile and run (using node)
(current-urlang-echo? #t) ; print JavaScript to screen
(current-urlang-console.log-module-level-expr? #t) ; use conole.log to print module-level exprs### Example (factorial)
> (urlang
(urmodule fact ; module name
(export fact) ; fact is exported
(import + - * = displayln ref console)
(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))
(fact 5)))The generated JavaScript:
"use strict";
function fact(n){return (((n===0)===false)?(n*(fact((n-1)))):1);};
console.log((fact(5)));
exports.fact=fact;The output from Node:
"120\n"
### Example (`cond`-macro and `array`)
Urlang macro transformers receive and produce standard Racket syntax objects.
This implies that standard tools such as syntax-parse are available.Consider an Urlang version of `cond`:
SYNTAX (cond [e0 e1 e2 ...] ... [else en]),
like Racket cond except there is no new scopeThe urlang macro transformer is an standard (phase 0) Racket function.
(define-urlang-macro cond
(λ (stx)
(syntax-parse stx
[(_cond [else e0:Expr e:Expr ...])
#'(begin e0 e ...)]
[(_cond [e0 e1 e2 ...] clause ...)
(syntax/loc stx
(if e0 (begin e1 e2 ...) (cond clause ...)))]
[(_cond)
(raise-syntax-error 'cond "expected an else clause" stx)])))
The macro can now we be used:
> (urlang
(urmodule sum-example
(define (even? x) (=== (% x 2) 0))
(var (sum 0) x (a (array 1 2 3 4 5)) (i 0) (n a.length))
(while (< i n)
(:= x (ref a i))
(cond
[(even? x) (+= sum (ref a i))]
[else "skip"])
(+= i 1))
sum))The generated JavaScript:
"use strict";
function even_p(x){return ((x%2)===0);};
var sum=0,x,a=[1,2,3,4,5],i=0,n=a.length;
while((i JavaScriptthat compiles an urlang module and produces JavaScript,
that can be evaluated by the Node.js platform (or be embedded in a web page).The Urlang module to be compiled can be represented
1. as a syntax object
2. as a Nanopass structure (representing an Lurlang program)Use 1) to program in Urlang directly.
Use 2) if you intend to use Urlang as a compiler backend.
[Note: Nanopass is a framework for implementing compilers.]
The intended use of Urlang is to use 1) to write (generate) a Racket runtime in JavaScript.
The middle-end of the Racket-to-JavaScript compiler will produce output as Nanopass
structures, so 2) will be used as the backend for the Racket-to-JavaScript compiler.Internally the function expand
expand : syntax -> LUrlang
will parse and expand its input and produce an LUrlang representation.
Note that `expand` allows the user to extend the input language
using define-urlang-macro. An Urlang macro is a syntax to syntax
transformation implemented as a normal Racket function.
This allow you to use all of the standard Racket macro machinery.Main functions:
expand : syntax -> Lurlang
expand the input and produce a fully expanded Urlang program
represented as a Lurlang structure
compile : syntax ->
Expand and compile. The output is written to standard out.
eval : syntax -> value
expand, compile and run the input (an Urlang module represented as a syntax object)
Running means that `node` is used to run the generated JavaScript.Having Urlang as a `#lang` language allows
* macros (using full Racket at compile time)
* export of defined names
* easier testingIn the grammar below:
- `x` stands for a non-keyword identifier
- `f` stands for an identifier defined as a function````
::= (urmodule ...)::= | | |
::= (export x ...)
::= (import x ...)
::= (define (f ...) )
| (define x )
::= x | [x ]::= | | | | | |
::= (var ...)
::= (block ...)
::= x | (x e)
::= (while ...)
::= (do-while ...)
::= (sif )
::= (break) | (break label)::= ...
::= | | |
| | | | |
::= (if )
::= x
::= ( ...)
::= (begin ...)
::= (:= x )
::= (let ((x ) ...) ... )
::= (lambda ( ...) )::= define | begin | urmodule | if | := | ...se code...
::= | | #t | #f
an identifier that is not a keyword
an integer between -2^53 and 2^53
a symbol or string
````# NOTES
Some application are special cases:
(ref e0 e1) becomes e0[e1]
(ref e0 "str") becomes e0.str
(array e ...) becomes [e,...]Property access with dot notation is rewritten to use bracket syntax in the parser.
Example: `object.property` becomes `object["property"]`
# SEMANTICS
### `(if e0 e1 e2)`
If `e0` evaluates to value strictly equal to false, then `e2` otherwise `e1`.
Note: The JavaScript becomes `((e0===false) ? e2 : e1)`### `(var x (y 3))`
Binds `x` to undefined and `y` to `3`.