js-good-parts-0.0.7: Javascript: The Good Parts -- AST & Pretty Printer

Safe HaskellSafe-Infered

Language.JavaScript.AST

Contents

Description

In Chapter 2 of "JavaScript: The Good Parts", Douglas Crockford presents a concrete grammar for "the good parts" of JavaScript.

This module provides an abstract grammar for those good parts. Henceforth, we abbreviate this language to JS:TGP

Crockford presents the grammar as a series of railroad diagrams. The correspondence between the concrete grammar and the abstract grammar in this module is NOT one-to-one. However, the following property does hold: the pretty printing of an abstract syntax tree will be parseable by the concrete grammar. i.e. For each valid program produced by the concrete grammar there is a corresponding abstract syntax tree that when pretty printed will produce that program (modulo whitespace).

The abstract grammar

  • removes unnecessary characters such as parentheses (normal, curly and square)
  • represents JavaScript's string, name and number literals directly in Haskell as String, String and Double respectively.

Conventions for concrete syntax

  • Non-terminals appear in angle brackets e.g. <Name>
  • ? means zero or one. e.g. <Expr>?
  • * means zero or more e.g. <Stmt>*
  • + means one or more e.g. <Stmt>+
  • ( ) are meta-brackets used to enclose a concrete-syntax expression so that ?,* or + can be applied. e.g. (= <Expr>)* This means zero or more repetitions of: = <Expr>

This library was designed so that it would be impossible, save for name, string literals to construct an incorrect JS:TGP program. To this end some of the data structures may look like they contain redundancy. For instance, consider the ESDelete constructor which is defined

ESDelete Expr Invocation

Why not just define it as ESDelete Expr since type Expr has a constructor defined as ExprInvocation Expr Invocation? The reason is that this would allow incorrect programs. A Expr is not necessarily a Invocation.

A note on precedence of JavaScript operators

Interestingly, the precedence of JavaScript operators is not defined in the ECMAScript standard. The precedence used in this library comes from the Mozilla Developer's Network pages. (https:developer.mozilla.orgenJavaScriptReferenceOperators/Operator_Precedence)

I have not used the precise precedence numbers from that page since in this module a lower precedence means the operator binds more tightly (as opposed to the page where a higher precedence does the same). Also, we have need for less precedence values so they have been normalised to what we are using in JS:TGP

You will also note that we don't even consider the associativity/precedence of "=", "+=", "-=" etc. In JS:TGP the notion of expression statements is quite different to that of expressions. It simply isn't legal to write an expression statement like

(a += 2) -= 3

or

a = (b = c) = (c = d)

although it is perfectly legal to write

a = b = c = d += 2

which if we add brackets to disambiguate is really

a = (b = (c = (d += 2)))

Interesting aspects of "the good parts":

A JS:TGP program is a collection of statements. You'll note that there is no statement to declare a function in JS:TGP. However you can assign a function literal to a variable.

e.g.

var fun = function(x) { return x + 1;}

What about recursive functions then? There is the option to give the function a name which is local to the literal.

e.g.

var factorial = function f(n) {
                    if ( n > 0 ) {
                      return n * f(n - 1);
                    } else {
                      return 1;
                    }
                  }

f is local. It will not be in scope outside of the function body.

Abbreviations:

 Stmt = Statement, Expr = Expression, Fn = Function, Decl = Declaration

Synopsis

Documentation

data Name Source

Instances

jsString :: String -> Either String JSStringSource

The only way you can create a Javascript string. This function needs to correctly encode all special characters. See p9 of "JavaScript: The Good Parts"

name :: String -> Either String NameSource

jsName is the only way you can create a Name

Data types

newtype Number Source

Constructors

Number Double 

Instances

Pretty Number 
PrettyPrec Number 

data VarStmt Source

Concrete syntax:

var <VarDecl> [, <VarDecl>]* ;

e.g. var x = 1, y;

Constructors

VarStmt (NonEmptyList VarDecl) 

Instances

Pretty VarStmt 
PrettyPrec VarStmt 

data VarDecl Source

Concrete syntax:

  1. <Name> (= <Expr>)?

e.g.

  1. x
  2. x = 2 + y

Constructors

VarDecl Name (Maybe Expr) 

Instances

Pretty VarDecl 
PrettyPrec VarDecl 

data Stmt Source

The many different kinds of statements

Constructors

