mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Under examples/unfinished. Was in my personal repo. This app displays a nicely-formatted full reference for the jsparse syntax tree.
280 lines
12 KiB
HTML
280 lines
12 KiB
HTML
<head>
|
|
<title>jsparse Docs</title>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="page">
|
|
{{> page}}
|
|
</div>
|
|
</body>
|
|
|
|
<template name="page">
|
|
|
|
<div class="topnotes">
|
|
<p>This is the spec for the JavaScript syntax tree returned by <code>jsparse</code>.</p>
|
|
|
|
<p>It's notable that <em>every token</em> from the source code appears in order in this syntax tree, which
|
|
is a Concrete Syntax Tree (CST) rather than an abstract AST.</p>
|
|
|
|
{{spacer}}
|
|
|
|
<p>The tree consists of <strong>nodes</strong>
|
|
and <strong>tokens</strong>. A node consists of a name followed
|
|
by zero or more child nodes or tokens. A token consists of
|
|
one or more characters from the source code.</p>
|
|
<p>For example, the following tree, which might have come from parsing <code>1 + 2</code>,
|
|
contains nodes named <span class="str">"binary"</span>,
|
|
<span class="str">"number"</span>, and <span class="str">"number"</span>
|
|
and the tokens <span class="token">1</span>, <span class="token">+</span>,
|
|
and <span class="token">2</span>:</p>
|
|
|
|
<p>{{#nodespec}}["binary", ["number", `1`], `+`, ["number", `2`]]{{/nodespec}}</p>
|
|
|
|
{{spacer}}
|
|
|
|
<p>The following notation is used throughout this document to give schematic definitions of
|
|
the different types of nodes.</p>
|
|
|
|
<p>An <span class="ref">italicized</span> word refers to a list of one or more possible
|
|
nodes or tokens that could fill a certain spot. For example, the following is the
|
|
definition of the <span class="str">"binary"</span> node:</p>
|
|
|
|
<p>{{#nodespec}}["binary", expression, binaryOp, expression]{{/nodespec}}</p>
|
|
|
|
<p>The <span class="ref">expression</span> spot could be filled by one of over 20 types
|
|
of expression nodes, and if you look up <span class="ref">binaryOp</span> you'll see
|
|
it refers to any of a list of binary operator tokens.</p>
|
|
|
|
<p>A vertical bar (<span class="punc or">|</span>) separates alternatives, of which exactly
|
|
one must be present in the tree. An ellipsis (<span class="punc">...</span>) stands
|
|
for a sequence of <em>zero or more</em> of the preceding item. For example:</p>
|
|
|
|
{{#nodespec}}["program", (statement | functionDecl), ...]{{/nodespec}}
|
|
|
|
<p>This definition says that a program node contains zero or more items, each of which may be either a
|
|
<span class="ref">statement</span> or a <span class="ref">functionDecl</span>.</p>
|
|
|
|
<p>A question mark (<span class="punct">?</span>) indicates that the previous item
|
|
(or items enclosed in parentheses) may or may not present.</p>
|
|
|
|
<p>Token classes like <span class="tokentype">IDENTIFIER</span> match any token in the class. The <span class="tokentype">BOOLEAN</span>, <span class="tokentype">NUMBER</span>, <span class="tokentype">STRING</span>, and <span class="tokentype">REGEX</span> classes are all literals. An example <span class="tokentype">STRING</span> would be <span class="token">"hello"</span>.
|
|
|
|
{{spacer}}
|
|
|
|
<p>Some definitions with ellipses may seem strangely permissive, such as:</p>
|
|
|
|
{{#nodespec}}["varStmnt", `var`, (varDecl | `,`), ..., semi]{{/nodespec}}
|
|
|
|
<p>This seems to say that <code>var;</code> and <code>var,,x,</code> are valid statements.
|
|
The reason for this style of definition is to discourage reliance on exact comma
|
|
positions, which may occasionally vary between JavaScript implementations and versions.
|
|
For example, the 5th edition of ECMAScript allows a trailing comma in object literals whereas
|
|
the 3rd edition doesn't.</p>
|
|
|
|
<p>For most applications, the commas aren't that important, and you can read all the varDecls
|
|
by just skipping any commas you encounter.</p>
|
|
|
|
</div>
|
|
|
|
{{#nodespec}}program:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["program", (statement | functionDecl), ...]{{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}statement:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["expressionStmnt", expression, semi]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>Function calls and assignments are expressions.</p>
|
|
</div>
|
|
{{#nodespec}}["emptyStmnt", `;`]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>Only an actual <span class="token">;</span> token can create an empty statement.</p>
|
|
</div>
|
|
{{#nodespec}}["blockStmnt", `{`, statement, ..., `}`]{{/nodespec}}
|
|
{{#nodespec}}["varStmnt", `var`, (varDecl | `,`), ..., semi]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>You can assume at least one varDecl is present.</p>
|
|
</div>
|
|
{{#nodespec}}["ifStmnt", `if`, `(`, expression, `)`, statement, (`else`, statement)?]{{/nodespec}}
|
|
{{#nodespec}}["whileStmnt", `while`, `(`, expression, `)`, statement]{{/nodespec}}
|
|
{{#nodespec}}["doStmnt", `do`, statement, `while`, `(`, expression, `)`, semi]{{/nodespec}}
|
|
{{#nodespec}}["forStmnt", `for`, `(`, forSpec, `)`, statement]{{/nodespec}}
|
|
{{#nodespec}}["returnStmnt", `return`, (expression | nil), semi]{{/nodespec}}
|
|
{{#nodespec}}["continueStmnt", `continue`, (IDENTIFIER | nil), semi]{{/nodespec}}
|
|
{{#nodespec}}["breakStmnt", `break`, (IDENTIFIER | nil), semi]{{/nodespec}}
|
|
{{#nodespec}}["throwStmnt", `throw`, expression, semi]{{/nodespec}}
|
|
{{#nodespec}}["withStmnt", `with`, `(`, expression, `)`, statement]{{/nodespec}}
|
|
{{#nodespec}}["switchStmnt", `switch`, `(`, expression, `)`, `{`, (case | default), ..., `}`]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>There's at most one default clause, but it can be anywhere in the list of cases and defaults.</p>
|
|
</div>
|
|
{{#nodespec}}["tryStmnt", `try`, statement, (catch | nil), (finally | nil)]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>The statement is always a blockStmnt.</p>
|
|
</div>
|
|
{{#nodespec}}["labelStmnt", IDENTIFIER, `:`, statement]{{/nodespec}}
|
|
{{#nodespec}}["debuggerStmnt", `debugger`, semi]{{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}functionDecl:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["functionDecl", `function`, IDENTIFIER, `(`, (IDENTIFIER | `,`), ..., `)`, `{`, (statement | functionDecl), ..., `}`]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>Different from a functionExpr only in that the function name is required and not optional.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{#nodespec}}expression:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["this", `this`]{{/nodespec}}
|
|
{{#nodespec}}["null", `null`]{{/nodespec}}
|
|
{{#nodespec}}["number", NUMBER]{{/nodespec}}
|
|
{{#nodespec}}["boolean", BOOLEAN]{{/nodespec}}
|
|
{{#nodespec}}["regex", REGEX]{{/nodespec}}
|
|
{{#nodespec}}["string", STRING]{{/nodespec}}
|
|
{{#nodespec}}["identifier", IDENTIFIER]{{/nodespec}}
|
|
{{#nodespec}}["parens", `(`, expression, `)`]{{/nodespec}}
|
|
{{#nodespec}}["array", `[`, (expression | `,`), ..., `]`]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>All commas are significant because of element elision, and any
|
|
combination of commas and expressions is possible.
|
|
The array <code>[,,,7,,8,,]</code> has 7 and 8 as
|
|
its 3rd and 5th elements.</p>
|
|
</div>
|
|
{{#nodespec}}["object", `{`, (prop | `,`), ..., `}`]{{/nodespec}}
|
|
{{#nodespec}}["functionExpr", `function`, (IDENTIFIER | nil), `(`, (IDENTIFIER | `,`), ..., `)`, `{`, (statement | functionDecl), ..., `}`]{{/nodespec}}
|
|
{{#nodespec}}["dot", expression, `.`, identifierName]{{/nodespec}}
|
|
{{#nodespec}}["bracket", expression, `[`, expression, `]`]{{/nodespec}}
|
|
{{#nodespec}}["call", expression, `(`, (expression | `,`), ..., `)`]{{/nodespec}}
|
|
{{#nodespec}}["new", `new`, expression]{{/nodespec}}
|
|
{{#nodespec}}["newcall", `new`, expression, `(`, (expression | `,`), ..., `)`]{{/nodespec}}
|
|
{{#nodespec}}["unary", unaryOp, expression]{{/nodespec}}
|
|
{{#nodespec}}["binary", expression, binaryOp, expression]{{/nodespec}}
|
|
{{#nodespec}}["postfix", expression, postfixOp]{{/nodespec}}
|
|
{{#nodespec}}["ternary", expression, `?`, expression, `:`, expression]{{/nodespec}}
|
|
{{#nodespec}}["assignment", expression, assignmentOp, expression]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>There is no simple constraint on what the first expression can be.
|
|
A left-hand-side expression could be a simple identifier like <code>foo</code>,
|
|
or it could be any expression ending in a property access, such as <code>foo().bar[baz]</code>.
|
|
Parentheses are also allowed around the left-hand side, so surprisingly <code>((x)) = 3</code>
|
|
is completely legal and equivalent to <code>x = 3</code>. This is because
|
|
JavaScript evaluates the left-hand side all the way down to the final variable reference.</p>
|
|
</div>
|
|
{{#nodespec}}["comma", (expression | `,`), ...]{{/nodespec}}
|
|
<div class="explan"><p>You can assume there are at least two expressions, since there would have been no comma otherwise.</p></div>
|
|
</div>
|
|
|
|
{{#nodespec}}nil:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["nil"]{{/nodespec}}
|
|
<div class="explan"><p>Serves as a placeholder for optional parts of nodes.</p></div>
|
|
</div>
|
|
|
|
{{#nodespec}}semi:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}[";"] | `;`{{/nodespec}}
|
|
<div class="explan">
|
|
<p>An optional semicolon at the end of a statement. Most semicolons in JavaScript can legally
|
|
be omitted if a line break follows. If the semicolon was omitted, a <span class="str">";"</span>
|
|
node takes its place.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{#nodespec}}prop:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["prop", propertyName, `:`, expression]{{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}propertyName:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["idPropName", identifierName]{{/nodespec}}
|
|
{{#nodespec}}["strPropName", STRING]{{/nodespec}}
|
|
{{#nodespec}}["numPropName", NUMBER]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>A property name in an object literal can be given as a bare identifier, a quoted string literal,
|
|
or a number literal. These nodes indicate which it is.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{#nodespec}}varDecl:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["varDecl", IDENTIFIER, (`=`, expression)?]{{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}forSpec:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["forSpec", (expression | nil), `;`, (expression | nil), `;`, (expression | nil)]{{/nodespec}}
|
|
{{#nodespec}}["forVarSpec", `var`, (varDecl | `,`), ..., `;`, (expression | nil), `;`, (expression | nil)]{{/nodespec}}
|
|
{{#nodespec}}["forInSpec", expression, `in`, expression]{{/nodespec}}
|
|
{{#nodespec}}["forVarInSpec", `var`, varDecl, `in`, expression]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>There are technically four types of for-loops in JavaScript.</p>
|
|
<p>These definitions suggest some
|
|
odd possibilities, and interestingly, they work. The form <code>for (x.foo in y)</code> will actually
|
|
set <code>x.foo</code> each time through the loop, and <code>for (var x = bar() in y)</code> will call
|
|
<code>bar()</code> and assign it first thing,
|
|
even if <code>x</code> is immediately overwritten before the first iteration of the loop.</p>
|
|
<p>The semicolons are mandatory, even at a line break.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{#nodespec}}case:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["case", `case`, expression, `:`, statement, ...]{{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}default:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["default", `default`, `:`, statement, ...]{{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}catch:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["catch", `catch`, `(`, IDENTIFIER, `)`, statement]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>The statement is always a blockStmnt.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{#nodespec}}finally:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}["finally", `finally`, statement]{{/nodespec}}
|
|
<div class="explan">
|
|
<p>The statement is always a blockStmnt.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{#nodespec}}identifierName:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}(IDENTIFIER | KEYWORD | BOOLEAN | `null`){{/nodespec}}
|
|
<div class="explan">
|
|
<p>As of ECMAScript 5th edition, keywords and reserved words can be used in some places
|
|
where an identifier is expected. For example, <code>x.return()</code> or
|
|
<code>{true: 'yes'}</code>.</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{#nodespec}}unaryOp:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}(`delete` | `void` | `typeof` | `++` | `--` | `+` | `-` | `~` | `!`){{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}binaryOp:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}(`*` | `/` | `%` | `+` | `-` | `<<` | `>>` | `>>>` | `<` | `>` | `<=` | `>=` | `instanceof` | `in` | `==` | `!=` | `===` | `!==` | `&` | `^` | `|` | `&&` | `||`){{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}postfixOp:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}(`++` | `--`){{/nodespec}}
|
|
</div>
|
|
|
|
{{#nodespec}}assignmentOp:{{/nodespec}}
|
|
<div class="indent">
|
|
{{#nodespec}}(`=` | `*=` | `/=` | `%=` | `+=` | `-=` | `<<=` | `>>=` | `>>>=` | `&=` | `^=` | `|=`){{/nodespec}}
|
|
</div>
|
|
|
|
</template>
|