5 Parenthetical JavaScript
This package recognizes an alternative, Scheme-like front-end syntax for JavaScript, dubbed Parenthetical JavaScript (or PJS for short). If you pronounce that "pee-jays" you will make me happy.
The PJS library can be required via:
(require (planet dherman/javascript:9:1/pjs)) |
5.1 Syntax
The syntax of PJS is as follows:
program-unit | = | (source-elt ...) | ||
source-elt | = | decl | ||
| | stmt | |||
decl | = | (function id (id ...) source-elt ...) | ||
| | (var var-init ...+) | |||
stmt | = | (#%statement stmt) | ||
| | block-stmt | |||
| | (if expr stmt) | |||
| | (if expr stmt stmt) | |||
| | (do stmt expr) | |||
| | (while expr stmt) | |||
| | (for (var var-init ...+) expr expr stmt) | |||
| | (for expr expr expr stmt) | |||
| | (for (var id) in expr stmt) | |||
| | (for id in expr stmt) | |||
| | (continue maybe-id) | |||
| | (break maybe-id) | |||
| | (return maybe-expr) | |||
| | (with expr stmt) | |||
| | (switch expr case-clause ...) | |||
| | (label id stmt) | |||
| | (throw expr) | |||
| | (try stmt catch-clause finally-clause) | |||
| | (try stmt catch-clause) | |||
| | (try stmt finally-clause) | |||
| | () | |||
| | expr | |||
block-stmt | = | (block source-elt ...) | ||
expr | = | (#%expression expr) | ||
| | (regexp string boolean boolean) | |||
| | (array array-elt ...) | |||
| | (object [id expr] ...) | |||
| | (field-ref expr expr) | |||
| | (field expr id) | |||
| | (new expr expr ...) | |||
| | (prefix prefix-op expr) | |||
| | (postfix expr postfix-op) | |||
| | (infix-op expr expr expr ...) | |||
| | (assign-op expr expr) | |||
| | (if expr expr expr) | |||
| | (function id (id ...) source-elt ...) | |||
| | (function (id ...) source-elt ...) | |||
| | (begin expr ...+) | |||
| | string | |||
| | number | |||
| | boolean | |||
| | null | |||
| | this | |||
| | javadot-id | |||
javadot-id | = | id.id... | ||
array-elt | = | expr | ||
| | () | |||
var-init | = | id | ||
| | [id expr] | |||
case-clause | = | (default stmt ...) | ||
| | (case expr stmt ...) | |||
catch-clause | = | (catch id stmt) | ||
finally-clause | = | (finally stmt) |
5.1.1 Operators
The set of recognized operators is:
prefix-op : ++, --, +, -, ~, !
infix-op : *, /, %, +, -, <, <<, >, >>, >>>, <=, >=, ==, !=, ===, !==, &, ^, \|, &&, \|\|
postfix-op : ++, --
assign-op : =, *=, /=, %=, +=, -=, <<=, >>=, >>>=, &=, ^=, \|=
For aesthetics and consistency with Scheme, some of the JavaScript operators with awkward or obscure operator tokens are given convenient synonyms in PJS:
or = \|\|
and = &&
bitwise-and = &
bitwise-ior = \|
bitwise-not = ~
bitwise-xor = ^
5.1.2 Identifiers
Identifiers in PJS (i.e., the id non-terminal in the grammar) are restricted to valid JavaScript identifiers. This ensures that, when generating actual JavaScript syntax, the exact name of a variable or object property is preserved, with no name mangling. (Since variable names are sometimes observable in JavaScript, this prevents subtle bugs where a program may change its behavior based on a particular mangling.)
5.2 Resolving Ambiguities
The grammar as presented contains several ambiguities.
5.2.1 Disambiguating operators with #%keyword
The following primitive operators have names that are valid JavaScript identifiers:
and
array
begin
block
field
label
object
or
prefix
postfix
regexp
The parsing and code generation functions maintain an environment for lexically bound variables. JavaScript variable bindings can therefore shadow these operators. For example, in the expression
(function (array) |
(return (array 1 2 3))) |
the inner occurrence of array is parsed as a variable reference rather than an array constructor.
Operators may be disambiguated with the use of the special form #%keyword. For example, (#%keyword . array) unconditionally refers to the initial binding of array, regardless of the current environment. The above example can then be rewritten as
(function (array) |
(return ((#%keyword . array) 1 2 3))) |
Note that if JavaScript code would dynamically bind one of these names (e.g., via with or eval), the operator is not shadowed, since the static environment only tracks lexical bindings.
5.2.2 Disambiguating expressions with #%expression
Expression statements introduce a few minor ambiguities into the PJS syntax. These include function declarations vs. named function expressions and if statements vs. if expressions. These are always resolved in favor of the declaration or statement form rather than the expression form, since in both cases the expression form is less likely to occur.
The expression form operator #%expression forces its argument to be parsed as an expression. For example, the expression (#%expression (if x y z)) is parsed as a conditional expression rather than a conditional statement.
5.3 Syntactic Conveniences
The PJS syntax provides several additional syntactic conveniences.
5.3.1 Java Dot Notation
Similar to the Java Dot Notation originally introduced by JScheme, PJS permits the use of dotted identifiers as a short-hand for the obvious corresponding chains of object field references. Unlike in JScheme, it is impossible to bind a variable with a dot in its name, so dotted identifiers are always unambiguously decoded as field references.
For example, the identifier document.body.innerHTML is equivalent to the expression (field (field document body) innerHTML).
5.3.2 Multiary Operators
Binary JavaScript operators are generalized in PJS to n-ary operators and expanded left-associatively into nested applications of the binary operator.
For example, the expression (- 4 3 2 1) is equivalent to (- (- (- 4 3) 2) 1).
5.4 Library Procedures
The PJS library provides procedures that operate on either syntax objects or S-expressions. The syntax object procedures maintain source location information.
5.4.1 S-expression Parsers
(syntax->expression stx) → Expression? |
stx : syntax? |
Parses the PJS expression stx.
(syntax->statement stx) → Statement? |
stx : syntax? |
Parses the PJS statement stx.
(syntax->source-element stx) → SourceElement? |
stx : syntax? |
Parses the PJS source element stx.
(syntax->program-unit stx) → (listof SourceElement?) |
stx : syntax? |
Parses the PJS program unit stx.
(sexp->expression sexp) → Expression? |
sexp : sexp? |
Parses the PJS expression sexp.
(sexp->statement sexp) → Statement? |
sexp : sexp? |
Parses the PJS statement sexp.
(sexp->source-element sexp) → SourceElement? |
sexp : sexp? |
Parses the PJS source element sexp.
(sexp->program-unit sexp) → (listof SourceElement?) |
sexp : (listof sexp?) |
Parses the PJS program unit sexp.
5.4.2 S-expression Generators
(expression->syntax expr) → syntax? |
expr : Expression? |
Generates a PJS representation of expr.
(statement->syntax stmt) → syntax? |
stmt : Statement? |
Generates a PJS representation of stmt.
(source-element->syntax elt) → syntax? |
elt : SourceElement? |
Generates a PJS representation of elt.
(program-unit->syntax elts) → syntax? |
elts : (listof SourceElement?) |
Generates a PJS representation of elts.
(expression->sexp expr) → sexp? |
expr : Expression? |
Generates a PJS representation of expr.
(statement->sexp stmt) → sexp? |
stmt : Statement? |
Generates a PJS representation of stmt.
(source-element->sexp elt) → sexp? |
elt : SourceElement? |
Generates a PJS representation of elt.
(program-unit->sexp elts) → sexp? |
elts : (listof SourceElement?) |
Generates a PJS representation of elts.