StmtExpr ExprStmt
<ExprStmt>;
StmtDisruptive DisruptiveStmt
<DisruptiveStmt>
StmtTry TryStmt
<TryStmt>
StmtIf IfStmt
<IfStmt>
StmtSwitch (Maybe Name) SwitchStmt
(<Name> : ) <SwitchStmt>
StmtWhile (Maybe Name) WhileStmt
(<Name> : ) <WhileStmt>
StmtFor (Maybe Name) ForStmt
(<Name> : ) <ForStmt>
StmtDo (Maybe Name) DoStmt
(<Name> : ) <DoStmt>

Instances

Pretty Stmt 
PrettyPrec Stmt 

data DisruptiveStmt Source

Disruptive statements

Constructors

DSBreak BreakStmt
<BreakStmt>
DSReturn ReturnStmt
syntax: <ReturnStmt>
DSThrow ThrowStmt
syntax: <ThrowStmt>

Instances

data IfStmt Source

Concrete syntax:

if ( <Expr> ) { <Stmt>* } -- for Nothing

or

if ( <Expr> ) { <Stmt>* } else { <Stmt>* } -- for 'Just . Left'

or

if ( <Expr> ) { <Stmt>* } else <IfStmt> -- for 'Just . Right'

e.g.

  1. if (x > 3) { y = 2; }
  2. if (x < 2) { y = 1; } else { y = 3; z = 2; }
  3. if (x > 0) { y = 20; } else if ( x > 10) { y = 30; } else { y = 10; }

Constructors

IfStmt Expr [Stmt] (Maybe (Either [Stmt] IfStmt)) 

Instances

Pretty IfStmt 
PrettyPrec IfStmt 

data SwitchStmt Source

Concrete syntax:

switch ( <Expr> ) { <CaseClause> }

or

 switch ( <Expr> ) {
  <CaseAndDisruptive>+
  default : <Stmt>*
 }

e.g.

 switch ( x ) {
   case 1:
     y = 2;
   }
 switch ( x ) {
   case 1:
     y = 2;
     break;
   case 2:
     y = 3;
     break;
   default:
     y = 4;
 }

Instances

data CaseAndDisruptive Source

A case clause followed by a disruptive statement

Concrete syntax:

<CaseClause> <DisruptiveStmt>

e.g.

  1. case 2: y = 2; break;

data CaseClause Source

Concrete syntax:

case <Expr> : <Stmt>*

e.g.

case 2:   // zero statements following the case expression is valid.
 case 2:
   y = 1;

Constructors

CaseClause Expr [Stmt] 

Instances

data ForStmt Source

Two style of for-statements -- C-style and In-style.

Concrete syntax:

 for (<ExprStmt>? ; <Expr>? ; <ExprStmt>? ) {
   <Stmt>*
 }
 for ( <Name> in <Expr> ) {
   <Stmt>*
 }

e.g.

  1. for ( ; ; ) { }
  2. for ( ; x < 10 ;) { x += 1; }
 for (i = 0; i < 10; i += 1) {
   x += i;
 }
  1. for ( i in indices ) { a[i] = 66; }

Instances

Pretty ForStmt 
PrettyPrec ForStmt 

data DoStmt Source

Concrete syntax:

do { <Stmt>* } while ( <Expr> );

Constructors

DoStmt [Stmt] Expr 

Instances

Pretty DoStmt 
PrettyPrec DoStmt 

data WhileStmt Source

Concrete syntax:

while ( <Expr>) { <Stmt>* }

Constructors

WhileStmt Expr [Stmt] 

Instances

Pretty WhileStmt 
PrettyPrec WhileStmt 

data TryStmt Source

Concrete syntax:

try { <Stmt>* } catch ( <Name> ) { <Stmt>* }

Constructors

TryStmt [Stmt] Name [Stmt] 

Instances

Pretty TryStmt 
PrettyPrec TryStmt 

data ThrowStmt Source

Concrete syntax:

throw <Expr>;

Constructors

ThrowStmt Expr 

Instances

Pretty ThrowStmt 
PrettyPrec ThrowStmt 

data ReturnStmt Source

Concrete syntax:

return <Expr>?;

e.g.

  1. return;
  2. return 2 + x;

Constructors

ReturnStmt (Maybe Expr) 

Instances

data BreakStmt Source

Concrete syntax:

break <Name>?;

e.g.

  1. break;
  2. break some_label;

