Representing Javascript ASTs

Hi, I’ve been trying to build a library to represent Javascript ASTs to provide a strongly typed interface to e.g. @babel/parser etc, and it seems like it’s well beyond my understanding of Purescript.

Because there are so many “versions” of javascript that I want to be able to manipulate.

  • ES5
  • ES201x
  • JSX
  • Various babel extensions

I tried to model the “core” with an open variant

-- x is additions to Expression
-- s is additions to Statement
-- p is additions to Pattern
newtype Statement (x :: # Type) (s :: # Type) (p :: # Type)
  = Statement (Variant 
                        ( "ExpressionStatement" :: ExpressionStatement x s p
                        , "BlockStatement" :: BlockStatement x s p
                        , "EmptyStatement" :: Node ()
                        , "DebuggerStatement" :: Node ()
                        , "WithStatement" :: WithStatement x s p
                        , "ReturnStatement" :: ReturnStatement x s p
                        , "LabeledStatement" :: LabeledStatement x s p
                        , "BreakStatement" :: BreakStatement
                        , "ContinueStatement" :: ContinueStatement
                        , "IfStatement" :: IfStatement x s p
                        , "ThrowStatement" :: ThrowStatement x s p
                        , "WhileStatement" :: WhileStatement x s p
                        , "DoWhileStatement" :: DoWhileStatement x s p
                        , "ForStatement" :: ForStatement x s p
                        , "FunctionDeclaration" :: FunctionDeclaration x s p
                        , "VariableDeclaration" :: VariableDeclaration x s p
                        | s

but then I literally don’t know how to populate those variables with anything other than ().

Is this a bad approach? Is this a solved problem?

Any help is appreciated.

I’m not sure if it is helpful in your context but I’ve extracted pieces of JS AST implementation from purescript-in-purescript few months ago. I’ve not published this lib but it won’t be a problem to do this:

1 Like

Thanks, I don’t think this is doing precisely what I want but it’s entirely possible that “what I want” is wrong so this is something to read

You should be able to do something like Statement ("MyNewExpressionType" :: ...) ..., but the problem you will run into is these extended fields can’t be recursive, which probably is not what you want. Structural types in PureScript do not support recursion at the language level. One possibility is to factor the recursion out and use recursions schemes, however this will only work with a single point of recursion, whereas you need mutual recursion. See Exploring mutually recursive Variants. I have not taken this further, unfortunately.

1 Like

The alternative to variants is to just create an extension point in your sum

data Statement f x s p
  = ExpressionStatement ...
  | BlockStatement ...
  | CustomStatement (f x s p)

data MyCustomStatement x s p
  = FancyJsStatement ...

type MyStatement = Statement MyCustomStatement

Thanks a lot man. This is great reading material. It’s weirdly satisfying to know why what I was attempting is impossible (or at least too difficult).

I think I’m going to give up on making it extensible and model the biggest superset of JS I want to work with.

I attempted something similar with ESTree a while back.

You might find this issue interesting.

1 Like