Constructors

BreakStmt (Maybe Name) 

Instances

Pretty BreakStmt 
PrettyPrec BreakStmt 

data ExprStmt Source

Concrete syntax:

<Value>+ <RValue>

or

delete <Expr> <Refinement>

Instances

Pretty ExprStmt 
PrettyPrec ExprStmt 

data LValue Source

Concrete syntax:

<Name> (<Invocation>* <Refinement>)*

e.g.

  1. x
  2. x.field_1
  3. fun().field_1
  4. fun(1)(2)
  5. fun(1)(2).field_1
  6. x.fun_field_1(x+2).fun_field_2(y+3).field_3

Constructors

LValue Name [([Invocation], Refinement)] 

Instances

Pretty LValue 
PrettyPrec LValue 

data RValue Source

Concrete syntax:

=  <Expr>

or

+= <Expr>

or

-= <Expr>

or

<Invocation>+

e.g.

  1. = 2
  2. += 3
  3. -= (4 + y)
  4. ()
  5. (1)
  6. (x,y,z)

Instances

Pretty RValue 
PrettyPrec RValue 

data Expr Source

Constructors

ExprLit Lit
<Lit>
ExprName Name
<Name>
ExprPrefix PrefixOperator Expr
<PrefixOperator> <Expr>
ExprInfix InfixOperator Expr Expr
<Expr> <InfixOperator> <Expr>
ExprTernary Expr Expr Expr
<Expr> ? <Expr> : <Expr>
ExprInvocation Expr Invocation
<Expr><Invocation>
ExprRefinement Expr Refinement
<Expr><Refinement>
ExprNew Expr Invocation

new <Expr><Invocation>

ExprDelete Expr Refinement

delete <Expr><Refinement>

Instances

Pretty Expr 
PrettyPrec Expr 

data PrefixOperator Source

Constructors

TypeOf
typeof
ToNumber
+
Negate
-
Not
!

Instances

data InfixOperator Source

Constructors

Mul
*
Div
/
Mod
%
Add
+
Sub
-
GTE
>=
LTE
<=
GT
>
LT
<
Eq
===
NotEq
!==
Or
||
And
&&

Instances

data Invocation Source

Concrete syntax:

<Expr>*

e.g.

  1. ()
  2. (1)
  3. (x,z,y)

Constructors

Invocation [Expr] 

Instances

data Refinement Source

Concrete syntax:

.<Name>

or

[<Expr>]

e.g.

  1. .field_1
  2. [i+1]

Constructors

Property Name 
Subscript Expr 

Instances

data Lit Source

Interestingly, the syntax diagrams presented in the book don't include boolean literals. I can only assume this is an oversight as they are used throughout the book.

Constructors

LitNumber Number
<Number>
LitBool Bool
<true | false>
LitString JSString
<String>
LitObject ObjectLit
<ObjectLit>
LitArray ArrayLit
<ArrayLit>
LitFn FnLit

<FnLit> | LitRegexp RegexpLit -- TODO: Add regexps

Instances

Pretty Lit 
PrettyPrec Lit 

data ObjectLit Source

Concrete syntax:

{} -- no fields

or

{<ObjectField> (, <ObjectField> )*} -- one or more fields

Constructors

ObjectLit [ObjectField] 

Instances

Pretty ObjectLit 
PrettyPrec ObjectLit 

data ObjectField Source

Concrete syntax:

<Name>: <Expr> -- for Left

or

<String>: <Expr> -- for Right

e.g.

  1. x: y + 3
  2. "value": 3 - z

Instances

data ArrayLit Source

Concrete syntax:

[] -- empty array

or

[<Expr> (, <Expr>*) ] -- non empty array

Constructors

ArrayLit [Expr] 

Instances

Pretty ArrayLit 
PrettyPrec ArrayLit 

data FnLit Source

Concrete syntax:

function <Name>? <FnBody>

Constructors

FnLit (Maybe Name) [Name] FnBody 

Instances

Pretty FnLit 
PrettyPrec FnLit 

data FnBody Source

Concrete syntax:

{ <VarStmt>+ <Stmt>+ }

Constructors

FnBody [VarStmt] [Stmt] 

Instances

Pretty FnBody 
PrettyPrec FnBody 

data Program Source

Programs. All variable statements come first.

Constructors

Program [VarStmt] [Stmt] 

Instances